Wiele agregacji tej samej kolumny za pomocą pandy GroupBy.agg()

Czy jest wbudowany sposób na zastosowanie dwóch różnych funkcji agregujących f1, f2 do tej samej kolumny df["returns"], bez konieczności wielokrotnego wywoływania agg()?

Przykładowa ramka danych:

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)
}) 

Składniowo błędnym, ale intuicyjnie właściwym sposobem na to byłoby:

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

Oczywiście Python nie pozwala na duplikowanie kluczy. Czy jest jakiś inny sposób na wyrażenie wejścia do agg()? Być może Lista krotek [(column, function)] byłaby lepsza, aby umożliwić wielokrotne funkcje zastosowane do tej samej kolumny? Ale wygląda na to, że akceptuje tylko słownik.

Czy istnieje obejście tego problemu poza zdefiniowaniem funkcji pomocniczej, która po prostu stosuje obie funkcje wewnątrz niego? (Jak to w ogóle działa z agregacją?)

Author: smci, 2012-09-25

3 answers

Możesz po prostu przekazać funkcje jako listę:

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

Lub jako słownik:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012
 197
Author: bmu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-08 09:16:31

TLDR; Pandas groupby.agg ma nową, łatwiejszą składnię do określania (1) agregacji na wielu kolumnach i (2) Wielu agregacji na kolumnie. Więc, aby to zrobić dla pand >= 0.25, użyj

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

Lub

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandy > = 0.25: Nazwane Agregacje

Pandy zmieniły zachowanie GroupBy.agg na rzecz bardziej intuicyjnej składni do określania nazw agregacji. Zobacz sekcję 0.25 docs na temat ulepszeń, a także istotne Problemy z githubem GH18366 i GH26512 .

Z dokumentacji,

Do obsługi agregacji specyficznej dla kolumn z kontrolą nad wyjściem nazwy kolumn, pandy przyjmują specjalną składnię w GroupBy.agg(), znane jako "named aggregation", gdzie

  • słowa kluczowe są nazwami kolumn wyjściowych
  • wartości są krotkami, których pierwszym elementem jest kolumna do wybrania, a drugim element jest agregacja do zastosowania do tej kolumny. Pandy zapewniają pandy.NamedAgg namedtuple with the fields ['column', 'aggfunc'] aby było jasne, jakie są argumenty. Jako Zwykle agregacja może być aliasem wywoływalnym lub ciągiem znaków.

Można teraz przekazać krotkę za pomocą argumentów słów kluczowych. Krotki mają format (<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

Alternatywnie, możesz użyć pd.NamedAgg (zasadniczo nazwastuple), co czyni rzeczy bardziej wyraźnymi.

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

Jest jeszcze prostsze dla serii, wystarczy przejść aggfunc do argumentu słowa kluczowego.

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       

Na koniec, jeśli nazwy kolumn nie są poprawnymi identyfikatorami Pythona, użyj słownika z rozpakowaniem:

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

Pandy

W nowszych wersjach pand prowadzących do 0.24, jeśli używasz słownika do określania nazw kolumn dla wyjścia agregacji, otrzymasz 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

Używanie słownika do zmiany nazw kolumn jest przestarzałe w wersji 0.20. w nowszych wersjach pand, może to być określa się po prostu poprzez podanie listy krotek. W przypadku określenia funkcji w ten sposób, wszystkie funkcje dla tej kolumny muszą być określone jako krotki par (nazwa, funkcja).

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

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Lub

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

            op1       op2
dummy                    
1      0.328953  0.032895
 124
Author: cs95,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-08 09:34:11

Czy coś takiego działa:

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
 6
Author: Chang She,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-26 01:30:08