【问题标题】:How to write custom aggregate function in pandas that transforms a series?如何在转换系列的熊猫中编写自定义聚合函数?
【发布时间】:2025-12-22 08:30:15
【问题描述】:

所以我有一个这样的数据框

df = pd.DataFrame({'item_id':[1,2,3,4,5,6,7,8,9,10], 'category':['A', 'B', 'A', 'C', 'B', 'B', 'C', 'A', 'A', 'C'], 'sales': [100, 150, 300, 1000, 300, 50, 1000, 600, 700, 100]})

   item_id category  sales
0        1        A    100
1        2        B    150
2        3        A    300
3        4        C   1000
4        5        B    300
5        6        B     50
6        7        C   1000
7        8        A    600
8        9        A    700
9       10        C    100

我想要每件商品的总销售额的累计百分比,从销量最多到销量最少。像这样:

df = df.sort_values(by = 'sales', ascending = False)
df['pct_of_total'] = df['sales']/df['sales'].sum()
df['cumsum_pct_of_total'] = df['pct_of_total'].cumsum()

   item_id category  sales  pct_of_total  cumsum_pct_of_total
3        4        C   1000      0.232558             0.232558
6        7        C   1000      0.232558             0.465116
8        9        A    700      0.162791             0.627907
7        8        A    600      0.139535             0.767442
2        3        A    300      0.069767             0.837209
4        5        B    300      0.069767             0.906977
1        2        B    150      0.034884             0.941860
0        1        A    100      0.023256             0.965116
9       10        C    100      0.023256             0.988372
5        6        B     50      0.011628             1.000000

但要注意的是,我希望这个过程不是针对整个数据框,而是在每个类别中。我尝试了一个自定义函数:

def acc_pct(s):
  s = s.sort_values(ascending = False)
  s = s/s.sum()
  s = s.cumsum()
  return s.sort_index()

df.groupby('category').agg({'sales':acc_pct})

但它没有用。它会抛出一个ValueError: Must produce aggregated value

我知道它必须是可能的,因为 groupby.cumcount()、groupby.cumsum() 和 groupby.shift() 的工作方式很像这样。我该怎么做?

【问题讨论】:

    标签: python pandas group-by aggregation split-apply-combine


    【解决方案1】:

    尝试除以groupby transform sum 得到pct_of_total 然后groupby cumsum 新列:

    df = df.sort_values('sales', ascending=False)
    df['pct_of_total'] = (
            df['sales'] / df.groupby('category')['sales'].transform('sum')
    )
    df['cumsum_pct_of_total'] = df.groupby('category')['pct_of_total'].cumsum()
    

    df:

       item_id category  sales  pct_of_total  cumsum_pct_of_total
    3        4        C   1000      0.476190             0.476190
    6        7        C   1000      0.476190             0.952381
    8        9        A    700      0.411765             0.411765
    7        8        A    600      0.352941             0.764706
    2        3        A    300      0.176471             0.941176
    4        5        B    300      0.600000             0.600000
    1        2        B    150      0.300000             0.900000
    0        1        A    100      0.058824             1.000000
    9       10        C    100      0.047619             1.000000
    5        6        B     50      0.100000             1.000000
    

    【讨论】:

    • 你是最棒的!!!这完全有效!也几乎适用于我的自定义函数 `df.groupby('category')['sales'].transform(acc_pct)
    • 是的。您不会像聚合中那样减少框架中的行。这绝对是一次转型操作。不过这里的数学运算应该比函数快。