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это.


Работает нормально, но столбец A отсутствует.
Vineesh TP 08

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

30

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

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

Это действительно работает. Удивительный. Поскольку @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})

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()
Вивек-Анант

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

6

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

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

2

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

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.