Pandas groupby: Как получить объединение строк
У меня есть dataframe, как это:
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
поэтому я надеялся, что любой метод серии будет работать. Любые идеи?
Ответы
Ответ 1
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}
Ответ 2
Вы можете использовать метод 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
.
Ответ 3
Вы можете использовать функцию aggregate
(или agg
), чтобы объединить значения. (Непроверенный код)
df.groupby('A')['B'].agg(lambda col: ''.join(col))
Ответ 4
Вы можете попробовать это:
df.groupby('A').agg({'B':'sum','C':'-'.join})
Ответ 5
простое решение:
>>> df.groupby(['A','B']).c.unique().reset_index()
Ответ 6
Если вы хотите перезаписать столбец B в кадре данных, это должно работать:
df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x))
Ответ 7
Именованные агрегаты с 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
Ответ 8
После хорошего ответа @Erfan в большинстве случаев при анализе агрегированных значений вам нужны уникальные возможные комбинации этих существующих значений символов:
unique_chars = lambda x: ', '.join(x.unique())
(df
.groupby(['A'])
.agg({'C': unique_chars}))