簡體   English   中英

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

[英]Multiple aggregations of the same column using pandas GroupBy.agg()

是否有一種 pandas 內置方法可以將兩個不同的聚合函數f1, f2應用於同一列df["returns"] ,而不必多次調用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()似乎只接受字典。

除了定義僅應用其中兩個功能的輔助 function 之外,是否有解決方法? (無論如何,這將如何與聚合一起工作?)

截至 2022 年 6 月 20 日,以下是公認的聚合做法:

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

包含在pandas歷史版本的首屏下方。

您可以簡單地將函數作為列表傳遞:

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

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

Pandas >= 0.25: 命名聚合

Pandas 改變了GroupBy.agg的行為,轉而采用更直觀的語法來指定命名聚合。 請參閱有關增強功能的 0.25 文檔部分以及相關的 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.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 上,這可以通過傳遞元組列表更簡單地指定。 如果以這種方式指定函數,則該列的所有函數都需要指定為 (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

像這樣的工作:

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

如果您有多個列需要應用相同的多個聚合函數,最簡單的方法 (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)

平坦的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM