Поєднайте два стовпці тексту у кадрі даних у пандах / пітонах


485

У мене є 20 x 4000 фреймів даних в Python, використовуючи панди. Два з цих стовпців названі Yearта quarter. Я хотів би створити змінну , periodяка робить Year = 2000і quarter= q2в 2000q2.

Хтось може допомогти у цьому?

Відповіді:


529

якщо обидва стовпці - це рядки, ви можете об'єднати їх безпосередньо:

df["period"] = df["Year"] + df["quarter"]

Якщо один (або обидва) стовпців не введено рядки, спершу слід перетворити його (їх),

df["period"] = df["Year"].astype(str) + df["quarter"]

Остерігайтеся NaNs, роблячи це!


Якщо вам потрібно приєднатися до декількох стовпців-рядків, ви можете використовувати agg:

df['period'] = df[['Year', 'quarter', ...]].agg('-'.join, axis=1)

Де "-" роздільник.


13
Чи можна додавати кілька стовпців разом, не вводячи всі стовпці? Скажімо, add(dataframe.iloc[:, 0:10])наприклад?
Гейзенберг

5
@Heisenberg Це повинно бути можливим із вбудованим Python sum.
Сільвадо

6
@silvado, чи можете ви зробити приклад додавання кількох стовпців? Дякую
c1c1c1

6
Будьте уважні, вам потрібно застосувати карту (str) до всіх стовпців, які не є рядками в першу чергу. якби чверть була цифрою, ви б зробили dataframe["period"] = dataframe["Year"].map(str) + dataframe["quarter"].map(str)карту, просто застосовуючи перетворення рядків до всіх записів.
Ozgur Ozturk

13
Це рішення може створити проблеми, якщо у вас є нан-значення,

269
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
df['period'] = df[['Year', 'quarter']].apply(lambda x: ''.join(x), axis=1)

Виходить цей фрейм даних

   Year quarter  period
0  2014      q1  2014q1
1  2015      q2  2015q2

Цей метод узагальнює довільну кількість стовпців рядків, замінюючи df[['Year', 'quarter']]будь-який фрагмент стовпця вашого фрейму даних, наприклад df.iloc[:,0:2].apply(lambda x: ''.join(x), axis=1).

Більше інформації про метод Apply () можна переглянути тут


20
lambda x: ''.join(x)просто ''.join, ні?
DSM

6
@OzgurOzturk: ​​справа в тому, що лямбда-частина lambda x: ''.join(x)будівництва нічого не робить; це як використовувати lambda x: sum(x)замість просто sum.
DSM

4
Підтверджено же результат при використанні ''.join, а саме: df['period'] = df[['Year', 'quarter']].apply(''.join, axis=1).
Макс Геніс

1
@Archie joinприймає лише strідентифікатори в ітерабелі. Використовуйте a, mapщоб перетворити їх у всі, strа потім використовувати join.
Джон Строд

16
приєднатися (x.map (str))
Манджул

257

Невеликі набори даних (<150 вар.)

[''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]

або трохи повільніше, але компактніше:

df.Year.str.cat(df.quarter)

Більший набір даних (> 150 вар.)

df['Year'].astype(str) + df['quarter']

ОНОВЛЕННЯ: Графік часу Панд 0.23.4

введіть тут опис зображення

Перевіримо його на 200K рядків DF:

In [250]: df
Out[250]:
   Year quarter
0  2014      q1
1  2015      q2

In [251]: df = pd.concat([df] * 10**5)

In [252]: df.shape
Out[252]: (200000, 2)

ОНОВЛЕННЯ: нові таймінги за допомогою Pandas 0.19.0

Час без оптимізації процесора / GPU (відсортовано від найшвидшого до найповільнішого):

In [107]: %timeit df['Year'].astype(str) + df['quarter']
10 loops, best of 3: 131 ms per loop

In [106]: %timeit df['Year'].map(str) + df['quarter']
10 loops, best of 3: 161 ms per loop

In [108]: %timeit df.Year.str.cat(df.quarter)
10 loops, best of 3: 189 ms per loop

