Aggregation

Aggregationen beziehen sich auf jede Datentransformation, die skalare Werte aus Arrays erzeugt. In den vorangegangenen Beispielen wurden mehrere von ihnen verwendet, darunter count und sum. Ihr fragt euch nun vielleicht, was passiert, wenn ihr sum() auf ein GroupBy-Objekt anwendet. Für viele gängige Aggregationen, wie die in der folgenden Tabelle, gibt es optimierte Implementierungen. Sie sind jedoch nicht auf diesen Satz von Methoden beschränkt.

Funktionsname

Beschreibung

any, all

Gibt True zurück, wenn einer (ein oder mehrere Werte) oder alle Nicht-NA-Werte „truthy“ sind

count

Anzahl der Nicht-NA-Werte

cummin, cummax

Kumuliertes Minimum und Maximum der Nicht-NA-Werte

cumsum

Kumulative Summe der Nicht-NA-Werte

cumprod

Kumulatives Produkt von Nicht-NA-Werten

first, last

Erste und letzte Nicht-NA-Werte

mean

Mittelwert der Nicht-NA-Werte

median

Arithmetischer Median der Nicht-NA-Werte

min, max

Minimum und Maximum der Nicht-NA-Werte

nth

Abrufen des n-ten größten Wertes

ohlc

Berechnung von vier Open-high-low-close-Statistiken für zeitreihenähnliche Daten

prod

Produkt der Nicht-NA-Werte

quantile

Berechnet das Stichprobenquantil

rank

Ordinale Ränge von Nicht-NA-Werten, wie beim Aufruf von Series.rank

sum

Summe der Nicht-NA-Werte

std, var

Standardabweichung und Varianz der Stichprobe

Ihr könnt eigene Aggregationen verwenden und zusätzlich jede Methode aufrufen, die auch für das gruppierte Objekt definiert ist. Zum Beispiel wählt die Series-Methode nsmallest die kleinste angeforderte Anzahl von Werten aus den Daten aus.

Obwohl nsmallest nicht explizit für GroupBy implementiert ist, können wir es dennoch mit einer nicht optimierten Implementierung verwenden. Intern zerlegt GroupBy die Series, ruft df.nsmallest(n) für jeden Teil auf und fügt diese Ergebnisse dann im Ergebnisobjekt zusammen:

[1]:
import numpy as np
import pandas as pd
[2]:
df = pd.DataFrame(
    {
        "Title": [
            "Jupyter Tutorial",
            "Jupyter Tutorial",
            "PyViz Tutorial",
            None,
            "Python Basics",
            "Python Basics",
        ],
        "2021-12": [30134, 6073, 4873, None, 427, 95],
        "2022-01": [33295, 7716, 3930, None, 276, 226],
        "2022-02": [19651, 6547, 2573, None, 525, 157],
    }
)

df
[2]:
Title 2021-12 2022-01 2022-02
0 Jupyter Tutorial 30134.0 33295.0 19651.0
1 Jupyter Tutorial 6073.0 7716.0 6547.0
2 PyViz Tutorial 4873.0 3930.0 2573.0
3 None NaN NaN NaN
4 Python Basics 427.0 276.0 525.0
5 Python Basics 95.0 226.0 157.0
[3]:
grouped = df.groupby("Title")
[4]:
grouped["2022-01"].nsmallest(1)
[4]:
Title
Jupyter Tutorial  1    7716.0
PyViz Tutorial    2    3930.0
Python Basics     5     226.0
Name: 2022-01, dtype: float64

Um eine eigene Aggregationsfunktionen zu verwenden, übergebt eine beliebige Funktion, die ein Array aggregiert, an die Methode aggregate oder agg:

[5]:
def range(arr):
    return arr.max() - arr.min()


grouped.agg(range)
[5]:
2021-12 2022-01 2022-02
Title
Jupyter Tutorial 24061.0 25579.0 13104.0
PyViz Tutorial 0.0 0.0 0.0
Python Basics 332.0 50.0 368.0

