Pandas: groupby¶
In diesem Kapitel unseres Pandas- und Python-Kurses geht es um eine äußerst wichtige Funktionalität, nämlich groupby
. Eigentlich ist
dieses Thema nicht wirklich kompliziert, aber nicht immer auf den ersten Blick ersichtlich und es wird manchmal als schwierig empfunden. Völlig zu Unrecht, wie wir sehen werden. Es ist auch sehr wichtig, sich mit groupby
vertraut zu machen, weil man damit wichtige Probleme lösen kann, die ohne groupby
nicht oder nur sehr schwer lösbar wären. Die Pandas groupby
-Operation geschieht in mehreren Schriten
- Aufsplitten des Objekts,
- Anwendung einer Funktion und
- Kombination der Ergebnisse.
Wir können ein DataFrame-Objekt nach verschiedenen Kriterien und zeilen- und spaltenweise, d.h. mit Hilfe des Parameters axis
, in Gruppen aufteilen.
Durch die Anwendung einer Funktion können wir die Daten
- filtern,
- umwandeln
- aggregieren.
groupby
kann auf Pandas Series-Objekte und DataFrame-Objekte angewendet werden! Wie das funktioniert, werden wir in diesem Tutorial anhand vieler kleiner praktischer Beispiele verstehen lernen.
groupby mit Series-Objekten¶
Wir erzeugen mit dem folgenden Python-Programm ein Series-Objekt mit einem Index der Größe nvalues
. Der Index wird nicht eindeutig sein, da die Zeichenketten für den Index aus der Liste fruits
entnommen werden, die weniger Elemente hat als `nvalues``:
import pandas as pd
import numpy as np
import random
nvalues = 30
# wir erstellen Zufallswerte für unser Series-Objekt:
values = np.random.randint(1, 20, (nvalues,))
fruits = ["bananas", "oranges", "apples",
"clementines", "cherries", "pears"]
fruits_index = np.random.choice(fruits, (nvalues,))
s = pd.Series(values, index=fruits_index)
print(s[:10])
grouped = s.groupby(s.index)
grouped
Wir können sehen, dass wir ein SeriesGroupBy
-Objekt erhalten, wenn wir groupby
auf den Index unseres Serienobjekts s
anwenden. Das Ergebnis dieser Operation grouped
ist iterierbar. In jedem Schritt erhalten wir ein Tupel-Objekt zurück, das aus einem Index-Label und einem Series-Objekt besteht. Das Series-Objekt s
ist auf dieses Label reduziert.
grouped = s.groupby(s.index)
for fruit, s_obj in grouped:
print(f"===== {fruit} =====")
print(s_obj)
Mit folgendem Python-Code hätten wir das gleiche Ergebnis - abgesehen von der Reihenfolge - auch ohne die Verwendung von groupby
erzielen können.
for fruit in set(s.index):
print(f"===== {fruit} =====")
print(s[fruit])
groupby mit DataFrames¶
Wir werden mit einem sehr einfachen DataFrame beginnen. Der DataFrame hat zwei Spalten, von denen die eine den Namen Name
enthält und die andere Coffee
die Anzahl der Tassen Kaffee, die die Person getrunken hat, in ganzen Zahlen.
import pandas as pd
beverages = pd.DataFrame({'Name': ['Robert', 'Melinda', 'Brenda',
'Samantha', 'Melinda', 'Robert',
'Melinda', 'Brenda', 'Samantha'],
'Coffee': [3, 0, 2, 2, 0, 2, 0, 1, 3],
'Tea': [0, 4, 2, 0, 3, 0, 3, 2, 0]})
beverages
Es ist einfach, und wir haben bereits in den vorherigen Kapiteln unseres Tutorials gesehen, wie man die Gesamtzahl der Kaffeetassen berechnet. Die Aufgabe besteht darin, eine Spalte eines DatFrame zu summieren, z.B. die Spalte Coffee
:
beverages['Coffee'].sum()
Berechnen wir nun die Gesamtzahl der Kaffees und Tees:
beverages[['Coffee', 'Tea']].sum()
groupby
war für die bisherigen Aufgaben nicht erforderlich.
Werfen wir noch einmal einen Blick auf unseren DataFrame. Wir können sehen, dass einige der Namen mehrfach vorkommen. Es wäre also sehr interessant sein, zu sehen, wie viele Tassen Kaffee und Tee jede Person insgesamt getrunken hat. Das heißt, wir wenden 'groupby' auf die Spalte 'Name' an. Dadurch teilen wir den DatFrame auf. Dann wenden wir 'sum' auf die Ergebnisse von 'groupby' an:
res = beverages.groupby(['Name']).sum()
print(res)
Wie wir sehen können, sind die Namen jetzt der Index des resultierenden DataFrame:
print(res.index)
Wir können auch die durchschnittliche Anzahl der Kaffee- und Teetassen berechnen, die die Personen getrunken haben:
beverages.groupby(['Name']).mean()
Weiteres groupby-Beispiel¶
Der folgende Python-Code wird verwendet, um die Daten zu erzeugen, die wir in unserem nächsten groupby
-Beispiel verwenden werden. Es ist nicht notwendig, den folgenden Python-Code für den nachfolgenden Inhalt zu verstehen.
Das Modul faker
muss installiert sein. Im Falle einer Anaconda-Installation kann dies durch die Ausführung eines der folgenden Befehle in einer Shell geschehen:
conda install -c conda-forge faker
conda install -c conda-forge/label/gcc7 faker
conda install -c conda-forge/label/cf201901 faker
conda install -c conda-forge/label/cf202003 faker
from faker import Faker
import numpy as np
from itertools import chain
fake = Faker('de_DE')
number_of_names = 10
names = []
for _ in range(number_of_names):
names.append(fake.first_name())
data = {}
workweek = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
weekend = ("Saturday", "Sunday")
for day in chain(workweek, weekend):
data[day] = np.random.randint(0, 10, (number_of_names,))
data_df = pd.DataFrame(data, index=names)
data_df
print(names)
names = ('Ortwin', 'Mara', 'Siegrun', 'Sylvester', 'Metin', 'Adeline', 'Utz', 'Susan', 'Gisbert', 'Senol')
data = {'Monday': np.array([0, 9, 2, 3, 7, 3, 9, 2, 4, 9]),
'Tuesday': np.array([2, 6, 3, 3, 5, 5, 7, 7, 1, 0]),
'Wednesday': np.array([6, 1, 1, 9, 4, 0, 8, 6, 8, 8]),
'Thursday': np.array([1, 8, 6, 9, 9, 4, 1, 7, 3, 2]),
'Friday': np.array([3, 5, 6, 6, 5, 2, 2, 4, 6, 5]),
'Saturday': np.array([8, 4, 8, 2, 3, 9, 3, 4, 9, 7]),
'Sunday': np.array([0, 8, 7, 8, 9, 7, 2, 0, 5, 2])}
data_df = pd.DataFrame(data, index=names)
data_df
Mit diesem DataFrame wollen wir demonstrieren, wie man Spalten mittels einer Funktion kombinieren kann. Es geht darum festzustellen wieviele Stunden jede Person während der Arbeits- und während des Wochenendes gearbeitet hatte:
def is_weekend(day):
if day in {'Saturday', 'Sunday'}:
return "Weekend"
else:
return "Workday"
for res_func, df in data_df.groupby(by=is_weekend, axis=1):
print(df)
data_df.groupby(by=is_weekend, axis=1).sum()
import pandas as pd
d = {"products": ["Oppilume", "Dreaker", "Lotadilo",
"Crosteron", "Wazzasoft", "Oppilume",
"Dreaker", "Lotadilo", "Wazzasoft"],
"colours": ["blue", "blue", "blue",
"green", "blue", "green",
"green", "green", "red"],
"customer_price": [2345.89, 2390.50, 1820.00,
3100.00, 1784.50, 2545.89,
2590.50, 2220.00, 2084.50],
"non_customer_price": [2445.89, 2495.50, 1980.00,
3400.00, 1921.00, 2645.89,
2655.50, 2140.00, 2190.00]}
product_prices = pd.DataFrame(d)
product_prices
Aufgabe 2¶
Berechnen Sie die Summe der Preise nach den Farben.
Aufgabe 3¶
Lesen Sie die Datei project_times.txt ein. Die Zeilen dieser Datei enthalten kommagetrennt das Datum, den Namen des Programmierers, den Namen des Projekts und die Zeit, die die Personen für das Projekt aufgewendet haben.
Berechnen Sie die Zeit, die für alle Projekte pro Tag aufgewendet wurde
Aufgabe 4¶
Erstellen Sie einen DateFrame mit der Gesamtzeit, die alle Programmiererinnen und Programmierer pro Tag für ein Projekt aufwenden
Aufgabe 5¶
Berechnen Sie die Gesamtzeit, die für die Projekte über den ganzen Monat hinweg aufgewendet wurde.
Aufgabe 6¶
Berechnen Sie die monatlichen Zeiten der einzelnen Programmierer und Programmiererinnen unabhängig von den Projekten
Aufgabe 7¶
Ordnen Sie den DataFrame mit einem MultiIndex, der aus dem Datum und den Projektnamen besteht, neu an. Für jede Person soll es eine Spalte mit den aufgewendeten Zeiten geben. Also so:
time
programmer Antonie Elise Fatima Hella Mariola
date project
2020-01-01 BIRDY NaN NaN NaN 1.50 1.75
NSTAT NaN NaN 0.25 NaN 1.25
XTOR NaN NaN NaN 1.00 3.50
2020-01-02 BIRDY NaN NaN NaN 1.75 2.00
NSTAT 0.5 NaN NaN NaN 1.75
Zusatzaufgabe: ersetzte NaN durch 0.
Aufgabe 8:¶
Die Datei donations.txt enthält die folgenden Daten, also "Vorname" (firstname), "Nachname" (surname), "Stadt" (city), "Job", "Einkommen" (income), "Spenden" (donations):
firstname,surname,city,job,income,donations
Janett,Schwital,Karlsruhe,Politician,244400,2512
Daniele,Segebahn,Freiburg,Student,16800,336
Kirstin,Klapp,Hamburg,Engineer,116900,1479
Oswald,Segebahn,Köln,Musician,57700,1142
Gruppiere die Daten anhand des Jobs der Personen.
x = product_prices.groupby("products").mean()
x
Lösung für Aufgabe 2¶
x = product_prices.groupby("colours").sum()
x
Lösung für Aufgabe 3¶
import pandas as pd
df = pd.read_csv("data1/project_times.txt", index_col=0)
df
times_per_day = df.groupby(df.index).sum()
print(times_per_day[:10])
Lösung für Aufgabe 4¶
times_per_day_project = df.groupby([df.index, 'project']).sum()
print(times_per_day_project[:10])
Lösung für Aufgabe 5¶
df.groupby(['project']).sum()
Lösung für Aufgabe 6¶
df.groupby(['programmer']).sum()
Lösung für Aufgabe 7¶
x = df.groupby([df.index, 'project', 'programmer']).sum()
x = x.unstack()
x
x = x.fillna(0)
print(x[:10])
Lösung für Aufgabe 8:¶
import pandas as pd
data = pd.read_csv('data1/donations.txt')
data_sum = data.groupby(['job']).sum()
data_sum.sort_values(by='donations')
data_sum['relative'] = data_sum.donations * 100 / data_sum.income
data_sum.sort_values(by='relative')