In [109]: %timeit df.loc[:, ['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 567 ms per loop

In [110]: %timeit df[['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 584 ms per loop

In [111]: %timeit df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)
1 loop, best of 3: 24.7 s per loop

Час використання оптимізації процесора / GPU:

In [113]: %timeit df['Year'].astype(str) + df['quarter']
10 loops, best of 3: 53.3 ms per loop

In [114]: %timeit df['Year'].map(str) + df['quarter']
10 loops, best of 3: 65.5 ms per loop

In [115]: %timeit df.Year.str.cat(df.quarter)
10 loops, best of 3: 79.9 ms per loop

In [116]: %timeit df.loc[:, ['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 230 ms per loop

In [117]: %timeit df[['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 230 ms per loop

In [118]: %timeit df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)
1 loop, best of 3: 9.38 s per loop

Вклад відповіді від @ anton-vbr


Яка різниця між 261 та 264 у часі?
Антон Протопопов

@AntonProtopopov, очевидно, 100 м з нізвідки :)
Денніс Голомазов

@AntonProtopopov, я думаю, це суміш двох таймінгів - один використовував оптимізацію CPU / GPU, інший - не. Я оновив свою відповідь і поставив там обидва набори часу ...
MaxU

Це використання .sum () не вдається Якщо всі стовпці виглядають так, що вони можуть бути цілими числами (тобто є рядковими формами цілих чисел). Натомість, схоже, панди перетворюють їх на числові показники перед підсумовуванням!
CPBL

@CPBL, спробуйте такий підхід:df.T.apply(lambda x: x.str.cat(sep=''))
MaxU

157

Для cat()цього.str дійсно добре працює метод аксесуара :

>>> import pandas as pd
>>> df = pd.DataFrame([["2014", "q1"], 
...                    ["2015", "q3"]],
...                   columns=('Year', 'Quarter'))
>>> print(df)
   Year Quarter
0  2014      q1
1  2015      q3
>>> df['Period'] = df.Year.str.cat(df.Quarter)
>>> print(df)
   Year Quarter  Period
0  2014      q1  2014q1
1  2015      q3  2015q3

cat() навіть дозволяє вам додати роздільник, наприклад, припустимо, у вас є лише цілі числа за рік та період, ви можете це зробити:

>>> import pandas as pd
>>> df = pd.DataFrame([[2014, 1],
...                    [2015, 3]],
...                   columns=('Year', 'Quarter'))
>>> print(df)
   Year Quarter
0  2014       1
1  2015       3
>>> df['Period'] = df.Year.astype(str).str.cat(df.Quarter.astype(str), sep='q')
>>> print(df)
   Year Quarter  Period
0  2014       1  2014q1
1  2015       3  2015q3

Приєднання до декількох стовпців - це лише питання передачі списку рядів або фрейму даних, що містить усі, крім першого стовпця, як параметр для str.cat()виклику в першому стовпці (Серії):

>>> df = pd.DataFrame(
...     [['USA', 'Nevada', 'Las Vegas'],
...      ['Brazil', 'Pernambuco', 'Recife']],
...     columns=['Country', 'State', 'City'],
... )
>>> df['AllTogether'] = df['Country'].str.cat(df[['State', 'City']], sep=' - ')
>>> print(df)
  Country       State       City                   AllTogether
0     USA      Nevada  Las Vegas      USA - Nevada - Las Vegas
1  Brazil  Pernambuco     Recife  Brazil - Pernambuco - Recife

Зверніть увагу, що якщо у вашому фреймі / серії панд є нульові значення, вам потрібно включити параметр na_rep для заміни значень NaN рядком, інакше комбінований стовпець за замовчуванням стане NaN.


12
Це здається набагато кращим (можливо, і більш ефективним), ніж lambdaабо map; також він просто читається найбільш чисто.
dwanderson

1
@ZakS, передаючи решта стовпців як фрейм даних, а не ряд як перший параметр str.cat(). Я
поправлю

Яку версію панд ви використовуєте? Я отримую ValueError: Ви мали на увазі ввести sepключове слово? у пандах-0,23,4. Дякую!
Qinqing Лю

@QinqingLiu, я повторно перевірив їх пандами-0,23,4, і вони здаються справними. sepПараметр необхідний тільки якщо ви маєте намір відокремити частини зчепленої рядки. Якщо ви отримали помилку, будь ласка, покажіть нам ваш невдалий приклад.
LeoRochael

31

Цього разу використання функції lamba разом із string.format ().

import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'Quarter': ['q1', 'q2']})
print df
df['YearQuarter'] = df[['Year','Quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)
print df

  Quarter  Year
0      q1  2014
1      q2  2015
  Quarter  Year YearQuarter
0      q1  2014      2014q1
1      q2  2015      2015q2

Це дозволяє вам працювати з значеннями без рядків і переформатувати значення за потребою.

import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'Quarter': [1, 2]})
print df.dtypes
print df

df['YearQuarter'] = df[['Year','Quarter']].apply(lambda x : '{}q{}'.format(x[0],x[1]), axis=1)
print df

Quarter     int64
Year       object
dtype: object
   Quarter  Year
0        1  2014
1        2  2015
   Quarter  Year YearQuarter
0        1  2014      2014q1
1        2  2015      2015q2

1
Набагато швидше: .На (. '' Приєднатися до (х), осі = 1)
Гханет

19

Проста відповідь на ваше запитання.

    year    quarter
0   2000    q1
1   2000    q2

> df['year_quarter'] = df['year'] + '' + df['quarter']

> print(df['year_quarter'])
  2000q1
  2000q2

3
не вдасться, якщо Yearце не рядок
geher

4
використанняdf['Year'].astype(str) + '' + df['quarter'].astype(str)
Єграб

2
У чому саме суть цього рішення, оскільки воно ідентичне верхній відповіді?
AMC

14

Хоча @silvado відповідь хороший , якщо ви зміните df.map(str)до df.astype(str)неї буде швидше:

import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})

In [131]: %timeit df["Year"].map(str)
10000 loops, best of 3: 132 us per loop

In [132]: %timeit df["Year"].astype(str)
10000 loops, best of 3: 82.2 us per loop

12

Давайте припустимо , що ваш dataframeIS dfз колонами Yearі Quarter.

import pandas as pd
df = pd.DataFrame({'Quarter':'q1 q2 q3 q4'.split(), 'Year':'2000'})

Припустимо, ми хочемо бачити кадр даних;

df
>>>  Quarter    Year
   0    q1      2000
   1    q2      2000
   2    q3      2000
   3    q4      2000

Нарешті, об'єднайте Yearі Quarterнаступне.

df['Period'] = df['Year'] + ' ' + df['Quarter']

Тепер ви можете print df побачити отриманий кадр даних.

df
>>>  Quarter    Year    Period
    0   q1      2000    2000 q1
    1   q2      2000    2000 q2
    2   q3      2000    2000 q3
    3   q4      2000    2000 q4

Якщо ви не хочете місця між роком і кварталом, просто видаліть його, зробивши його;

df['Period'] = df['Year'] + df['Quarter']

3
Зазначено як рядкиdf['Period'] = df['Year'].map(str) + df['Quarter'].map(str)
Stuber

Я отримую, TypeError: Series cannot perform the operation +коли бігаю df2['filename'] = df2['job_number'] + '.' + df2['task_number']або df2['filename'] = df2['job_number'].map(str) + '.' + df2['task_number'].map(str).
Карл Бейкер

Однак df2['filename'] = df2['job_number'].astype(str) + '.' + df2['task_number'].astype(str)справились.
Карл Бейкер

@KarlBaker, я думаю, у вас не було рядків у вашому введенні. Але я радий, що ти це зрозумів. Якщо ви подивитесь на приклад, dataframeякий я створив вище, ви побачите, що всі стовпці є strings.
Самуель

У чому саме суть цього рішення, оскільки воно ідентичне верхній відповіді?
AMC

10

Ось реалізація, яку я вважаю дуже універсальною:

In [1]: import pandas as pd 

In [2]: df = pd.DataFrame([[0, 'the', 'quick', 'brown'],
   ...:                    [1, 'fox', 'jumps', 'over'], 
   ...:                    [2, 'the', 'lazy', 'dog']],
   ...:                   columns=['c0', 'c1', 'c2', 'c3'])

In [3]: def str_join(df, sep, *cols):
   ...:     from functools import reduce
   ...:     return reduce(lambda x, y: x.astype(str).str.cat(y.astype(str), sep=sep), 
   ...:                   [df[col] for col in cols])
   ...: 

In [4]: df['cat'] = str_join(df, '-', 'c0', 'c1', 'c2', 'c3')

In [5]: df
Out[5]: 
   c0   c1     c2     c3                cat
0   0  the  quick  brown  0-the-quick-brown
1   1  fox  jumps   over   1-fox-jumps-over
2   2  the   lazy    dog     2-the-lazy-dog

FYI: Цей метод чудово працює з Python 3, але створює проблеми в Python 2.
Alex P. Miller

10

Оскільки ваші дані вставляються в кадр даних, ця команда повинна вирішити вашу проблему:

df['period'] = df[['Year', 'quarter']].apply(lambda x: ' '.join(x.astype(str)), axis=1)

Ця відповідь ідентична старій, більш популярній .
AMC

9

більш ефективним є

def concat_df_str1(df):
    """ run time: 1.3416s """
    return pd.Series([''.join(row.astype(str)) for row in df.values], index=df.index)

і ось час перевірки:

import numpy as np
import pandas as pd

from time import time


def concat_df_str1(df):
    """ run time: 1.3416s """
    return pd.Series([''.join(row.astype(str)) for row in df.values], index=df.index)


def concat_df_str2(df):
    """ run time: 5.2758s """
    return df.astype(str).sum(axis=1)


def concat_df_str3(df):
    """ run time: 5.0076s """
    df = df.astype(str)
    return df[0] + df[1] + df[2] + df[3] + df[4] + \
           df[5] + df[6] + df[7] + df[8] + df[9]


def concat_df_str4(df):
    """ run time: 7.8624s """
    return df.astype(str).apply(lambda x: ''.join(x), axis=1)


def main():
    df = pd.DataFrame(np.zeros(1000000).reshape(100000, 10))
    df = df.astype(int)

    time1 = time()
    df_en = concat_df_str4(df)
    print('run time: %.4fs' % (time() - time1))
    print(df_en.head(10))


if __name__ == '__main__':
    main()

sumнарешті , коли (concat_df_str2) використовується, результат не просто concat, він перейде в ціле число.


+1 Акуратне рішення, це також дозволяє нам вказати стовпці: наприклад, df.values[:, 0:3]або df.values[:, [0,2]].
Снігова опіка

9

узагальнення до кількох стовпців, чому б ні:

columns = ['whatever', 'columns', 'you', 'choose']
df['period'] = df[columns].astype(str).sum(axis=1)

Виглядає круто, але що робити, якщо я хочу додати роздільник між рядками, як-от "-"?
Одіссео

@Odisseo побачити цю відповідь stackoverflow.com/questions/19377969 / ...
geher

6

Використання zipможе бути ще швидшим:

df["period"] = [''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]

Графік:

введіть тут опис зображення

import pandas as pd
import numpy as np
import timeit
import matplotlib.pyplot as plt
from collections import defaultdict

df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})

