Конкатенація рядків двох стовпців панд


85

У мене є таке DataFrame:

from pandas import *
df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})

Це виглядає так:

    bar foo
0    1   a
1    2   b
2    3   c

Тепер я хочу мати щось на зразок:

     bar
0    1 is a
1    2 is b
2    3 is c

Як я можу цього досягти? Я спробував наступне:

df['foo'] = '%s is %s' % (df['bar'], df['foo'])

але це дає мені неправильний результат:

>>>print df.ix[0]

bar                                                    a
foo    0    a
1    b
2    c
Name: bar is 0    1
1    2
2
Name: 0

Вибачте за тупе запитання, але ця панда: об’єднання двох стовпців у DataFrame мені не допомогло.

Відповіді:



65

На це питання вже дано відповіді, але я вважаю, що було б непогано вкласти в суміш деякі корисні методи, які раніше не обговорювались, і порівняти всі запропоновані на сьогодні методи з точки зору ефективності.

Ось декілька корисних рішень цієї проблеми в порядку збільшення продуктивності.


DataFrame.agg

Це простий str.formatпідхід.

df['baz'] = df.agg('{0[bar]} is {0[foo]}'.format, axis=1)
df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

Ви також можете використовувати форматування f-рядка тут:

df['baz'] = df.agg(lambda x: f"{x['bar']} is {x['foo']}", axis=1)
df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

char.arrayна основі об'єднання

Перетворіть стовпці для об’єднання як chararrays, а потім складіть їх разом.

a = np.char.array(df['bar'].values)
b = np.char.array(df['foo'].values)

df['baz'] = (a + b' is ' + b).astype(str)
df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

Розуміння списку за допомогоюzip

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

df['baz'] = [str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])]

Крім того, використання str.joinconcat (також буде краще масштабуватися):

df['baz'] = [
    ' '.join([str(x), 'is', y]) for x, y in zip(df['bar'], df['foo'])]

df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

Розуміння списків перевершує маніпулювання рядками, оскільки рядкові операції за своєю суттю важко векторизувати, а більшість "векторизованих" функцій панд в основному є обгортками навколо циклів. Я багато писав про цю тему в розділі « Для циклів з пандами» - коли мені потрібно дбати? . Загалом, якщо вам не доводиться турбуватися про вирівнювання індексу, використовуйте розуміння списку, маючи справу з операціями рядка та регулярного виразу.

Наведений вище список за замовчуванням не обробляє NaN. Однак ви завжди можете написати функцію, яка обгортає спробу, за винятком випадків, коли вам потрібно було її обробити.

def try_concat(x, y):
    try:
        return str(x) + ' is ' + y
    except (ValueError, TypeError):
        return np.nan


df['baz'] = [try_concat(x, y) for x, y in zip(df['bar'], df['foo'])]

perfplot Вимірювання продуктивності

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

Графік, сформований за допомогою perfplot . Ось повний перелік кодів .

Функції

def brenbarn(df):
    return df.assign(baz=df.bar.map(str) + " is " + df.foo)

def danielvelkov(df):
    return df.assign(baz=df.apply(
        lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1))

def chrimuelle(df):
    return df.assign(
        baz=df['bar'].astype(str).str.cat(df['foo'].values, sep=' is '))

def vladimiryashin(df):
    return df.assign(baz=df.astype(str).apply(lambda x: ' is '.join(x), axis=1))

def erickfis(df):
    return df.assign(
        baz=df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1))

def cs1_format(df):
    return df.assign(baz=df.agg('{0[bar]} is {0[foo]}'.format, axis=1))

def cs1_fstrings(df):
    return df.assign(baz=df.agg(lambda x: f"{x['bar']} is {x['foo']}", axis=1))

def cs2(df):
    a = np.char.array(df['bar'].values)
    b = np.char.array(df['foo'].values)

    return df.assign(baz=(a + b' is ' + b).astype(str))

def cs3(df):
    return df.assign(
        baz=[str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])])

4
Це все, що я завжди хотів знати про об’єднання рядків у пандах, але надто боявся запитати!
IanS

Чи можете ви оновити сюжет до наступного рівня 10 4 (або навіть вище), швидка візуальна відповідь з поточним сюжетом, обмеженим 10 3 (1000, що дуже мало на сьогоднішній день), це те, що cs3 найкращий, зрештою, коли brenbarn виглядає менш експоненціально, ніж cs3, тому, швидше за все, для великих наборів даних brenbarn є найкращою (швидшою) відповіддю.
Велізар ВЕСЕЛІНОВ

1
@VelizarVESSELINOV Оновлено! Мене дивує те, що об’єднання numpy відбувається повільніше, ніж об’єднання списку та об’єднання панд.
cs95

1
Ви роздумували про використання df['bar'].tolist()та df['foo'].tolist()введення cs3()? Я припускаю, що це трохи збільшило б "базовий" час, але масштабувало б краще.
shadowtalker

44

Проблема вашого коду полягає в тому, що ви хочете застосувати операцію до кожного рядка. Те, як ви це написали, бере всі стовпці "bar" і "foo", перетворює їх у рядки і повертає вам один великий рядок. Ви можете написати це так:

df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)

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


13

Ви також можете використовувати

df['bar'] = df['bar'].str.cat(df['foo'].values.astype(str), sep=' is ')

1
Це не працює, оскільки df ['bar'] не є рядковим стовпцем. Правильним призначенням є df['bar'] = df['bar'].astype(str).str.cat(df['foo'], sep=' is ').
cbrnr

8
df.astype(str).apply(lambda x: ' is '.join(x), axis=1)

0    1 is a
1    2 is b
2    3 is c
dtype: object

Ця відповідь також працює з невизначеною кількістю стовпців (> 1) та невизначеними назвами стовпців, що робить її більш корисною, ніж інші.
johnDanger

4

Відповідь @DanielVelkov правильна, АЛЕ швидше використовувати рядкові літерали:

# Daniel's
%timeit df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)
## 963 µs ± 157 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# String literals - python 3
%timeit df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1)
## 849 µs ± 4.28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

4

series.str.cat є найбільш гнучким способом вирішення цієї проблеми:

Для df = pd.DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})

df.foo.str.cat(df.bar.astype(str), sep=' is ')

>>>  0    a is 1
     1    b is 2
     2    c is 3
     Name: foo, dtype: object

АБО

df.bar.astype(str).str.cat(df.foo, sep=' is ')

>>>  0    1 is a
     1    2 is b
     2    3 is c
     Name: bar, dtype: object

Найголовніше (і на відміну від цього .join()), це дозволяє ігнорувати або замінювати Nullзначення na_repпараметром.


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