pandas按组聚合多列自定义函数

8

我正在尝试在pandas中使用自定义函数与groupby一起使用。我发现使用apply可以以以下方式进行:

(一个计算两组新平均值的例子)

import pandas as pd

def newAvg(x):
    x['cm'] = x['count']*x['mean']
    sCount = x['count'].sum()
    sMean = x['cm'].sum()
    return sMean/sCount

data = [['A', 4, 2.5], ['A', 3, 6], ['B', 4, 9.5], ['B', 3, 13]]
df = pd.DataFrame(data, columns=['pool', 'count', 'mean'])

df_gb = df.groupby(['pool']).apply(newAvg)

是否可以将此集成到agg函数中?类似这样:

df.groupby(['pool']).agg({'count': sum, ['count', 'mean']: apply(newAvg)})
5个回答

5

IIUC

df.groupby(['pool']).apply(lambda x : pd.Series({'count':sum(x['count']),'newavg':newAvg(x)}))
Out[58]: 
      count  newavg
pool               
A       7.0     4.0
B       7.0    11.0

我非常喜欢这个。虽然感谢大家。 - Christian
@Christian 祝你编程愉快 - BENY

3

函数agg将每列分别处理,因此可能的解决方案是首先使用assign创建列cm,然后聚合sum,最后除以每列:

df_gb = df.assign(cm=df['count']*df['mean']).groupby('pool')['cm','count'].sum()
print (df_gb)
        cm  count
pool             
A     28.0      7
B     77.0      7

out = df_gb.pop('cm') / df_gb.pop('count')
print (out)
pool
A     4.0
B    11.0
dtype: float64

3

使用evalassign

df.assign(cm=df['count']*df['mean'])\
  .groupby('pool', as_index=False)['cm','count'].sum()\
  .eval('AggCol = cm / count')

输出:

  pool    cm  count  AggCol
0    A  28.0      7     4.0
1    B  77.0      7    11.0

2

使用具有 agg 的字典可用于对每个系列执行单独的计算。对于您的问题,我建议使用 pd.concat

g = df.groupby('pool')
res = pd.concat([g['count'].sum(), g.apply(newAvg).rename('newAvg')], axis=1)

print(res)

#       count  newAvg
# pool               
# A         7     4.0
# B         7    11.0

这并不是最高效的解决方案,因为你的函数newAvg执行的计算可以在整个数据框上进行,但它支持任意预定义的计算。

1
如果您正在计算加权平均值,可以使用agg和NumPy np.average函数轻松完成。只需读取“mean”列的Series即可:
df_gb = df.groupby(['pool']).agg(lambda x: np.average(x['mean'], weights=x['count']))['mean']

你也可以使用 newAvg 函数来实现,但这样会产生警告:
df_gb2 = df.groupby(['pool']).agg(newAvg)['mean']

如果您愿意使用newAvg函数,您可以重新定义它以避免对副本进行操作:
def newAvg(x):
    cm = x['count']*x['mean']
    sCount = x['count'].sum()
    sMean = cm.sum()
    return sMean/sCount

使用此修改,您将获得预期的输出:
df_gb2 = df.groupby(['pool']).agg(newAvg)['mean']
print(df_gb2)

# pool
# A     4.0
# B    11.0
# Name: mean, dtype: float64

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接