myfuncs = {
"df['Year'].astype(str) + df['quarter']":
    lambda: df['Year'].astype(str) + df['quarter'],
"df['Year'].map(str) + df['quarter']":
    lambda: df['Year'].map(str) + df['quarter'],
"df.Year.str.cat(df.quarter)":
    lambda: df.Year.str.cat(df.quarter),
"df.loc[:, ['Year','quarter']].astype(str).sum(axis=1)":
    lambda: df.loc[:, ['Year','quarter']].astype(str).sum(axis=1),
"df[['Year','quarter']].astype(str).sum(axis=1)":
    lambda: df[['Year','quarter']].astype(str).sum(axis=1),
    "df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)":
    lambda: df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1),
    "[''.join(i) for i in zip(dataframe['Year'].map(str),dataframe['quarter'])]":
    lambda: [''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]
}

d = defaultdict(dict)
step = 10
cont = True
while cont:
    lendf = len(df); print(lendf)
    for k,v in myfuncs.items():
        iters = 1
        t = 0
        while t < 0.2:
            ts = timeit.repeat(v, number=iters, repeat=3)
            t = min(ts)
            iters *= 10
        d[k][lendf] = t/iters
        if t > 2: cont = False
    df = pd.concat([df]*step)

pd.DataFrame(d).plot().legend(loc='upper center', bbox_to_anchor=(0.5, -0.15))
plt.yscale('log'); plt.xscale('log'); plt.ylabel('seconds'); plt.xlabel('df rows')
plt.show()

