Czy uzyskać statystyki dla każdej grupy (takie jak liczba, średnia itp.) Za pomocą pand GroupBy?

438

Mam ramkę danych dfi używam z niej kilku kolumn do groupby:

df['col1','col2','col3','col4'].groupby(['col1','col2']).mean()

W powyższy sposób prawie otrzymuję potrzebną tabelę (ramkę danych). Brakuje dodatkowej kolumny, która zawiera liczbę wierszy w każdej grupie. Innymi słowy, mam na myśli, ale chciałbym również wiedzieć, ile liczb wykorzystano do uzyskania tych środków. Na przykład w pierwszej grupie jest 8 wartości, w drugiej 10 i tak dalej.

W skrócie: Jak uzyskać statystyki grupowe dla ramki danych?

rzymski
źródło

Odpowiedzi:

427

Na groupbyobiekcie aggfunkcja może pobrać listę, aby zastosować kilka metod agregacji jednocześnie. To powinno dać ci wynik, którego potrzebujesz:

df[['col1', 'col2', 'col3', 'col4']].groupby(['col1', 'col2']).agg(['mean', 'count'])
Boud
źródło
2
Myślę, że potrzebujesz odniesienia do kolumny, aby być listą. Czy może masz na myśli: df[['col1','col2','col3','col4']].groupby(['col1','col2']).agg(['mean', 'count'])
rysqui,
43
To tworzy cztery kolumny zliczeń, ale jak zdobyć tylko jedną? (Pytanie dotyczy „dodatkowej kolumny” i tego też chciałbym.)
Jaan
16
Proszę zobaczyć moją odpowiedź, jeśli chcesz uzyskać tylko jedną countkolumnę na grupę.
Pedro M Duarte,
Co jeśli mam osobną liczbę o nazwie Liczniki i zamiast zliczać wiersze zgrupowanego typu, muszę dodać wzdłuż kolumny Liczniki.
Abhishek Bhatia
@Jan result = df['col1','col2','col3','col4'].groupby(['col1', 'col2']).mean() ; counts = times.groupby(['col1', 'col2']).size() ; result['count'] = counts
alvitawa
911

Szybka odpowiedź:

Najprostszym sposobem uzyskania liczby wierszy na grupę jest wywołanie .size(), które zwraca Series:

df.groupby(['col1','col2']).size()


Zwykle chcesz ten wynik jako DataFrame(zamiast a Series), dzięki czemu możesz:

df.groupby(['col1', 'col2']).size().reset_index(name='counts')


Jeśli chcesz dowiedzieć się, jak obliczyć liczbę wierszy i inne statystyki dla każdej grupy, czytaj dalej poniżej.


Szczegółowy przykład:

Rozważ następujący przykładowy frameframe:

In [2]: df
Out[2]: 
  col1 col2  col3  col4  col5  col6
0    A    B  0.20 -0.61 -0.49  1.49
1    A    B -1.53 -1.01 -0.39  1.82
2    A    B -0.44  0.27  0.72  0.11
3    A    B  0.28 -1.32  0.38  0.18
4    C    D  0.12  0.59  0.81  0.66
5    C    D -0.13 -1.65 -1.64  0.50
6    C    D -1.42 -0.11 -0.18 -0.44
7    E    F -0.00  1.42 -0.26  1.17
8    E    F  0.91 -0.47  1.35 -0.34
9    G    H  1.48 -0.63 -1.14  0.17

Najpierw użyjmy, .size()aby uzyskać liczbę wierszy:

In [3]: df.groupby(['col1', 'col2']).size()
Out[3]: 
col1  col2
A     B       4
C     D       3
E     F       2
G     H       1
dtype: int64

Następnie użyjmy, .size().reset_index(name='counts')aby uzyskać liczbę wierszy:

In [4]: df.groupby(['col1', 'col2']).size().reset_index(name='counts')
Out[4]: 
  col1 col2  counts
0    A    B       4
1    C    D       3
2    E    F       2
3    G    H       1


W tym wyniki dla większej liczby statystyk

Gdy chcesz obliczyć statystyki zgrupowanych danych, zwykle wygląda to tak:

In [5]: (df
   ...: .groupby(['col1', 'col2'])
   ...: .agg({
   ...:     'col3': ['mean', 'count'], 
   ...:     'col4': ['median', 'min', 'count']
   ...: }))
Out[5]: 
            col4                  col3      
          median   min count      mean count
