是否有熊猫内置的方法来应用两个不同的聚合函数f1, f2到同一列df["返回"],而不必多次调用agg() ?

示例dataframe:

import pandas as pd
import datetime as dt
import numpy as np

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()似乎只接受字典。

除了定义一个辅助函数来应用它里面的两个函数,还有什么解决方法吗?(这在聚合中是如何工作的呢?)


当前回答

TLDR;熊猫groupby。Agg有一个新的更简单的语法,可以指定(1)多个列上的聚合,以及(2)一个列上的多个聚合。因此,为熊猫>= 0.25执行此操作,使用

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

OR

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandas >= 0.25:命名聚合

Pandas改变了GroupBy的行为。Agg,支持更直观的语法来指定命名聚合。参见0.25文档部分的增强以及相关的GitHub问题GH18366和GH26512。

从文档来看,

To support column-specific aggregation with control over the output column names, pandas accepts the special syntax in GroupBy.agg(), known as “named aggregation”, where The keywords are the output column names The values are tuples whose first element is the column to select and the second element is the aggregation to apply to that column. Pandas provides the pandas.NamedAgg namedtuple with the fields ['column', 'aggfunc'] to make it clearer what the arguments are. As usual, the aggregation can be a callable or a string alias.

您现在可以通过关键字参数传递一个元组。元组的格式为(<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之前的pandas最新版本中,如果使用字典为聚合输出指定列名,则会得到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中不支持使用字典重命名列。在最新版本的pandas中,可以通过传递一个元组列表来更简单地指定这一点。如果以这种方式指定函数,则该列的所有函数都需要指定为(名称,函数)对的元组。

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

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Or,

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

            op1       op2
dummy                    
1      0.328953  0.032895

其他回答

如果需要对多个列应用相同的多个聚合函数,最简单的方法(imo)是使用字典理解。

#setup
df = pd.DataFrame({'dummy': [0, 1, 1], 'A': range(3), 'B':range(1, 4), 'C':range(2, 5)})

# aggregation
df.groupby("dummy").agg({k: ['sum', 'mean'] for k in ['A', 'B', 'C']})

上面的结果是一个带有MultiIndex列的数据框架。如果希望使用平面自定义列名,则可以使用命名聚合(如本文其他答案所建议的那样)。

如文档中所述,键应该是输出列名,值应该是命名聚合的元组(column, aggregation function)。由于有多个列和多个函数,这将导致一个嵌套结构。要将其平铺成单个字典,可以使用collections.ChainMap()或嵌套循环。

同样,如果希望将grouper列(dummy)作为列(而不是索引),则在groupby()中指定as_index=False。

from collections import ChainMap
# convert a list of dictionaries into a dictionary
dct = dict(ChainMap(*reversed([{f'{k}_total': (k, 'sum'), f'{k}_mean': (k, 'mean')} for k in ['A','B','C']])))
# {'A_total': ('A', 'sum'), 'A_avg': ('A', 'mean'), 'B_total': ('B', 'sum'), 'B_avg': ('B', 'mean'), 'C_total': ('C', 'sum'), 'C_avg': ('C', 'mean')}

# the same result obtained by a nested loop
# dct = {k:v for k in ['A','B','C'] for k,v in [(f'{k}_total', (k, 'sum')), (f'{k}_avg', (k, 'mean'))]}

# aggregation
df.groupby('dummy', as_index=False).agg(**dct)

TLDR;熊猫groupby。Agg有一个新的更简单的语法,可以指定(1)多个列上的聚合,以及(2)一个列上的多个聚合。因此,为熊猫>= 0.25执行此操作,使用

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

OR

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandas >= 0.25:命名聚合

Pandas改变了GroupBy的行为。Agg,支持更直观的语法来指定命名聚合。参见0.25文档部分的增强以及相关的GitHub问题GH18366和GH26512。

从文档来看,

To support column-specific aggregation with control over the output column names, pandas accepts the special syntax in GroupBy.agg(), known as “named aggregation”, where The keywords are the output column names The values are tuples whose first element is the column to select and the second element is the aggregation to apply to that column. Pandas provides the pandas.NamedAgg namedtuple with the fields ['column', 'aggfunc'] to make it clearer what the arguments are. As usual, the aggregation can be a callable or a string alias.

您现在可以通过关键字参数传递一个元组。元组的格式为(<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之前的pandas最新版本中,如果使用字典为聚合输出指定列名,则会得到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中不支持使用字典重命名列。在最新版本的pandas中,可以通过传递一个元组列表来更简单地指定这一点。如果以这种方式指定函数,则该列的所有函数都需要指定为(名称,函数)对的元组。

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

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Or,

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

            op1       op2
dummy                    
1      0.328953  0.032895

截至2022-06-20,以下是可接受的聚合实践:

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

下面的折叠包括历史版本的熊猫。

你可以简单地以列表的形式传递函数:

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

像这样的东西会有用吗:

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