6

Найпростіше рішення:

Загальне рішення

df['combined_col'] = df[['col1', 'col2']].astype(str).apply('-'.join, axis=1)

Вирішення конкретного питання

df['quarter_year'] = df[['quarter', 'year']].astype(str).apply(''.join, axis=1)

Вкажіть бажаний роздільник усередині лапок перед .join



5

Це рішення використовує проміжний крок стиснення двох стовпців DataFrame в один стовпчик, що містить список значень. Це працює не тільки для рядків, але і для всіх типів стовпців-типів

import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
df['list']=df[['Year','quarter']].values.tolist()
df['period']=df['list'].apply(''.join)
print(df)

Результат:

   Year quarter        list  period
0  2014      q1  [2014, q1]  2014q1
1  2015      q2  [2015, q2]  2015q2

схоже, що інші типи не працюватимуть. У мене з'явився TypeError: елемент послідовності 1: очікуваний екземпляр str, float знайдено
Прометей

застосуйте спочатку амплуа до рядка. Операція приєднання працює лише для рядків
Маркус

Це рішення не допоможе поєднати два стовпчики з різним типом, дивіться мою відповідь щодо правильного рішення для такого випадку.
Добрий Воля

2

Як уже згадувалося раніше, ви повинні перетворити кожен стовпець у рядок, а потім використовувати оператор плюс для об'єднання двох стовпців. Ви можете досягти значного підвищення продуктивності за допомогою NumPy.

