pandas groupby 可以聚合成一个列表,而不是 sum、mean 等吗?
Posted
技术标签:
【中文标题】pandas groupby 可以聚合成一个列表,而不是 sum、mean 等吗?【英文标题】:Can pandas groupby aggregate into a list, rather than sum, mean, etc? 【发布时间】:2013-11-01 01:45:05 【问题描述】:我已经成功地使用 groupby 函数按组对给定变量求和或平均,但是有没有办法聚合成一个值列表,而不是得到一个结果? (这还叫聚合吗?)
我不完全确定这是我应该采取的方法,所以下面是我想用玩具数据进行的转换示例。
也就是说,如果数据看起来像这样:
A B C
1 10 22
1 12 20
1 11 8
1 10 10
2 11 13
2 12 10
3 14 0
我想要得到的结果类似于以下内容。我不完全确定这是否可以通过 groupby 聚合到列表中来完成,并且不知道从这里去哪里。
假设输出:
A B C New1 New2 New3 New4 New5 New6
1 10 22 12 20 11 8 10 10
2 11 13 12 10
3 14 0
也许我应该改为追求支点?将数据放入列的顺序无关紧要 - 此示例中的所有列 B 到 New6 都是等效的。非常感谢所有建议/更正。
【问题讨论】:
【参考方案1】:我用了以下
grouped = df.groupby('A')
df = grouped.aggregate(lambda x: tuple(x))
df['grouped'] = df['B'] + df['C']
【讨论】:
这个得到我的投票!它返回一个分组数据框,其单元格内容是包含组中包含的值的列表。 如果你想将一列聚合成一个列表,你可以这样做df.groupby('A', as_index=False)['B'].agg('list':(lambda x: list(x)))
只要df.groupby('A', as_index=False)['B'].agg(list)
就可以了。
tuple
已经可以作为函数调用了,所以不用写.aggregate(lambda x: tuple(x))
可以直接.aggregate(tuple)
。想想看,您正在创建一个函数,其唯一目的是使用单个参数调用另一个函数。
知道如何处理空记录,目前它正在将其转换为 nan 并且无法对其进行任何操作。【参考方案2】:
我正在回答标题和第一句话中所述的问题:以下将值汇总到列表中。
import pandas as pd
df = pd.DataFrame( 'A' : [1, 1, 1, 1, 2, 2, 3], 'B' : [10, 12, 11, 10, 11, 12, 14], 'C' : [22, 20, 8, 10, 13, 10, 0])
print df
df2 = df.groupby('A').aggregate(lambda tdf: tdf.unique().tolist())
print df2
# Old version:
# df2=df.groupby(['A']).apply(lambda tdf: pd.Series( dict([[vv,tdf[vv].unique().tolist()] for vv in tdf if vv not in ['A']]) ))
输出如下:
In [3]: run tmp
A B C
0 1 10 22
1 1 12 20
2 1 11 8
3 1 10 10
4 2 11 13
5 2 12 10
6 3 14 0
[7 rows x 3 columns]
B C
A
1 [10, 12, 11] [22, 20, 8, 10]
2 [11, 12] [13, 10]
3 [14] [0]
[3 rows x 2 columns]
【讨论】:
唯一真正符合问题所述的答案!对问题本身投反对票,因为“聚合到列表中”与创建新列非常不同...... 承认我并没有真正阅读过这个问题,但当我在 Google 上搜索pandas groupby array_agg
时,这个问题实现了我所希望的。我正在寻找与 SQL (postgres) array_agg 等效的东西。这个答案实际上比那个(imo)好一点,它 unique 数组聚合除 groupedby 之外的每一列。【参考方案3】:
这里是一个班轮
# if list of unique items is desired, use set
df.groupby('A',as_index=False)['B'].aggregate(lambda x: set(x))
# if duplicate items are okay, use list
df.groupby('A',as_index=False)['B'].aggregate(lambda x: list(x))
【讨论】:
我认为必须在'B'周围使用两组括号才能使这项工作,即df.groupby('A',as_index=False)[['B']].aggregate(lambda x: set(x))
其他一班:df.groupby('A', as_index=False).aggregate(pd.Series.tolist)
df.groupby('A', as_index=False).aggregate(lambda x: x.unique().tolist())
好的解决方案,更好的用户名【参考方案4】:
类似的解决方案,但相当透明(我认为)。您可以获得完整列表或唯一列表。
df = pd.DataFrame('A':[1,1,2,2,2,3,3,3,4,5],
'B':[6,7, 8,8,9, 9,9,10,11,12],
'C':['foo']*10)
df
Out[24]:
A B C
0 1 6 foo
1 1 7 foo
2 2 8 foo
3 2 8 foo
4 2 9 foo
5 3 9 foo
6 3 9 foo
7 3 10 foo
8 4 11 foo
9 5 12 foo
list_agg = df.groupby(by='A').agg('B':lambda x: list(x),
'C':lambda x: tuple(x))
list_agg
Out[26]:
C B
A
1 (foo, foo) [6, 7]
2 (foo, foo, foo) [8, 8, 9]
3 (foo, foo, foo) [9, 9, 10]
4 (foo,) [11]
5 (foo,) [12]
unique_list_agg = df.groupby(by='A').agg('B':lambda x: list(pd.unique(x)),
'C':lambda x: tuple(pd.unique(x)))
unique_list_agg
Out[28]:
C B
A
1 (foo,) [6, 7]
2 (foo,) [8, 9]
3 (foo,) [9, 10]
4 (foo,) [11]
5 (foo,) [12]
【讨论】:
【参考方案5】:我的解决方案比您预期的要长一些,我敢肯定它可以缩短,但是:
g = df.groupby("A").apply(lambda x: pd.concat((x["B"], x["C"])))
k = g.reset_index()
k["i"] = k1.index
k["rn"] = k1.groupby("A")["i"].rank()
k.pivot_table(rows="A", cols="rn", values=0)
# output
# rn 1 2 3 4 5 6
# A
# 1 10 12 11 22 20 8
# 2 10 11 10 13 NaN NaN
# 3 14 10 NaN NaN NaN NaN
稍微解释一下。第一行,g = df.groupby("A").apply(lambda x: pd.concat((x["B"], x["C"])))
。这一组 df
by A
然后将列 B
和 C
放入一列:
A
1 0 10
1 12
2 11
0 22
1 20
2 8
2 3 10
4 11
3 10
4 13
3 5 14
5 10
然后k = g.reset_index()
,创建顺序索引,结果为:
A level_1 0
0 1 0 10
1 1 1 12
2 1 2 11
3 1 0 22
4 1 1 20
5 1 2 8
6 2 3 10
7 2 4 11
8 2 3 10
9 2 4 13
10 3 5 14
11 3 5 10
现在我想将此索引移动到列中(我想听听如何在不重置索引的情况下创建顺序列),k["i"] = k1.index
:
A level_1 0 i
0 1 0 10 0
1 1 1 12 1
2 1 2 11 2
3 1 0 22 3
4 1 1 20 4
5 1 2 8 5
6 2 3 10 6
7 2 4 11 7
8 2 3 10 8
9 2 4 13 9
10 3 5 14 10
11 3 5 10 11
现在,k["rn"] = k1.groupby("A")["i"].rank()
将在每个 A
中添加 row_number(就像 SQL 中的 row_number() over(partition by A order by i)
:
A level_1 0 i rn
0 1 0 10 0 1
1 1 1 12 1 2
2 1 2 11 2 3
3 1 0 22 3 4
4 1 1 20 4 5
5 1 2 8 5 6
6 2 3 10 6 1
7 2 4 11 7 2
8 2 3 10 8 3
9 2 4 13 9 4
10 3 5 14 10 1
11 3 5 10 11 2
最后,只需使用k.pivot_table(rows="A", cols="rn", values=0)
进行旋转:
rn 1 2 3 4 5 6
A
1 10 12 11 22 20 8
2 10 11 10 13 NaN NaN
3 14 10 NaN NaN NaN NaN
【讨论】:
实际上你完成了我在第一行代码中寻找的终点!我以为我必须先分组并组装列表(如此处的另一个答案所示),但这直接切入了目标。我接受这个是因为它是我正在使用的,但另一个答案也是我解释问题的方式的一个很好的解决方案。 This answer 是一个更好的通用答案。【参考方案6】:我一直在努力解决完全相同的问题,答案是可以,您可以使用 grouby 获取列表。我不是 100% 确定我是以最 Pythonic 的方式来做这件事的,但在这里我试图解决你的问题是值得的。您可以创建包含在按组中的数据列表,如下所示:
import pandas as pd
import numpy as np
from itertools import chain
Data = 'A' : [1, 1, 1, 1, 2, 2, 3], 'B' : [10, 12, 11, 10, 11, 12, 14], 'C' : [22, 20, 8, 10, 13, 10, 0]
DF = pd.DataFrame(Data)
DFGrouped = DF.groupby('A')
OutputLists = []
for group in DFGrouped:
AList = list(group[1].A)
BList = list(group[1].B)
CList = list(group[1].C)
print list(group[1].A)
print list(group[1].B)
print list(group[1].C)
ZIP = zip(BList, CList)
print ZIP
OutputLists.append(list(chain(*ZIP)))
OutputLists
这会以我认为您想要的方式将您的数据输出到列表列表中。然后,您可以将其设为数据框。上述印刷声明仅用于清楚说明。使用我的方法执行此操作的最有效(就代码而言)方法如下:
import pandas as pd
import numpy as np
from itertools import chain
Data = 'A' : [1, 1, 1, 1, 2, 2, 3], 'B' : [10, 12, 11, 10, 11, 12, 14], 'C' : [22, 20, 8, 10, 13, 10, 0]
DF = pd.DataFrame(Data)
DFGrouped = DF.groupby('A')
OutputLists = []
for group in DFGrouped:
ZIPPED = zip(group[1].B, group[1].C)
OutputLists.append(list(chain(*ZIPPED)))
OutputLists
据我所知,从分组数据中获取列表的关键是认识到数据本身存储在分组数据中每个组的 group[1] 中。
希望这会有所帮助!
【讨论】:
【参考方案7】:df2 = df.groupby('A').aggregate(lambda tdf: tdf.unique().tolist())
这似乎很完美,但生成的数据框有两层列,而 df.columns 仅显示数据框中的一列。 要更正此问题,请使用:
df2_copy=df2.copy()
df2_copy = df2_copy.reset_index(col_level=0)
您可以使用以下方法查看列级别: df2_copy.columns=df2_copy.columns.get_level_values(0)
df2_copy()
应该可以解决这个问题。
【讨论】:
以上是关于pandas groupby 可以聚合成一个列表,而不是 sum、mean 等吗?的主要内容,如果未能解决你的问题,请参考以下文章
Pandas`agc`列表,“AttributeError / ValueError:函数不减少”