使用pandas GroupBy.agg()对同一列进行多次聚合


127

是否有熊猫内置的方法将两个不同的聚合函数f1, f2应用于同一列df["returns"],而无需agg()多次调用?

示例数据框:

import pandas as pd
import datetime as dt

pd.np.random.seed(0)
df = pd.DataFrame({
         "date"    :  [dt.date(2012, x, 1) for x in range(1, 11)], 
         "returns" :  0.05 * np.random.randn(10), 
         "dummy"   :  np.repeat(1, 10)
}) 

语法上错误但直观上正确的方法是:

# Assume `f1` and `f2` are defined for aggregating.
df.groupby("dummy").agg({"returns": f1, "returns": f2})

显然,Python不允许重复的键。还有其他表达方式agg()吗?也许元组列表[(column, function)]可以更好地工作,以允许将多个函数应用于同一列?但agg()似乎它只接受字典。

除了定义仅在其中应用这两个功能的辅助功能之外,还有其他解决方法吗?(无论如何,这如何与聚合一起使用?)



2
从0.25开始,pandas为多种聚合提供了更直观的语法,并重命名了输出列。请参阅有关命名聚合的文档。
cs95

仅供参考,这个问题在9
2012

1
仅供参考,不赞成使用的答案也不要使用-不要将agg()传递给命令。
cs95

@ cs95:我知道它已过时,我说SO到处都是旧版本的旧解决方案。因此,除了注释之外,没有其他标记方式。
smci

Answers:


159

您可以简单地将函数作为列表传递:

In [20]: df.groupby("dummy").agg({"returns": [np.mean, np.sum]})
Out[20]:         
           mean       sum
dummy                    
1      0.036901  0.369012

或作为字典:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012

4
有没有办法指定结果列名称?

3
@Ben我认为您之后必须使用重命名。Tom Augspurger的示例(请参阅单元格25)
Stewbaca

1
@Ben:我添加了一个示例
bmu

10
@sparc_spread 在pandas文档中对将多个函数作为列表进行传递进行了详细说明。重命名和通过字典传递多个功能将在以后的熊猫版本中弃用。详细信息在0.20更改日志中,我也在SO的其他地方进行了总结
joelostblom

3
已经有人说过了,但是不建议使用字典从age重命名输出列。您可以改为指定一个元组列表。看到这个答案。
cs95

101

TLDR;Pandas groupby.agg具有一种新的,更简单的语法,用于指定(1)多列聚合,以及(2)一列多个聚合。因此,要对大于等于0.25的熊猫执行此操作,请使用

df.groupby('dummy').agg(Mean=('returns', 'mean'), Sum=('returns', 'sum'))

           Mean       Sum
dummy                    
1      0.036901  0.369012

要么

df.groupby('dummy')['returns'].agg(Mean='mean', Sum='sum')

           Mean       Sum
dummy                    
1      0.036901  0.369012

大熊猫> = 0.25:命名汇总

熊猫已经改变了行为,GroupBy.agg转而使用更直观的语法来指定命名聚合。请参阅0.25文档部分中的增强功能以及相关的GitHub问题GH18366GH26512

从文档中

为了支持特定于列的聚合并控制输出列名称,pandas接受特殊的语法GroupBy.agg(),称为“命名聚合”,其中

  • 关键字是输出列名称
  • 值是元组,其第一个元素是要选择的列,第二个元素是要应用于该列的聚合。Pandas为pandas.NamedAgg namedtuple提供了字段['column','aggfunc'],以使参数更清晰。像往常一样,聚合可以是可调用的或字符串别名。

您现在可以通过关键字参数传递一个元组。元组遵循的格式(<colName>, <aggFunc>)

import pandas as pd

pd.__version__                                                                                                                            
# '0.25.0.dev0+840.g989f912ee'

# Setup
df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
                   'height': [9.1, 6.0, 9.5, 34.0],
                   'weight': [7.9, 7.5, 9.9, 198.0]
})

df.groupby('kind').agg(
    max_height=('height', 'max'), min_weight=('weight', 'min'),)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

另外,您可以使用pd.NamedAgg(本质上是namedtuple)使事情更明确。

df.groupby('kind').agg(
    max_height=pd.NamedAgg(column='height', aggfunc='max'), 
    min_weight=pd.NamedAgg(column='weight', aggfunc='min')
)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

对于Series来说甚至更简单,只需将aggfunc传递给关键字参数即可。

df.groupby('kind')['height'].agg(max_height='max', min_height='min')    

      max_height  min_height
kind                        
cat          9.5         9.1
dog         34.0         6.0       

最后,如果您的列名不是有效的python标识符,请使用带有解包功能的字典:

df.groupby('kind')['height'].agg(**{'max height': 'max', ...})

熊猫<0.25

在最新版本的熊猫(最高可达0.24)中,如果使用字典为聚合输出指定列名,则会得到FutureWarning

df.groupby('dummy').agg({'returns': {'Mean': 'mean', 'Sum': 'sum'}})
# FutureWarning: using a dict with renaming is deprecated and will be removed 
# in a future version

v0.20中不推荐使用字典重命名列。在较新版本的熊猫上,可以通过传递元组列表来更简单地指定它。如果以这种方式指定函数,则该列的所有函数都必须指定为(名称,函数)对的元组。

df.groupby("dummy").agg({'returns': [('op1', 'sum'), ('op2', 'mean')]})

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

要么,

df.groupby("dummy")['returns'].agg([('op1', 'sum'), ('op2', 'mean')])

            op1       op2
dummy                    
1      0.328953  0.032895

4
这应该是最佳答案,因为使用了较新版本的界面,并且使用了更清晰,更干净的解决方案。
NKSHELL

用于命名聚合的示例并不能解决在同一列上使用多个聚合的原始问题。例如,您是否可以按高度的最小值和最大值进行汇总,而无需先为设置子集df.groupby('kind')['height']
胜利者

1
@victor我在答案的顶部添加了一个TLDR,可以直接解决该问题。第二个问题的答案是肯定的,请查看我的答案的编辑内容。
cs95

对于您的== 0.25答案的最后一个示例,使用更通用的代码来处理汇总多列这样的事情就好了。 df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece

6

这样的事情会做:

In [7]: df.groupby('dummy').returns.agg({'func1' : lambda x: x.sum(), 'func2' : lambda x: x.prod()})
Out[7]: 
              func2     func1
dummy                        
1     -4.263768e-16 -0.188565

2
不,这不起作用。如果查看aggregate它的doc字符串,则明确表示在dict传递a时,键必须是列名。因此,要么输入您的示例而无需检查此错误,要么Pandas在这里破坏了自己的文档。
2012年

N / MI returns在那里看不到额外的电话。这是聚合的系列版本吗?我正在寻找聚合的DataFrame版本,并且我想一次对每个列应用几种不同的聚合。
2012年

1
试试这个:df.groupby('dummy')。agg({'returns':{'func1':lambda x:x.sum(),'func2':lambda x:x.mean()}})
Chang她

它给出断言错误而没有消息。从代码的外观(pandas.core.internals.py,第406-408行,版本0.7.3)看,它似乎在末尾进行了检查,以确保返回的列不多于第一个内的键。聚合字典的层。
2012年

在master上工作正常。您想尝试更新吗?
Chang She
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.