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 |
---|---|
|
Gibt |
|
Anzahl der Nicht-NA-Werte |
|
Kumuliertes Minimum und Maximum der Nicht-NA-Werte |
|
Kumulative Summe der Nicht-NA-Werte |
|
Kumulatives Produkt von Nicht-NA-Werten |
|
Erste und letzte Nicht-NA-Werte |
|
Mittelwert der Nicht-NA-Werte |
|
Arithmetischer Median der Nicht-NA-Werte |
|
Minimum und Maximum der Nicht-NA-Werte |
|
Abrufen des n-ten größten Wertes |
|
Berechnung von vier Open-high-low-close-Statistiken für zeitreihenähnliche Daten |
|
Produkt der Nicht-NA-Werte |
|
Berechnet das Stichprobenquantil |
|
Ordinale Ränge von Nicht-NA-Werten, wie beim Aufruf von Series.rank |
|
Summe der Nicht-NA-Werte |
|
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.