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


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.