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

поэтому я надеялся, что любой метод Series будет работать. Любые идеи?

Энн
источник

Ответы:

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самом деле объединяются и не возвращаются ошибки при суммировании строк.
NelsonGon
1
Если вы пытаетесь объединить строки и добавить символ между ними, решение .agg, рекомендованное @voithos ниже, намного быстрее, чем рекомендуемое здесь .apply. В моем тестировании я становился в 5-10 раз быстрее.
Doubledown
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это.

BrenBarn
источник
Работает нормально, но столбец A отсутствует.
Vineesh TP 08
@VineeshTP: столбец A использовался в качестве столбца группировки, поэтому он находится в индексе, как вы можете видеть в примере. Вы можете вернуть его в виде столбца, используя .reset_index().
BrenBarn
30

Вы можете использовать функцию aggregate(или agg) для объединения значений. (Непроверенный код)

df.groupby('A')['B'].agg(lambda col: ''.join(col))
voithos
источник
Это действительно работает. Удивительный. Поскольку @voithos упомянул "непроверенный", я был не очень оптимистичен. Бит, я тестировал его версию как запись в словаре agg, и она работала как задумано: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) Сделал мой день
маттиас
2
Если вы пытаетесь объединить строки вместе с каким-либо разделителем, я обнаружил, что это предложение .agg намного быстрее, чем .apply. Для набора данных из 600k + текстовых строк я получил идентичные результаты в 5-10 раз быстрее.
Doubledown
14

Вы можете попробовать это:

df.groupby('A').agg({'B':'sum','C':'-'.join})
user3241146
источник
2
Из обзора: не могли бы вы добавить дополнительные пояснения к своему ответу?
toti08
1
Groupby применяется к столбцу «A», и с помощью функции agg я мог бы использовать разные функции в разных столбцах, например, суммировать элементы в столбце «C», объединять элементы в столбце «C», вставляя «-» между словами
user3241146
8

простое решение:

>>> df.groupby(['A','B']).c.unique().reset_index()
UserYmY
источник
это должен быть правильный ответ. дает вам чистый ответ. большое спасибо!
imsrgadich
Если в случае, если кто-то заинтересован в объединении содержимого списка в строку df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
Вивек-Анант
8

Именованные агрегаты с pandas >= 0.25.0

Начиная с версии pandas 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
Erfan
источник
6

Если вы хотите перезаписать столбец B во фрейме данных, это должно сработать:

    df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x))
Amit
источник
2

Следуя хорошему ответу @ Erfan, в большинстве случаев при анализе совокупных значений вам нужны уникальные возможные комбинации этих существующих значений символов:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
Поль Руже
источник