Pandas GroupBy - 如何保持行达到累计和的百分比?
Pandas GroupBy - How to Keep Rows Up to Percentage of Cumulative Sum?
我有一个未排序的数据框:
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
我想要 return 行 B
,直到它们的累计总和达到 B
B
中每个分组的总和的某个最小百分比 Moves
21=](我先拿最高的)。
一旦达到 % 阈值,我就停止获取行(累计和)。该过程必须是 "greedy",因为如果某行超过所需的百分比,则它包括该行。
如果占总数的最小百分比是50%,那么我要先return:
期望输出
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]
我试过的:
我可以 return 在 question and 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
另外我可以return每组的总步数(总和):
df.groupby(by="A").sum()
Moves
A
E1 45
E2 24
E3 102
E4 28
根据 this question and 问题,我可以 return 每行占该类别总和的百分比:
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
这会评估整个数据框的百分比,而不是单个组中的百分比。
我只是不知道如何将这些拼凑起来以获得我的输出。
感谢任何帮助。
您可以将 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
编辑
我在代码中添加了一些注释。为了更清楚地说明它的作用,我还添加(注释)了中间变量的打印语句。如果您取消注释它们,请不要惊讶第一组被打印 twice
我有一个未排序的数据框:
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
我想要 return 行 B
,直到它们的累计总和达到 B
B
中每个分组的总和的某个最小百分比 Moves
21=](我先拿最高的)。
一旦达到 % 阈值,我就停止获取行(累计和)。该过程必须是 "greedy",因为如果某行超过所需的百分比,则它包括该行。
如果占总数的最小百分比是50%,那么我要先return:
期望输出
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]
我试过的:
我可以 return 在 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
另外我可以return每组的总步数(总和):
df.groupby(by="A").sum()
Moves
A
E1 45
E2 24
E3 102
E4 28
根据 this question and
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
这会评估整个数据框的百分比,而不是单个组中的百分比。
我只是不知道如何将这些拼凑起来以获得我的输出。
感谢任何帮助。
您可以将 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
编辑
我在代码中添加了一些注释。为了更清楚地说明它的作用,我还添加(注释)了中间变量的打印语句。如果您取消注释它们,请不要惊讶第一组被打印 twice