Ihr werdet feststellen, dass einige Methoden wie describe ebenfalls funktionieren, auch wenn es sich dabei streng genommen nicht um Aggregationen handelt:

[6]:
grouped.describe()
[6]:
2021-12 2022-01 2022-02
count mean std min 25% 50% 75% max count mean ... 75% max count mean std min 25% 50% 75% max
Title
Jupyter Tutorial 2.0 18103.5 17013.696262 6073.0 12088.25 18103.5 24118.75 30134.0 2.0 20505.5 ... 26900.25 33295.0 2.0 13099.0 9265.927261 6547.0 9823.0 13099.0 16375.0 19651.0
PyViz Tutorial 1.0 4873.0 NaN 4873.0 4873.00 4873.0 4873.00 4873.0 1.0 3930.0 ... 3930.00 3930.0 1.0 2573.0 NaN 2573.0 2573.0 2573.0 2573.0 2573.0
Python Basics 2.0 261.0 234.759451 95.0 178.00 261.0 344.00 427.0 2.0 251.0 ... 263.50 276.0 2.0 341.0 260.215295 157.0 249.0 341.0 433.0 525.0

3 rows × 24 columns

Hinweis:

Benutzerdefinierte Aggregationsfunktionen sind im Allgemeinen viel langsamer als die optimierten Funktionen in der obigen Tabelle. Dies liegt daran, dass bei der Erstellung der Zwischendatensätze für die Gruppe ein gewisser Mehraufwand entsteht (Funktionsaufrufe, Umordnung von Daten).

Spaltenweise zusätzliche Funktionen

Wie wir bereits gesehen haben, ist das Aggregieren einer Series oder aller Spalten eines DataFrame eine Frage der Verwendung von aggregate (oder agg) mit der gewünschten Funktion oder des Aufrufs einer Methode wie mean oder std. Es kommt jedoch häufiger vor, dass gleichzeitig mit einer anderen Funktion je nach Spalte oder mit mehreren Funktionen aggregiert werden soll.

[7]:
grouped.agg("mean")
[7]:
2021-12 2022-01 2022-02
Title
Jupyter Tutorial 18103.5 20505.5 13099.0
PyViz Tutorial 4873.0 3930.0 2573.0
Python Basics 261.0 251.0 341.0

Wenn ihr stattdessen eine Liste von Funktionen oder Funktionsnamen übergebt, erhaltet ihr einen DataFrame mit Spaltennamen aus den Funktionen zurück:

[8]:
grouped.agg(["mean", "std", range])
[8]:
2021-12 2022-01 2022-02
mean std range mean std range mean std range
Title
Jupyter Tutorial 18103.5 17013.696262 24061.0 20505.5 18087.084356 25579.0 13099.0 9265.927261 13104.0
PyViz Tutorial 4873.0 NaN 0.0 3930.0 NaN 0.0 2573.0 NaN 0.0
Python Basics 261.0 234.759451 332.0 251.0 35.355339 50.0 341.0 260.215295 368.0

Hier haben wir agg eine Liste von Aggregationsfunktionen übergeben, die unabhängig voneinander für die Datengruppen ausgewertet werden sollen.

Ihr braucht die Namen, die GroupBy den Spalten gibt, nicht zu akzeptieren; insbesondere haben Lambda-Funktionen den Namen <lambda>, was ihre Identifizierung erschwert. Wenn ihr eine Liste von Tupels übergebt, wird das erste Element jedes Tuples als Spaltenname im DataFrame verwendet:

[9]:
grouped.agg(
    [("Mittelwert", "mean"), ("Standardabweichung", "std"), ("Bereich", range)]
)
[9]:
2021-12 2022-01 2022-02
Mittelwert Standardabweichung Bereich Mittelwert Standardabweichung Bereich Mittelwert Standardabweichung Bereich
Title
Jupyter Tutorial 18103.5 17013.696262 24061.0 20505.5 18087.084356 25579.0 13099.0 9265.927261 13104.0
PyViz Tutorial 4873.0 NaN 0.0 3930.0 NaN 0.0 2573.0 NaN 0.0
Python Basics 261.0 234.759451 332.0 251.0 35.355339 50.0 341.0 260.215295 368.0

