Erhalten Sie Statistiken für jede Gruppe (wie Anzahl, Mittelwert usw.) mit pandas GroupBy?

435

Ich habe einen Datenrahmen dfund verwende mehrere Spalten daraus, um groupby:

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

Auf die obige Weise bekomme ich fast die Tabelle (Datenrahmen), die ich brauche. Was fehlt, ist eine zusätzliche Spalte, die die Anzahl der Zeilen in jeder Gruppe enthält. Mit anderen Worten, ich habe gemein, aber ich würde auch gerne wissen, wie viele Zahlen verwendet wurden, um diese Mittel zu erhalten. Zum Beispiel gibt es in der ersten Gruppe 8 Werte und in der zweiten 10 Werte und so weiter.

Kurz gesagt: Wie erhalte ich gruppenweise Statistiken für einen Datenrahmen?

römisch
quelle

Antworten:

425

Für ein groupbyObjekt kann die aggFunktion eine Liste erstellen , um mehrere Aggregationsmethoden gleichzeitig anzuwenden . Dies sollte Ihnen das Ergebnis liefern, das Sie benötigen:

df[['col1', 'col2', 'col3', 'col4']].groupby(['col1', 'col2']).agg(['mean', 'count'])
Boud
quelle
2
Ich denke, Sie brauchen die Spaltenreferenz, um eine Liste zu sein. df[['col1','col2','col3','col4']].groupby(['col1','col2']).agg(['mean', 'count'])
Meinen
43
Dadurch werden vier Zählspalten erstellt, aber wie erhält man nur eine? (Die Frage fragt nach "einer zusätzlichen Spalte" und das möchte ich auch.)
Jaan
16
Bitte lesen Sie meine Antwort, wenn Sie nur eine countSpalte pro Gruppe erhalten möchten .
Pedro M Duarte
Was ist, wenn ich einen separaten Namen namens Counts habe und anstatt die Zeilen des gruppierten Typs zu zählen, muss ich entlang der Spalte Counts hinzufügen.
Abhishek Bhatia
@ Jaan result = df['col1','col2','col3','col4'].groupby(['col1', 'col2']).mean() ; counts = times.groupby(['col1', 'col2']).size() ; result['count'] = counts
Alvitawa
910

Schnelle Antwort:

Der einfachste Weg, um die Anzahl der Zeilen pro Gruppe zu ermitteln, ist ein Aufruf .size(), der Folgendes zurückgibt Series:

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


Normalerweise möchten Sie dieses Ergebnis als DataFrame(anstelle von Series), damit Sie Folgendes tun können:

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


Wenn Sie herausfinden möchten, wie die Zeilenanzahl und andere Statistiken für jede Gruppe berechnet werden, lesen Sie weiter unten.


Detailliertes Beispiel:

Betrachten Sie den folgenden Beispieldatenrahmen:

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

Lassen Sie .size()uns zuerst die Zeilenanzahl ermitteln:

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

Dann verwenden wir .size().reset_index(name='counts'), um die Zeilenanzahl zu erhalten:

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


Einschließlich Ergebnisse für weitere Statistiken

Wenn Sie Statistiken zu gruppierten Daten berechnen möchten, sieht dies normalerweise folgendermaßen aus:

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

Das obige Ergebnis ist aufgrund der verschachtelten Spaltenbeschriftungen und auch der Zeilenanzahl pro Spalte etwas ärgerlich.

Um mehr Kontrolle über die Ausgabe zu erhalten, teile ich die Statistiken normalerweise in einzelne Aggregationen auf, die ich dann mit kombiniere join. Es sieht aus wie das:

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



Fußnoten

Der zum Generieren der Testdaten verwendete Code wird unten gezeigt:

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


Haftungsausschluss:

Wenn einige der Spalten, die Sie aggregieren, Nullwerte haben, möchten Sie wirklich die Anzahl der Gruppenzeilen als unabhängige Aggregation für jede Spalte betrachten. Andernfalls werden Sie möglicherweise irregeführt, wie viele Datensätze tatsächlich zur Berechnung des Mittelwerts verwendet werden, da Pandas NaNEinträge in der Mittelwertberechnung löschen, ohne Sie darüber zu informieren.

Pedro M Duarte
quelle
1
Hey, ich mag deine Lösung wirklich, besonders die letzte, bei der du Methodenverkettung verwendest. Da es jedoch häufig erforderlich ist, unterschiedliche Aggregationsfunktionen auf unterschiedliche Spalten anzuwenden, können die resultierenden Datenrahmen auch mit pd.concat verknüpft werden. Dies ist vielleicht einfacher zu lesen als eine Folgeverkettung
Quickbeam2k1
4
nette Lösung, aber für In [5]: counts_df = pd.DataFrame(df.groupby('col1').size().rename('counts')), vielleicht ist es besser, die Größe () als neue Spalte festzulegen, wenn Sie den Datenrahmen für die weitere Analyse manipulieren möchten, die sein solltecounts_df = pd.DataFrame(df.groupby('col1').size().reset_index(name='counts')
LancelotHolmes
2
Vielen Dank für das Bit "Ergebnisse für mehr Statistiken einschließen"! Da es bei meiner nächsten Suche darum ging, den resultierenden Multiindex für Spalten zu reduzieren
Nickolay
Großartig! Könnten Sie mir bitte einen Hinweis geben, wie Sie isnulldiese Abfrage ergänzen können, damit sie auch in einer Spalte angezeigt wird? 'col4': ['median', 'min', 'count', 'isnull']
Peter.k
38

Eine Funktion, um sie alle zu regieren: GroupBy.describe

Returns count, mean, stdund andere nützliche Statistiken pro-Gruppe.

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

Um bestimmte Statistiken zu erhalten, wählen Sie sie einfach aus.

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

describefunktioniert für mehrere Spalten (ändern Sie ['C']zu['C', 'D'] - oder entfernen Sie es ganz - und sehen Sie, was passiert. Das Ergebnis ist ein MultiIndexed-Spalten-Datenrahmen).

Sie erhalten auch verschiedene Statistiken für Zeichenfolgendaten. Hier ist ein Beispiel:

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

Weitere Informationen finden Sie in der Dokumentation .

cs95
quelle
Nicht alle Distributionen sind normal. IQR wäre unglaublich.
Brad
7

Wir können es einfach tun, indem wir groupby und count verwenden. Wir sollten jedoch daran denken, reset_index () zu verwenden.

df[['col1','col2','col3','col4']].groupby(['col1','col2']).count().\
reset_index()
Nimesh
quelle
3
Diese Lösung funktioniert, solange die Spalten keinen Nullwert enthalten. Andernfalls kann sie irreführend sein (die Anzahl ist niedriger als die tatsächliche Anzahl der Beobachtungen pro Gruppe).
Adrien Pacifico
4

Um mehrere Statistiken zu erhalten, reduzieren Sie den Index und behalten Sie die Spaltennamen bei:

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

Produziert:

** Geben Sie hier die Bildbeschreibung ein **

Jake Drew
quelle
1

Erstellen Sie ein Gruppenobjekt und rufen Sie Methoden wie im folgenden Beispiel auf:

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

grp.max() 
grp.mean() 
grp.describe() 
Mahendra
quelle
1

Bitte versuchen Sie diesen Code

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

Ich denke, dieser Code fügt eine Spalte mit dem Namen "count it" hinzu, die für jede Gruppe zählt

Ichsan
quelle