Wiele agregacji tej samej kolumny przy użyciu pandas GroupBy.agg ()

129

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

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

Nieprawidłowy składniowo, ale intuicyjnie właściwy sposób na zrobienie tego to:

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

Oczywiście Python nie zezwala na zduplikowane klucze. Czy istnieje inny sposób wyrażenia wkładu agg()? Może lista krotek [(column, function)]działałaby lepiej, umożliwiając stosowanie wielu funkcji do tej samej kolumny? Ale agg()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 niej? (Jak to i tak działałoby z agregacją?)

ely
źródło
Powiązane - Agregacja w
pandach
2
Począwszy od 0.25 pandy zapewniają bardziej intuicyjną składnię dla wielu agregacji, a także zmieniają nazwy kolumn wyjściowych. Zobacz dokumentację nazwanych agregacji .
cs95
Do Twojej wiadomości to pytanie zostało zadane na pandach 0.8.x w 9/2012
smci
1
FYI zaakceptowana odpowiedź jest również przestarzała - nie przekazuj agg () a dict of dicts.
cs95
@ cs95: Wiem, że jest przestarzały, mówię, że SO jest zaśmiecony starymi, nieaktualnymi rozwiązaniami ze starych wersji. SO nie ma innego sposobu na zaznaczenie tego - poza komentarzami.
smci

Odpowiedzi:

159

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
bmu
źródło
5
Czy istnieje sposób określenia nazw kolumn wynikowych?
Ben
3
@Ben Myślę, że później musisz użyć zmiany nazwy. przykład autorstwa Toma Augspurgera (patrz komórka 25)
Stewbaca,
1
@Ben: Dodałem przykład
bmu
10
@sparc_spread Przekazywanie wielu funkcji jako listy jest dobrze opisane w dokumentacji pand . Zmiana nazwy i przekazywanie wielu funkcji jako słownika zostanie wycofana w przyszłych wersjach pand. Szczegóły znajdują się w dzienniku zmian 0,20 , który podsumowałem również w innym miejscu na SO .
joelostblom
3
Zostało to już powiedziane, ale używanie słowników do zmiany nazw kolumn wyjściowych z wieku jest przestarzałe. Zamiast tego możesz określić listę krotek. Zobacz tę odpowiedź.
cs95
102

TLDR; Pandy groupby.aggma nową, łatwiejszą składnię do określania (1) agregacji w wielu kolumnach i (2) wielu agregacji w kolumnie. 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.aggna rzecz bardziej intuicyjnej składni do określania nazwanych agregacji. Zobacz sekcję 0.25 docs dotyczącą ulepszeń, a także istotne problemy z GitHub GH18366 i GH26512 .

Z dokumentacji,

Aby obsługiwać agregację specyficzną dla kolumny z kontrolą nad nazwami kolumn wyjściowych, pandy akceptuje specjalną składnię w GroupBy.agg(), znaną jako „nazwane agregowanie”, gdzie

  • Słowa kluczowe to nazwy kolumn wyjściowych
  • Wartości to krotki, których pierwszym elementem jest kolumna do wybrania, a drugim elementem jest agregacja do zastosowania w tej kolumnie. Pandas dostarcza pandas.NamedAgg namedtuple z polami ['column', 'aggfunc'], aby lepiej zrozumieć, jakie są argumenty. Jak zwykle agregacja może być aliasem wywoływalnym lub ciągiem.

Możesz 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 nazwanego tupli), 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

W przypadku Series jest to jeszcze prostsze, wystarczy przekazać funkcję 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ą prawidłowymi identyfikatorami Pythona, użyj słownika z rozpakowywaniem:

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

Pandy <0,25

W nowszych wersjach pand prowadzących do 0,24, jeśli używasz słownika do określania nazw kolumn dla danych wyjściowych 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żna to określić prościej, przekazując listę krotek. Jeśli określasz funkcje 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
cs95
źródło
4
To powinna być najlepsza odpowiedź ze względu na zastosowanie bardziej przejrzystego i przejrzystego rozwiązania przy użyciu nowszej wersji interfejsu.
NKSHELL
Przykłady użyte dla nazwanej agregacji nie rozwiązują pierwotnego problemu używania wielu agregacji w tej samej kolumnie. Na przykład, czy możesz agregować zarówno przez min, jak i max dla wysokości bez uprzedniego podzbioru dla df.groupby('kind')['height']?
zwycięzca
1
@victor Dodałem TLDR na górze odpowiedzi, która bezpośrednio dotyczy pytania. Odpowiedź na drugie pytanie brzmi: tak, proszę spojrzeć na zmianę w mojej odpowiedzi.
cs95
Bardziej ogólny kod do ostatniego przykładu Twojej odpowiedzi> = 0,25 do obsługi agregacji wielu kolumn w ten sposób byłby świetny. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece
6

Czy coś takiego jak ta praca:

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
Chang She
źródło
2
Nie, to nie działa. Jeśli spojrzysz na ciąg dokumentacyjny, aggregatektóry wyraźnie mówi, że gdy dictprzekazywane jest a , klucze muszą być nazwami kolumn. Więc albo twój przykład jest czymś, co wpisałeś bez sprawdzania tego błędu, albo Pandy psują tutaj swoje własne dokumenty.
ely
N / MI nie widział tam dodatkowego wezwania returns. Więc to jest seria agregatów w wersji? Chcę wykonać agregację w wersji DataFrame i chcę jednocześnie zastosować kilka różnych agregacji do każdej kolumny.
ely
1
Spróbuj tego: df.groupby ('dummy'). Agg ({'return': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Ona
Daje błąd asercji bez komunikatu. Z wyglądu kodu (pandas.core.internals.py, wiersze 406-408, wersja 0.7.3) wygląda na to, że sprawdza na końcu, czy nie zwraca więcej kolumn niż jest kluczy w pierwszym warstwa słownika agregacji.
ely
Działa dobrze na master. Chcesz spróbować zaktualizować?
Chang She