【问题标题】:Multiple aggregations of the same column using pandas GroupBy.agg()使用 pandas GroupBy.agg() 对同一列进行多个聚合
【发布时间】:2012-09-17 08:32:41
【问题描述】:

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

示例数据框:

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

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

【问题讨论】:

  • 从 0.25 开始,pandas 为多个聚合以及重命名输出列提供了更直观的语法。请参阅Named Aggregations 上的文档。
  • 仅供参考,这个问题早在 2012 年 9 月的 pandas 0.8.x 上就被问到了
  • 仅供参考,接受的答案也已弃用 - 不要将 agg() 传递给 dicts。
  • @cs95:我知道它已被弃用,我是说 SO 中充斥着旧版本的旧解决方案。 SO 没有办法标记它 - 除了 cmets。

标签: python pandas dataframe aggregate pandas-groupby


【解决方案1】:

这样的工作是否可行:

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

【讨论】:

  • 不,这不起作用。如果您查看aggregate 的文档字符串,它明确表示当传递dict 时,键必须是列名。因此,要么您的示例是您在没有检查此错误的情况下输入的内容,要么 Pandas 在这里破坏了它自己的文档。
  • N/M 我没有看到对returns 的额外调用。所以这是聚合的系列版本?我正在寻找聚合的 DataFrame 版本,我想一次对每一列应用几个不同的聚合。
  • 试试这个:df.groupby('dummy').agg({'returns': {'func1' : lambda x: x.sum(), 'func2' : lambda x: x.意思是()}})
  • 它给出了一个没有消息的断言错误。从代码的外观来看(pandas.core.internals.py,第 406-408 行,版本 0.7.3),它看起来像是在最后进行检查以确保它返回的列不超过第一个中的键聚合字典的层。
  • 在 master 上运行良好。您想尝试更新吗?
【解决方案2】:

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

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

为了避免弃用警告:

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

【讨论】:

【解决方案3】:

TLDR; Pandas groupby.agg 有一个新的、更简单的语法,用于指定 (1) 多列上的聚合,以及 (2) 列上的多个聚合。因此,要对 pandas >= 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:Named Aggregation

Pandas 改变了 GroupBy.agg 的行为,转而采用更直观的语法来指定命名聚合。请参阅 0.25 docs section on Enhancements 以及相关的 GitHub 问题 GH18366GH26512

从文档中,

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

  • 关键字是输出列名
  • 值是元组,其第一个元素是要选择的列,第二个元素是要应用于该列的聚合。 Pandas 为 pandas.NamedAgg 命名元组提供字段 ['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(本质上是一个命名元组),这会使事情更加明确。

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.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

Using a dictionary for renaming columns is deprecated in v0.20. 在更新版本的 pandas 上,这可以通过传递元组列表更简单地指定。如果以这种方式指定函数,则该列的 所有 函数需要指定为 (name, function) 对的元组。

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

【讨论】:

  • 这应该是最佳答案,因为使用较新版本的界面使用更清晰和干净的解决方案。
  • 用于命名聚合的示例并没有解决在同一列上使用多个聚合的原始问题。例如,您能否在不为df.groupby('kind')['height'] 设置第一个子集的情况下同时按高度的最小值和最大值进行聚合?
  • @victor 我在直接解决问题的答案顶部添加了一个 TLDR。你的第二个问题的答案是肯定的,请看一下我的答案的编辑。
  • 对您的 >=0.25 答案的最后一个示例更通用的代码来处理像这样聚合多个列会很棒。 df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
  • 很好的答案!你如何用 lambda 函数做到这一点?
猜你喜欢
  • 2016-01-02
  • 2019-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-07
  • 2020-09-29
相关资源
最近更新 更多