%timeit df['Year'].values.astype(str) + df.quarter
71.1 ms ± 3.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit df['Year'].astype(str) + df['quarter']
565 ms ± 22.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Я хотів би використовувати numpyified версію , але я отримую повідомлення про помилку: Input : df2['filename'] = df2['job_number'].values.astype(str) + '.' + df2['task_number'].values.astype(str)-> Висновок : TypeError: ufunc 'add' did not contain a loop with signature matching types dtype('<U21') dtype('<U21') dtype('<U21'). І номер_задачі, і номер_задачі є вставками.
Карл Бейкер

Це тому, що ви поєднуєте два масивні масиви. Він працює, якщо поєднувати нумерований масив із серіями pandas. asdf['Year'].values.astype(str) + df.quarter
AbdulRehmanLiaqat

2

Я думаю, що найкращий спосіб об’єднати стовпці в панди - це перетворення обох стовпців на цілі, а потім на str.

df[['Year', 'quarter']] = df[['Year', 'quarter']].astype(int).astype(str)
df['Period']= df['Year'] + 'q' + df['quarter']

перетворення обох стовпців у цілі числа Чому перетворюємо в int спочатку? Після того, як ви видалите цю диваку, це рішення тотожне поточній відповіді.
AMC

2

Ось мій підсумок вищезазначених рішень для об'єднання / об'єднання двох стовпців зі значенням int та str у новий стовпчик, використовуючи роздільник між значеннями стовпців. Для цього працюють три рішення.

# be cautious about the separator, some symbols may cause "SyntaxError: EOL while scanning string literal".
# e.g. ";;" as separator would raise the SyntaxError

separator = "&&" 

# pd.Series.str.cat() method does not work to concatenate / combine two columns with int value and str value. This would raise "AttributeError: Can only use .cat accessor with a 'category' dtype"

df["period"] = df["Year"].map(str) + separator + df["quarter"]
df["period"] = df[['Year','quarter']].apply(lambda x : '{} && {}'.format(x[0],x[1]), axis=1)
df["period"] = df.apply(lambda x: f'{x["Year"]} && {x["quarter"]}', axis=1)

Дякую! Ваше рішення f-string було саме тим, що я сподівався знайти !!!
leerssej

1

Використовуйте .combine_first.

df['Period'] = df['Year'].combine_first(df['Quarter'])

Це неправильно. .combine_firstпризведе або до значення, 'Year'яке зберігається в 'Period', або, якщо воно є Null, значення з 'Quarter'. Він не з'єднає два рядки і не збереже їх 'Period'.
Стів Г

Це неправильно.
AMC

0
def madd(x):
    """Performs element-wise string concatenation with multiple input arrays.

    Args:
        x: iterable of np.array.

    Returns: np.array.
    """
    for i, arr in enumerate(x):
        if type(arr.item(0)) is not str:
            x[i] = x[i].astype(str)
    return reduce(np.core.defchararray.add, x)

Наприклад:

data = list(zip([2000]*4, ['q1', 'q2', 'q3', 'q4']))
df = pd.DataFrame(data=data, columns=['Year', 'quarter'])
df['period'] = madd([df[col].values for col in ['Year', 'quarter']])

df

    Year    quarter period
0   2000    q1  2000q1
1   2000    q2  2000q2
2   2000    q3  2000q3
3   2000    q4  2000q4

0

Можна використовувати надайте метод DataFrame :

df= (pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']}).
  assign(period=lambda x: x.Year+x.quarter ))

-1
dataframe["period"] = dataframe["Year"].astype(str).add(dataframe["quarter"])

або якщо значення на зразок [2000] [4] і хочуть зробити [2000q4]

dataframe["period"] = dataframe["Year"].astype(str).add('q').add(dataframe["quarter"]).astype(str)

підставляючи .astype(str)з .map(str)роботою теж.


Це по суті ідентично верхній відповіді.
AMC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.