Pandas: Groupby Beispiele

Person auf einem Geldhaufen

In diesem Teil unseres Python- und Pandas-Kurses möchten wir ein ausführliches Beispiel für die Verwendung von groupby geben. Wir werden eine Datendatei [donations.txt] (https://python-course.eu/data/donations.txt) verwenden.

Die ersten Zeilen dieser Datendatei sehen wie folgt aus:

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
Heinz-Joachim,Wagner,Stuttgart,Engineer,109300,1592

Die Daten dieser Datei wurden mit Hilfe des Python-Faker-Moduls künstlich erzeugt, eine mögliche Übereinstimmung mit real existierenden Personen ist also rein zufällig und nicht beabsichtigt. Jede Zeile enthält den Vor- und Nachnamen einer fiktiven Person sowie den Wohnort, die berufliche Position (einer der fünf Berufe Politiker, Student, Ingenieur, Musiker und Manager), das frühe Einkommen und die Summe der Spenden pro Jahr.

Zunächst lesen wir die Datendatei ein, die trotz der Endung 'txt' natürlch eine csv-Datei ist:

In [2]:
import pandas as pd

fname = 'data1/donations.txt'
data = pd.read_csv(fname, usecols=[2, 3, 4, 5])
data[:10]
Out[2]:
city job income donations
0 Karlsruhe Politician 244400 2512
1 Freiburg Student 16800 336
2 Hamburg Engineer 116900 1479
3 Köln Musician 57700 1142
4 Stuttgart Engineer 109300 1592
5 Hamburg Student 12500 250
6 Freiburg Engineer 128700 1984
7 Stuttgart Politician 161300 822
8 Freiburg Engineer 129000 2159
9 Stuttgart Musician 108800 1516

Schauen wir uns zunächst an, wie viel jede Berufsgruppe insgesamt verdient und gespendet hat. Dazu können wir die Pandas-Funktion groupby zusammen mit sum verwenden:

In [3]:
data_sum = data.groupby(['job']).sum()
data_sum
Out[3]:
income donations
job
Engineer 2067200 25564
Manager 12862600 87475
Musician 1448700 24376
Politician 4118300 30758
Student 372900 7458

Durch die Verwendung von "sort_values" für die Spalte "donations" können wir diesen DataFrame sortieren:

In [26]:
data_sum.sort_values(by='donations')
Out[26]:
income donations
job
Student 372900 7458
Musician 1448700 24376
Engineer 2067200 25564
Politician 4118300 30758
Manager 12862600 87475

Schauen wir uns die folgenden drei Personen aus unserem Datei an:

Janett,Schwital,Karlsruhe,Politician,244400,2512
Daniele,Segebahn,Freiburg,Student,16800,336
Kirstin,Klapp,Hamburg,Engineer,116900,1479

Wer ist der großzügigste von ihnen? Die Politikerin Janett Schwital? 2512 Euro sind doch fast achtmal so viel wie das, was die Studentin Daniele Segebahn gespendet hat, und fast zweimal so viel wie die Ingenieurin Kirstin Klapp gespendet hat.

Die meisten Menschen würden denken, dass wir die Spenden in Relation zu ihren Einkommen sehen müssen. Dann erhalten wir folgendes:

Der Politikerin hat etwa 1 % ihres Einkommens gespendet, die Ingenieurin etwa 1,3 % und die Studentin 2 %. Wir können also sagen, dass Daniele im Verhältnis zu ihrem Einkommen am großzügigsten ist!

Machen wir das Gleiche mit dem vorherigen DataFrame:

In [4]:
data_sum['relative'] = data_sum.donations * 100 / data_sum.income
data_sum.sort_values(by='relative')
Out[4]:
income donations relative
job
Manager 12862600 87475 0.680072
Politician 4118300 30758 0.746862
Engineer 2067200 25564 1.236649
Musician 1448700 24376 1.682612
Student 372900 7458 2.000000

Wir sehen also, dass die Manager und Managerinnen und die Politikerinnen und Politiker sehr knauserig sind und die Studierenden extrem großzügig sind. Können wir dies verallgemeinern, indem wir sagen: Je weniger Leute haben, desto großzügiger sind sie. Jedoch darf man nicht vergessen, dass der ganze Datensatz gefälscht ist!

Wir wollen nun die Anzahl der Personen in jeder Berufsgruppe für jede Stadt zählen. Die Verwendung von "count" und "groupby" in der folgenden Weise ist hilfreich, aber nicht schön. Die Zahlen in den Spalten "Einkommen" und "Spenden" zeigen die gewünschten Zahlen, aber die Spaltennamen sind irreführend und wir brauchen die Ergebnisse nicht doppelt:

In [28]:
x = data.groupby(['city', 'job']).count()
x
Out[28]:
income donations
city job
Berlin Engineer 3 3
Manager 3 3
Musician 2 2
Politician 1 1
Student 2 2
Freiburg Engineer 3 3
Manager 5 5
Musician 3 3
Politician 3 3
Student 5 5
Hamburg Engineer 4 4
Musician 4 4
Politician 1 1
Student 2 2
Karlsruhe Engineer 1 1
Manager 3 3
Politician 7 7
Student 2 2
Konstanz Engineer 2 2
Manager 5 5
Musician 3 3
Politician 4 4
Student 3 3
Köln Engineer 1 1
Manager 3 3
Musician 2 2
Politician 1 1
Student 3 3
Stuttgart Engineer 4 4
Manager 4 4
Musician 4 4
Politician 3 3
Student 4 4

Wir könnten das Folgende tun, um ein gutes Ergebnis zu erzielen, aber der ganze Weg ist ungeschickt:

In [5]:
people_job_city = data.groupby(['city', 'job']).count()
people_job_city.drop(columns=['income'], inplace=True)
people_job_city.rename(columns={'donations': 'number of people'})
Out[5]:
number of people
city job
Berlin Engineer 3
Manager 3
Musician 2
Politician 1
Student 2
Freiburg Engineer 3
Manager 5
Musician 3
Politician 3
Student 5
Hamburg Engineer 4
Musician 4
Politician 1
Student 2
Karlsruhe Engineer 1
Manager 3
Politician 7
Student 2
Konstanz Engineer 2
Manager 5
Musician 3
Politician 4
Student 3
Köln Engineer 1
Manager 3
Musician 2
Politician 1
Student 3
Stuttgart Engineer 4
Manager 4
Musician 4
Politician 3
Student 4

Der bessere Weg besteht darin, groupby von Pandas zu benutzen. In der folgenden Lösung gruppiert die groupby-Methode den DataFrame nach den angegebenen Spalten (in diesem Fall "city" und "job") und erstellt für jede Gruppe einen neuen DataFrame. Die size-Methode gibt die Anzahl jeder Gruppe zurück, was zu einem neuen DataFrame mit den Anzahlen für jede Kombination von Stadt und Stelle führt. Beachten Sie, dass das Ergebnis ein Series-Objekt ist:

In [6]:
result = data[['city', 'job']].groupby(['city', 'job']).size()
result
Out[6]:
city       job       
Berlin     Engineer      3
           Manager       3
           Musician      2
           Politician    1
           Student       2
Freiburg   Engineer      3
           Manager       5
           Musician      3
           Politician    3
           Student       5
Hamburg    Engineer      4
           Musician      4
           Politician    1
           Student       2
Karlsruhe  Engineer      1
           Manager       3
           Politician    7
           Student       2
Konstanz   Engineer      2
           Manager       5
           Musician      3
           Politician    4
           Student       3
Köln       Engineer      1
           Manager       3
           Musician      2
           Politician    1
           Student       3
Stuttgart  Engineer      4
           Manager       4
           Musician      4
           Politician    3
           Student       4
dtype: int64

Interessanter als das vorherige Ergebnis ist die Anzahl der Spenden und insbesondere die Spenden im Verhältnis zum Einkommen für jeden Job und jede Stadt:

In [8]:
city_job_data = data.groupby(['city', 'job']).sum()
city_job_data[:12]  # the first 12 lines
Out[8]:
income donations
city job
Berlin Engineer 334300 3732
Manager 1916000 16290
Musician 135500 2644
Politician 246700 1103
Student 31200 624
Freiburg Engineer 358400 5431
Manager 2423700 17811
Musician 273800 4640
Politician 489300 4352
Student 87500 1750
Hamburg Engineer 448400 5390
Musician 358800 5359

Auch hier ziehen wir es vor, die Spenden im Verhältnis zu den Einnahmen zu sehen:

In [9]:
city_job_data['rel_donations'] = (city_job_data['donations'] * 100 / city_job_data['income']).round(2)
city_job_data.drop(['income', 'donations'], axis=1, inplace=True)
city_job_data[:12]
Out[9]:
rel_donations
city job
Berlin Engineer 1.12
Manager 0.85
Musician 1.95
Politician 0.45
Student 2.00
Freiburg Engineer 1.52
Manager 0.73
Musician 1.69
Politician 0.89
Student 2.00
Hamburg Engineer 1.20
Musician 1.49

Vielleicht wollen Sie jetzt wissen, in welcher Stadt die großzügigsten Menschen leben?

In [10]:
cities_donations = data.groupby(['city']).sum()
cities_donations['relative'] = cities_donations['donations'] * 100 / cities_donations['income']
cities_donations.sort_values(by='relative')
Out[10]:
income donations relative
city
Karlsruhe 3457600 22265 0.643944
Konstanz 4165500 29386 0.705462
Köln 2369500 21158 0.892931
Stuttgart 3522900 31891 0.905249
Berlin 2663700 24393 0.915756
Freiburg 3632700 33984 0.935503
Hamburg 1057800 12554 1.186803