col1 col2                                   
A    B    -0.810 -1.32     4 -0.372500     4
C    D    -0.110 -1.65     3 -0.476667     3
E    F     0.475 -0.47     2  0.455000     2
G    H    -0.630 -0.63     1  1.480000     1

Powyższy wynik jest trochę irytujący ze względu na zagnieżdżone etykiety kolumn, a także dlatego, że liczba wierszy dotyczy poszczególnych kolumn.

Aby uzyskać większą kontrolę nad danymi wyjściowymi, zwykle dzielę statystyki na poszczególne agregacje, które następnie łączę za pomocą join. To wygląda tak:

In [6]: gb = df.groupby(['col1', 'col2'])
   ...: counts = gb.size().to_frame(name='counts')
   ...: (counts
   ...:  .join(gb.agg({'col3': 'mean'}).rename(columns={'col3': 'col3_mean'}))
   ...:  .join(gb.agg({'col4': 'median'}).rename(columns={'col4': 'col4_median'}))
   ...:  .join(gb.agg({'col4': 'min'}).rename(columns={'col4': 'col4_min'}))
   ...:  .reset_index()
   ...: )
   ...: 
Out[6]: 
  col1 col2  counts  col3_mean  col4_median  col4_min
0    A    B       4  -0.372500       -0.810     -1.32
1    C    D       3  -0.476667       -0.110     -1.65
2    E    F       2   0.455000        0.475     -0.47
3    G    H       1   1.480000       -0.630     -0.63



Przypisy

Kod użyty do wygenerowania danych testowych pokazano poniżej:

In [1]: import numpy as np
   ...: import pandas as pd 
   ...: 
   ...: keys = np.array([
   ...:         ['A', 'B'],
   ...:         ['A', 'B'],
   ...:         ['A', 'B'],
   ...:         ['A', 'B'],
   ...:         ['C', 'D'],
   ...:         ['C', 'D'],
   ...:         ['C', 'D'],
   ...:         ['E', 'F'],
   ...:         ['E', 'F'],
   ...:         ['G', 'H'] 
   ...:         ])
   ...: 
   ...: df = pd.DataFrame(
   ...:     np.hstack([keys,np.random.randn(10,4).round(2)]), 
   ...:     columns = ['col1', 'col2', 'col3', 'col4', 'col5', 'col6']
   ...: )
   ...: 
   ...: df[['col3', 'col4', 'col5', 'col6']] = \
   ...:     df[['col3', 'col4', 'col5', 'col6']].astype(float)
   ...: 


Zrzeczenie się:

Jeśli niektóre kolumny, które agregujesz, mają wartości null, naprawdę chcesz spojrzeć na wiersze grupy jako niezależne agregowanie dla każdej kolumny. W przeciwnym razie możesz zostać wprowadzony w błąd co do tego, ile rekordów faktycznie używa się do obliczania rzeczy takich jak średnia, ponieważ pandy będą NaNzapisywać wpisy w obliczeniach średnich, nie mówiąc ci o tym.

Pedro M. Duarte
źródło
1
Hej, naprawdę podoba mi się twoje rozwiązanie, szczególnie ostatnie, w którym używasz łączenia łańcuchowego. Ponieważ jednak często konieczne jest zastosowanie różnych funkcji agregujących do różnych kolumn, można także skonkatować wynikowe ramki danych za pomocą pd.concat. To może być łatwiejsze do odczytania niż kolejne łańcuchy
Quickbeam2k1
4
fajne rozwiązanie, ale In [5]: counts_df = pd.DataFrame(df.groupby('col1').size().rename('counts'))może lepiej ustawić rozmiar () jako nową kolumnę, jeśli chcesz manipulować ramką danych w celu dalszej analizy, która powinna byćcounts_df = pd.DataFrame(df.groupby('col1').size().reset_index(name='counts')
LancelotHolmes
2
Dziękujemy za fragment „Dołączanie wyników dla większej liczby statystyk”! Ponieważ moje następne wyszukiwanie dotyczyło spłaszczenia wynikowego multiindeksu na kolumnach, odsyłam do odpowiedzi tutaj: stackoverflow.com/a/50558529/1026
Nickolay
Świetny! Czy mógłbyś mi podpowiedzieć, jak dodać isnulldo tego zapytania, aby mieć je również w jednej kolumnie? 'col4': ['median', 'min', 'count', 'isnull']
Peter.k
38

Jedna funkcja, aby rządzić nimi wszystkimi: GroupBy.describe

Powroty count, mean, std, i inne przydatne statystyki per-grupy.

df.groupby(['col1', 'col2'])['col3', 'col4'].describe()

# Setup
np.random.seed(0)
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                          'foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three',
                          'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8),
                   'D' : np.random.randn(8)})

