【发布时间】:2021-10-19 16:43:35
【问题描述】:
TL;DR:我们如何在 pandas 中使用任何类型的聚合来实现类似于 Group By Roll Up 的效果? (此学期归功于@Scott Boston)
我有以下数据框:
P Q R S T
0 PLAC NR F HOL F
1 PLAC NR F NHOL F
2 TRTB NR M NHOL M
3 PLAC NR M NHOL M
4 PLAC NR F NHOL F
5 PLAC R M NHOL M
6 TRTA R F HOL F
7 TRTA NR F HOL F
8 TRTB NR F NHOL F
9 PLAC NR F NHOL F
10 TRTB NR F NHOL F
11 TRTB NR M NHOL M
12 TRTA NR F HOL F
13 PLAC NR F HOL F
14 PLAC R F NHOL F
对于['Q', 'R', 'S', 'T'] 的列列表,我想在以下 4 个分组列列表中计算P 列上的一些聚合:
['Q']['Q', 'R']['Q', 'R', 'S']['Q', 'R', 'S', 'T']
我已经编写了代码以将上述数据帧分组到越来越多的列中,并计算每个 groupby 对象的聚合(使用 count 为简单起见),最后将它们连接起来:
cols = list('QRST')
aggCol = 'P'
groupCols = []
result = []
for col in cols:
groupCols.append(col)
result.append(df.groupby(groupCols)[aggCol].agg(count='count').reset_index())
result = pd.concat(result)[groupCols+['count']]
但是,我强烈感觉上述方法在 CPU 时间方面效率不高。有没有更有效的方法来对不断增加的列数应用聚合以进行分组?
为什么我认为它效率不高是因为:对于上述值,在第一次迭代中,它将数据框分组到 Q 列然后计算聚合。然后在下一次迭代中,它将数据帧分组到 Q 和 R,这意味着它再次需要按 Q 然后 R 分组,但在第一次迭代中它已经按 Q 分组,所以重复相同的操作。如果有一些方法可以利用之前创建的组,我认为它会很有效。
输出:
Q R S T count
0 NR NaN NaN NaN 12
1 R NaN NaN NaN 3
0 NR F NaN NaN 9
1 NR M NaN NaN 3
2 R F NaN NaN 2
3 R M NaN NaN 1
0 NR F HOL NaN 4
1 NR F NHOL NaN 5
2 NR M NHOL NaN 3
3 R F HOL NaN 1
4 R F NHOL NaN 1
5 R M NHOL NaN 1
0 NR F HOL F 4
1 NR F NHOL F 5
2 NR M NHOL M 3
3 R F HOL F 1
4 R F NHOL F 1
5 R M NHOL M 1
我已经研究了Is there an equivalent of SQL GROUP BY ROLLUP in Python pandas? 和Pandas Pivot tables row subtotals,它们在我的情况下不起作用,我已经尝试过它们,即这些方法只能用于获取计数,并且即使对于唯一计数,当相同时也会立即失败标识符出现多个值:
pd.pivot_table(df, aggCol, columns=cols, aggfunc='count', margins=True).T.reset_index()
Q R S T P
0 NR F HOL F 4
1 NR F NHOL F 5
2 NR M NHOL M 3
3 NR All 3
4 R F HOL F 1
5 R F NHOL F 1
6 R M NHOL M 1
7 R All 3
更新
为了避免根据评论中的建议获取 count 时产生不必要的混淆,我已将其添加为聚合的平均值,将 P 列更改为数字类型:
P Q R S T
0 9 NR F HOL F
1 7 NR F NHOL F
2 3 NR M NHOL M
3 9 NR M NHOL M
4 1 NR F NHOL F
5 0 R M NHOL M
6 1 R F HOL F
7 7 NR F HOL F
8 2 NR F NHOL F
9 2 NR F NHOL F
10 1 NR F NHOL F
11 2 NR M NHOL M
12 3 NR F HOL F
13 6 NR F HOL F
14 0 R F NHOL F
cols = list('QRST')
cols = list('QRST')
aggCol = 'P'
groupCols = []
result = []
for col in cols:
groupCols.append(col)
result.append(df.groupby(groupCols)[aggCol]
.agg(agg=np.mean)
.round(2).reset_index())
result = pd.concat(result)[groupCols+['agg']]
>>> result
Q R S T agg
0 NR NaN NaN NaN 4.33
1 R NaN NaN NaN 0.33
0 NR F NaN NaN 4.22
1 NR M NaN NaN 4.67
2 R F NaN NaN 0.50
3 R M NaN NaN 0.00
0 NR F HOL NaN 6.25
1 NR F NHOL NaN 2.60
2 NR M NHOL NaN 4.67
3 R F HOL NaN 1.00
4 R F NHOL NaN 0.00
5 R M NHOL NaN 0.00
0 NR F HOL F 6.25
1 NR F NHOL F 2.60
2 NR M NHOL M 4.67
3 R F HOL F 1.00
4 R F NHOL F 0.00
5 R M NHOL M 0.00
【问题讨论】:
-
只是一个想法:如何先制作
['Q','R','S','T']-grouped 结果,然后使用索引计算“更高级别”组计数? -
@Bill 感谢您的关注。这不仅仅是计数,而是几个不同的聚合。我只是使用
count来简单地理解实际的问题描述。而且,是的,你所说的似乎是正确的方法,用最多的列分组,然后计算一次下降一个级别的聚合。 -
代替
count,尝试使用mean进行说明;这将立即让读者关注困难的部分(因为mean不具有关联性;有人会说它不能以单子方式组合)。 -
太棒了;我有一个通用的解决方案。有时
groupby将输出压缩到Series时会出现问题。耐心... -
在我的回答中,我还添加了一个可选的总计(可以使用
total=False禁用它)。这与 SQL 的“group by roll up”一致,其中包括总计(所有分组列NaN)。
标签: python pandas dataframe pandas-groupby