Bei einem DataFrame habt ihr die Möglichkeit, eine Liste von Funktionen anzugeben, die auf alle Spalten oder auf verschiedene Funktionen pro Spalte angewendet werden. Nehmen wir an, wir möchten die gleichen drei Statistiken für die Spalten berechnen:

[10]:
stats = ["count", "mean", "max"]
evaluations = grouped.agg(stats)

evaluations
[10]:
2021-12 2022-01 2022-02
count mean max count mean max count mean max
Title
Jupyter Tutorial 2 18103.5 30134.0 2 20505.5 33295.0 2 13099.0 19651.0
PyViz Tutorial 1 4873.0 4873.0 1 3930.0 3930.0 1 2573.0 2573.0
Python Basics 2 261.0 427.0 2 251.0 276.0 2 341.0 525.0

Wie ihr sehen könnt, hat der resultierende DataFrame hierarchische Spalten, genauso wie ihr sie bekommen würdet, wenn ihr jede Spalte separat aggregieren und pandas.concat verwenden würdet, um die Ergebnisse zusammenzufügen, indem ihr die Spaltennamen als Schlüsselargument verwendet:

[11]:
evaluations["2021-12"]
[11]:
count mean max
Title
Jupyter Tutorial 2 18103.5 30134.0
PyViz Tutorial 1 4873.0 4873.0
Python Basics 2 261.0 427.0

Wie zuvor kann eine Liste von Tupeln mit benutzerdefinierten Namen übergeben werden:

[12]:
tuples = [("Mean", "mean"), ("Variance", "var")]

grouped[["2021-12", "2022-01"]].agg(tuples)
[12]:
2021-12 2022-01
Mean Variance Mean Variance
Title
Jupyter Tutorial 18103.5 289465860.5 20505.5 327142620.5
PyViz Tutorial 4873.0 NaN 3930.0 NaN
Python Basics 261.0 55112.0 251.0 1250.0

Nehmen wir nun an, dass potenziell verschiedene Funktionen auf eine oder mehrere der Spalten angewendet werden sollen, dann übergeben wir dazu ein dict an agg, das eine Zuordnung von Spaltennamen zu einer der Funktionsspezifikationen enthält:

[13]:
grouped.agg({"2021-12": "mean", "2022-01": "var"})
[13]:
2021-12 2022-01
Title
Jupyter Tutorial 18103.5 327142620.5
PyViz Tutorial 4873.0 NaN
Python Basics 261.0 1250.0
[14]:
grouped.agg({"2021-12": ["min", "max", "mean", "std"], "2022-01": "sum"})
[14]:
2021-12 2022-01
min max mean std sum
Title
Jupyter Tutorial 6073.0 30134.0 18103.5 17013.696262 41011.0
PyViz Tutorial 4873.0 4873.0 4873.0 NaN 3930.0
Python Basics 95.0 427.0 261.0 234.759451 502.0

Aggregierte Daten ohne Zeilenindizes zurückgeben

In allen bisherigen Beispielen werden die aggregierten Daten mit einem Index zurückgegeben. Da dies nicht immer erwünscht ist, könnt ihr dieses Verhalten in den meisten Fällen deaktivieren, indem ihr as_index=False an groupby übergebt:

[15]:
grouped.agg([range]).mean()
[15]:
2021-12  range    8131.000000
2022-01  range    8543.000000
2022-02  range    4490.666667
dtype: float64

Durch die Verwendung der Methode as_index=False werden einige unnötige Berechnungen vermieden. Natürlich ist es jederzeit möglich, das Ergebnis wieder mit Index zu erhalten, indem reset_index für das Ergebnis aufgerufen wird.