from IPython.display import display

with pd.option_context('precision', 2):
    display(df.groupby(['A', 'B'])['C'].describe())

           count  mean   std   min   25%   50%   75%   max
A   B                                                     
bar one      1.0  0.40   NaN  0.40  0.40  0.40  0.40  0.40
    three    1.0  2.24   NaN  2.24  2.24  2.24  2.24  2.24
    two      1.0 -0.98   NaN -0.98 -0.98 -0.98 -0.98 -0.98
foo one      2.0  1.36  0.58  0.95  1.15  1.36  1.56  1.76
    three    1.0 -0.15   NaN -0.15 -0.15 -0.15 -0.15 -0.15
    two      2.0  1.42  0.63  0.98  1.20  1.42  1.65  1.87

Aby uzyskać określone statystyki, wystarczy je wybrać,

df.groupby(['A', 'B'])['C'].describe()[['count', 'mean']]

           count      mean
A   B                     
bar one      1.0  0.400157
    three    1.0  2.240893
    two      1.0 -0.977278
foo one      2.0  1.357070
    three    1.0 -0.151357
    two      2.0  1.423148

describedziała dla wielu kolumn (zmień ['C']na ['C', 'D']- lub usuń go całkowicie - i zobacz, co się stanie, wynikiem jest ramka danych z kolumną MultiIndexed).

Otrzymujesz także różne statystyki dla danych ciągów. Oto przykład

df2 = df.assign(D=list('aaabbccc')).sample(n=100, replace=True)

with pd.option_context('precision', 2):
    display(df2.groupby(['A', 'B'])
               .describe(include='all')
               .dropna(how='all', axis=1))

              C                                                   D                
          count  mean       std   min   25%   50%   75%   max count unique top freq
A   B                                                                              
bar one    14.0  0.40  5.76e-17  0.40  0.40  0.40  0.40  0.40    14      1   a   14
    three  14.0  2.24  4.61e-16  2.24  2.24  2.24  2.24  2.24    14      1   b   14
    two     9.0 -0.98  0.00e+00 -0.98 -0.98 -0.98 -0.98 -0.98     9      1   c    9
foo one    22.0  1.43  4.10e-01  0.95  0.95  1.76  1.76  1.76    22      2   a   13
    three  15.0 -0.15  0.00e+00 -0.15 -0.15 -0.15 -0.15 -0.15    15      1   c   15
    two    26.0  1.49  4.48e-01  0.98  0.98  1.87  1.87  1.87    26      2   b   15

Aby uzyskać więcej informacji, zobacz dokumentację .

cs95
źródło
Nie wszystkie dystrybucje są normalne. IQR byłby niesamowity.
Brad
7

Możemy to łatwo zrobić, używając funkcji grupowania i liczenia. Ale powinniśmy pamiętać, aby użyć reset_index ().

df[['col1','col2','col3','col4']].groupby(['col1','col2']).count().\
reset_index()
Nimesh
źródło
3
To rozwiązanie działa, dopóki w kolumnach nie ma wartości zerowej, w przeciwnym razie może być mylące (liczba będzie mniejsza niż faktyczna liczba obserwacji według grup).
Adrien Pacifico
4

Aby uzyskać wiele statystyk, zwinąć indeks i zachować nazwy kolumn:

df = df.groupby(['col1','col2']).agg(['mean', 'count'])
df.columns = [ ' '.join(str(i) for i in col) for col in df.columns]
df.reset_index(inplace=True)
df

Produkuje:

** wprowadź opis zdjęcia tutaj **

Jake Drew
źródło
1

Utwórz obiekt grupy i wywołaj metody takie jak poniżej:

grp = df.groupby(['col1',  'col2',  'col3']) 

grp.max() 
grp.mean() 
grp.describe() 
Mahendra
źródło
1

Spróbuj tego kodu

new_column=df[['col1', 'col2', 'col3', 'col4']].groupby(['col1', 'col2']).count()
df['count_it']=new_column
df

Myślę, że kod doda kolumnę o nazwie „policz to”, która liczy każdą grupę

Ichsan
źródło