Pandas groupby: Як отримати об'єднання рядків


122

У мене є такий кадр даних:

   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

Дзвінок

In [10]: print df.groupby("A")["B"].sum()

повернеться

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

Тепер я хотів би зробити "те саме" для стовпця "C". Оскільки цей стовпець містить рядки, sum () не працює (хоча ви можете подумати, що він об'єднав би рядки). Те, що я дуже хотів би побачити, - це список або набір рядків для кожної групи, тобто

A
1    {This, string}
2    {is, !}
3    {a}
4    {random}

Я намагався знайти способи це зробити.

Series.unique () ( http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html ) не працює, хоча

df.groupby("A")["B"]

це

pandas.core.groupby.SeriesGroupBy object

тому я сподівався, що будь-який метод серії буде спрацьовувати. Будь-які ідеї?

Відповіді:


178
In [4]: df = read_csv(StringIO(data),sep='\s+')

In [5]: df
Out[5]: 
   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

In [6]: df.dtypes
Out[6]: 
A      int64
B    float64
C     object
dtype: object

При застосуванні власної функції не відбувається автоматичне виключення нечислових стовпців. Це, проте, повільніше, ніж застосування .sum()доgroupby

In [8]: df.groupby('A').apply(lambda x: x.sum())
Out[8]: 
   A         B           C
A                         
1  2  1.615586  Thisstring
2  4  0.421821         is!
3  3  0.463468           a
4  4  0.643961      random

sum за замовчуванням конкатенати

In [9]: df.groupby('A')['C'].apply(lambda x: x.sum())
Out[9]: 
A
1    Thisstring
2           is!
3             a
4        random
dtype: object

Ви можете робити майже все, що завгодно

In [11]: df.groupby('A')['C'].apply(lambda x: "{%s}" % ', '.join(x))
Out[11]: 
A
1    {This, string}
2           {is, !}
3               {a}
4          {random}
dtype: object

Роблячи це на цілому кадрі, по одній групі за раз. Ключовим є повернення аSeries

def f(x):
     return Series(dict(A = x['A'].sum(), 
                        B = x['B'].sum(), 
                        C = "{%s}" % ', '.join(x['C'])))

In [14]: df.groupby('A').apply(f)
Out[14]: 
   A         B               C
A                             
1  2  1.615586  {This, string}
2  4  0.421821         {is, !}
3  3  0.463468             {a}
4  4  0.643961        {random}

Здається, ці операції зараз векторизовані, усуваючи потребу в applyі lambda. Я прийшов сюди, цікавлячись, чому pandasнасправді конкрети і не повернути помилку підсумовування рядків.
НельсонГон

1
Якщо ви намагаєтеся стиснути рядки і додати символ між ними, рішення .agg, рекомендоване @voithos нижче, набагато швидше, ніж тут. Рекомендовано. Під час тестування я набирала 5-10 разів швидше.
Подвоєння

70

Ви можете використовувати applyспосіб застосувати довільну функцію до згрупованих даних. Тож якщо ви хочете набір, подайте заявку set. Якщо ви хочете список, подайте заявку list.

>>> d
   A       B
0  1    This
1  2      is
2  3       a
3  4  random
4  1  string
5  2       !
>>> d.groupby('A')['B'].apply(list)
A
1    [This, string]
2           [is, !]
3               [a]
4          [random]
dtype: object

Якщо ви хочете чогось іншого, просто напишіть функцію, яка робить те, що ви хочете, і тоді applyце.


Працює нормально, але стовпець А відсутній.
Vineesh TP

@VineeshTP: стовпець A використовувався як групуючий стовпчик, тому він знаходиться в індексі, як ви бачите в прикладі. Ви можете повернути його як стовпець, використовуючи .reset_index().
BrenBarn

30

Можливо, ви можете використовувати функцію aggregate(або agg) для об'єднання значень. (Неперевірений код)

df.groupby('A')['B'].agg(lambda col: ''.join(col))

Це справді працює. Дивовижний. Як @voithos згадував "неперевірений", я не був дуже оптимістичним. Біт Я перевірив його версію як запис у словнику агг, і він працював за призначенням: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) Зробив мій день
matthhias

2
Якщо ви намагаєтеся стискати рядки разом з деяким типом роздільника, я вважаю, що ця пропозиція .agg набагато швидше, ніж .apply. Для набору даних 600k + текстових рядків я отримав однакові результати на 5-10 разів швидше.
Подвоєння

14

Ви можете спробувати це:

df.groupby('A').agg({'B':'sum','C':'-'.join})

2
З огляду: чи могли б ви додати більше пояснень до своєї відповіді?
toti08

1
GroupBy наносять на колонку «A» і з AGG функції я міг би використовувати різні функції на різних колонках кажуть підсумовувати елементи в стовпці «C», конкатенації елементів в колонці «C» при вставці «-» між словами
user3241146

8

простим рішенням буде:

>>> df.groupby(['A','B']).c.unique().reset_index()

це має бути правильна відповідь. змушує вас відповісти чисто. дуже дякую!
imsrgadich

Якщо ви хочете, щоб хтось зацікавився об'єднати вміст списку в рядок df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
Vivek-Ananth,

8

Названі агрегати з pandas >= 0.25.0

Починаючи з панд версії 0.25.0, ми назвали агрегації, де ми можемо групувати, об'єднувати та одночасно присвоювати нові імена нашим стовпцям. Таким чином, ми не отримаємо стовпці MultiIndex, а назви стовпців мають більше сенсу, враховуючи дані, які вони містять:


об'єднати та отримати список рядків

grp = df.groupby('A').agg(B_sum=('B','sum'),
                          C=('C', list)).reset_index()

print(grp)
   A     B_sum               C
0  1  1.615586  [This, string]
1  2  0.421821         [is, !]
2  3  0.463468             [a]
3  4  0.643961        [random]

об'єднати і з'єднати рядки

grp = df.groupby('A').agg(B_sum=('B','sum'),
                          C=('C', ', '.join)).reset_index()

print(grp)
   A     B_sum             C
0  1  1.615586  This, string
1  2  0.421821         is, !
2  3  0.463468             a
3  4  0.643961        random

6

Якщо ви хочете перезаписати стовпчик B у кадр даних, це має працювати:

    df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x))

2

Виходячи з гарної відповіді @ Ерфана, більшість разів під час аналізу сукупних значень ви хочете отримати унікальні можливі комбінації цих існуючих символьних значень:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.