Pandas GroupBy - 如何将行数保持在累计和的百分比?

Posted

技术标签:

【中文标题】Pandas GroupBy - 如何将行数保持在累计和的百分比?【英文标题】:Pandas GroupBy - How to Keep Rows Up to Percentage of Cumulative Sum? 【发布时间】:2018-05-11 16:43:09 【问题描述】:

我有一个未排序的数据框:

df
     A   B  Moves
0   E1  E2     10
1   E1  E3     20
2   E1  E4     15
3   E2  E1      9
4   E2  E3      8
5   E2  E4      7
6   E3  E1     30
7   E3  E2     32
8   E3  E4     40
9   E4  E1      5
10  E4  E2     20
11  E4  E3      3

我想返回行B,直到它们的累积总和达到AB 的每个分组的总总数Moves 的某个最小百分比(我先取最高值)。

一旦达到 % 阈值,我将停止获取行(累积总和)。该过程必须是“贪婪的”,因为如果一行超过所需的百分比,它包括该行。

如果占总数的最小百分比是50%,那么我想先返回:

期望的输出

     A   B  Moves
    E1  E3     20
    E1  E4     15
    E2  E1      9
    E2  E3      8
    E3  E4     40
    E3  E2     32
    E4  E2     20

然后我想使用 df.groupby(...).apply(list) fromthis question 提取每个分组的行名

A     Most_Moved
E1      [E3, E4] 
E2      [E1, E3]
E3      [E4, E2]
E4          [E2]

我的尝试:

我可以在this 问题和this 问题中返回使用cumsum 订购的Total_Moves:

df.groupby(by=['A','B']).sum().groupby(level=[0]).cumsum()[::-1]

       Moves
A  B        
E4 E3     28
   E2     25
   E1      5
E3 E4    102
   E2     62
   E1     30
E2 E4     24
   E3     17
   E1      9
E1 E4     45
   E3     30
   E2     10

另外,我可以返回每组的总移动数(总和):

df.groupby(by="A").sum()

    Moves
A        
E1     45
E2     24
E3    102
E4     28

从this question 和this question 我可以将每一行作为该类别总和的百分比返回:

df.groupby(by=["A"])["Moves"].apply(lambda x: 100 * x / float(x.sum()))

0     22.222222
1     44.444444
2     33.333333
3     37.500000
4     33.333333
5     29.166667
6     29.411765
7     31.372549
8     39.215686
9     17.857143
10    71.428571
11    10.714286

什么不起作用

但是,如果我将这些结合起来,它会评估整个行的百分比:

df.groupby(by=["A", "B"])["Moves"].agg("Total_Moves":sum).sort_values("Total_Moves", ascending=False).apply(lambda x: 100 * x / float(x.sum()))

       Total_Moves
A  B              
E3 E4    20.100503
   E2    16.080402
   E1    15.075377
E1 E3    10.050251
E4 E2    10.050251
E1 E4     7.537688
   E2     5.025126
E2 E1     4.522613
   E3     4.020101
   E4     3.517588
E4 E1     2.512563
   E3     1.507538

这会评估整个数据框的百分比,而不是单个组内的百分比。

我只是不知道如何将这些拼凑起来得到我的输出。

任何帮助表示赞赏。

【问题讨论】:

【参考方案1】:

您可以将groupby.apply 与自定义函数一起使用

def select(group, pct=50):
    # print(group)
    moves = group['Moves'].sort_values(ascending=False)
    cumsum = moves.cumsum() / moves.sum()
    # print(cumsum)
    # `cumsum` is the cumulative contribution of the sorted moves
    idx = len(cumsum[cumsum < pct/100]) + 1
    # print(idx)
    # `idx` is the first index of the move which has a cumulative sum of `pct` or higher
    idx = moves.index[:idx]  
    # print(idx)
    # here, `idx` is the Index of all the moves in with a cumulative contribution of `pct` or higher
    # print(group.loc[idx])
    return group.loc[idx].set_index(['B'], drop=True)['Moves']
    # return a Series of Moves with column `B` as index of the items which have index `idx`
df.groupby('A').apply(select)
        Moves
A   B   
E1  E3  20
    E4  15
E2  E1  9
    E3  8
E3  E4  40
    E2  32
E4  E2  20

编辑

我在代码中添加了一些 cmets。为了更清楚它的作用,我还添加了(注释)中间变量的打印语句。如果您取消注释它们,请不要惊讶第一组被打印出来twice

【讨论】:

谢谢马丁。完美 - 这适用于我上面的示例和我的真实数据集。 感谢您添加 cmets。

以上是关于Pandas GroupBy - 如何将行数保持在累计和的百分比?的主要内容,如果未能解决你的问题,请参考以下文章

如何将行转换为按行数创建标题的列

尝试将行附加到按对象分组中的每个组时的奇怪行为

将行附加到 pandas DataFrame 而不制作新副本

Python/Pandas - 结合 groupby 平均值和最小值

将行添加到多个工作表时清除错误

Excel公式复制和粘贴具有相同列字母的单元格,但在粘贴时将行数增加7