Eintrag

Zusammenfassung des Kaggle-„Pandas“-Kurses (2) – Lektion 4–6

Praxisleitfaden zu Pandas für Datenbereinigung und -aufbereitung: Gruppieren & Sortieren, Datentypen & Missing Values, Umbenennen & Kombinieren. Zusammenfassung und Ergänzungen zu Kaggle‑Lektion 4–6.

Zusammenfassung des Kaggle-„Pandas“-Kurses (2) – Lektion 4–6

Hier fasse ich das zusammen, was ich im Pandas-Kurs von Kaggle gelernt habe.
Da der Umfang recht groß ist, habe ich es in zwei Teile aufgeteilt.

Zertifikat über den Abschluss

Lektion 4. Gruppieren und Sortieren

Oft muss man Daten in Gruppen einteilen und gruppenweise Operationen ausführen oder nach bestimmten Kriterien sortieren.

Gruppenauswertung

Mit der Methode groupby() lassen sich Daten mit gleichem Wert in einer bestimmten Spalte zu Gruppen zusammenfassen und anschließend gruppenweise überblicken oder transformieren.

Zuvor hatten wir bereits die Methode value_counts() kennengelernt; dasselbe Verhalten kann man mit groupby() wie folgt nachbilden.

1
reviews.groupby('taster_name').size()
  1. Den DataFrame reviews nach gleichen Werten in der Spalte taster_name gruppieren
  2. Die Größe jeder Gruppe (Anzahl der Zeilen) als Series zurückgeben

Oder:

1
reviews.groupby('taster_name').taster_name.count()
  1. Den DataFrame reviews nach gleichen Werten in der Spalte taster_name gruppieren
  2. Pro Gruppe die Spalte taster_name auswählen
  3. Die Anzahl der Nicht-Null-Werte als Series zurückgeben

Mit anderen Worten: value_counts() ist im Grunde nur eine Abkürzung für obige Operationen. Neben count() lassen sich so beliebige Übersichts-/Aggregatfunktionen einsetzen. Möchte man z. B. den Minimalpreis je Bewertungspunkt ermitteln, geht das so:

1
reviews.groupby('points').price.min()
1
2
3
4
5
6
7
points
80      5.0
81      5.0
       ... 
99     44.0
100    80.0
Name: price, Length: 21, dtype: float64
  1. Den DataFrame reviews nach gleichen Werten in der Spalte points gruppieren
  2. Pro Gruppe die Spalte price auswählen
  3. Den Minimalwert je Gruppe als Series zurückgeben

Man kann auch nach mehreren Spalten gruppieren. Um je Land und Provinz den Wein mit der höchsten Punktzahl auszuwählen:

1
reviews.groupby(['country', 'province']).apply(lambda df: df.loc[df.points.idxmax()])

Eine weitere nützliche Methode von DataFrameGroupBy-Objekten ist agg(). Damit kann man nach dem Gruppieren mehrere Funktionen pro Gruppe gleichzeitig ausführen.

Als Argumente sind zulässig:

  • eine Funktion
  • ein Funktionsname als String
  • eine Liste aus Funktionen oder Funktionsnamen
  • ein Dict, dessen Keys Achsenlabels sind und dessen Values je Achse eine Funktion oder Funktionsliste angeben

Die Funktionen müssen

Dies stand im ursprünglichen Kaggle-Kurs nicht; es wurde anhand der offiziellen Pandas‑Doku ergänzt.

Beispiel: einfache Preisstatistiken je Land berechnen.

1
reviews.groupby(['country']).price.agg([len, min, max])

len ist die Python‑Builtin‑Funktion len(). Im obigen Beispiel liefert sie die inklusive fehlender Werte gezählte Anzahl der Preiswerte (price) pro Gruppe (country). Da sie mit DataFrames bzw. Series umgehen kann, ist dieser Einsatz möglich.

Die Pandas‑Methode count() zählt hingegen nur nicht fehlende (gültige) Werte und unterscheidet sich somit im Verhalten.

Dies stand im ursprünglichen Kaggle‑Kurs nicht; es wurde anhand der Python‑ und Pandas‑Doku ergänzt.

MultiIndex

Bei gruppierungsbasierten Transformationen erhält man mitunter DataFrames mit mehrstufigem Index statt eines einfachen Index.

1
2
countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
countries_reviewed
len
Countryprovince
ArgentinaMendoza Province3264
Other536
.........
UruguaySan Jose3
Uruguay24
1
2
mi = countries_reviewed.index
type(mi)
1
pandas.core.indexes.multi.MultiIndex

Ein MultiIndex bringt einige Methoden mit, die es für einfache Indizes nicht gibt. Ausführliche Beispiele und Richtlinien finden sich im Abschnitt „MultiIndex / advanced indexing“ des Pandas User Guide.

Am häufigsten wird man jedoch den MultiIndex wieder zu einem normalen Index zurückführen wollen – mit reset_index():

1
countries_reviewed.reset_index()
 countryprovincelen
0ArgentinaMendoza Province3264
1ArgentinaOther536
423UruguaySan Jose3
424UruguayUruguay24

Sortieren

Schaut man sich unser Beispiel countries_reviewed an, fällt auf: Die gruppierten Ergebnisse werden in der Reihenfolge der Indexwerte zurückgegeben. Die Zeilenreihenfolge des groupby-Ergebnisses wird also nicht vom Inhalt, sondern vom Index bestimmt.

Bei Bedarf kann man selbst sortieren – komfortabel mit sort_values(). Zum Beispiel lässt sich nach der enthaltenen Anzahl („len“) aufsteigend sortieren:

1
2
countries_reviewed = countries_reviewed.reset_index()
countries_reviewed.sort_values(by='len')
 countryprovincelen
179GreeceMuscat of Kefallonian1
192GreeceSterea Ellada1
415USWashington8639
392USCalifornia36247

Standardmäßig sortiert sort_values() aufsteigend; mit einer Option geht auch absteigend:

1
countries_reviewed.sort_values(by='len', ascending=False)
 countryprovincelen
392USCalifornia36247
415USWashington8639
63ChileCoelemu1
149GreeceBeotia1

Zum Sortieren nach dem Index verwendet man sort_index(). Es besitzt dieselben Argumente und dieselbe Standardreihenfolge (absteigend) wie sort_values(); die Anwendung ist identisch.

1
countries_reviewed.sort_index()
 countryprovincelen
0ArgentinaMendoza Province3264
1ArgentinaOther536
423UruguaySan Jose3
424UruguayUruguay24

Schließlich kann man auch nach mehreren Spalten gleichzeitig sortieren:

1
countries_reviewed.sort_values(by=['country', 'len'])

Lektion 5. Datentypen und fehlende Werte

Reale Daten sind selten perfekt bereinigt. Meistens muss man Datentypen konvertieren und mit fehlenden Werten umgehen. In vielen Projekten ist genau dieser Schritt die größte Hürde.

Datentypen

Der Datentyp einer DataFrame‑Spalte bzw. Series heißt in Pandas der dtype. Über das Attribut dtype ermittelt man den Typ einer Spalte. Beispiel: der dtype der Spalte price im DataFrame reviews.

1
reviews.price.dtype
1
dtype('float64')

Mit dem Attribut dtypes bekommt man alle Spaltentypen auf einmal:

1
reviews.dtypes
1
2
3
4
5
6
country        object
description    object
                ...  
variety        object
winery         object
Length: 13, dtype: object

Der Datentyp gibt an, wie Pandas die Werte intern speichert: float64 für 64‑Bit‑Gleitkommazahlen, int64 für 64‑Bit‑Integer usw.

Eine Besonderheit: Spalten, die ausschließlich Strings enthalten, besitzen keinen eigenen String‑Typ, sondern sind vom Typ object.

Mit astype() lassen sich Spalten in andere Typen konvertieren. So kann man etwa die int64‑Spalte points in float64 konvertieren:

1
reviews.points.astype('float64')
1
2
3
4
5
6
0         87.0
1         87.0
          ... 
129969    90.0
129970    90.0
Name: points, Length: 129971, dtype: float64

Auch der Index eines DataFrames oder einer Series hat einen eigenen Datentyp:

1
reviews.index.dtype
1
dtype('int64')

Daneben unterstützt Pandas externe Typen wie Kategoriedaten und Zeitreihen.

Fehlende Werte

Leere Einträge (ohne Wert) werden als NaN („Not a Number“) markiert. Aus technischen Gründen ist NaN stets vom Typ float64.

Pandas bietet spezielle Funktionen für Missing Values. Ähnlich wie bereits kurz gesehen gibt es neben Methoden auch unabhängige Funktionen: pd.isna und pd.notna. Sie liefern, je nach Eingabe, einen booleschen Einzelwert oder ein boolesches Array und lassen sich so einsetzen:

1
reviews[pd.isna(reviews.country)]

Oft prüft man zunächst, ob Missing Values vorhanden sind, und füllt sie anschließend sinnvoll. Eine Strategie ist fillna(): fehlende Werte durch einen geeigneten Ersatzwert auffüllen. Beispiel: alle NaN in reviews.region_2 durch "Unknown" ersetzen.

1
reviews.region_2.fillna("Unknown")

Alternativ kann man mit dem jeweils nächstliegenden gültigen Wert aus der Vergangenheit/Zukunft auffüllen (Forward/Backward Fill): ffill() bzw. bfill().

Früher konnte man fillna() auch mit dem Parameter method und den Strings 'ffill'/'bfill' verwenden; seit Pandas 2.1.0 ist das deprecatet. Stattdessen sollten situativ ffill() bzw. bfill() genutzt werden.

Mitunter möchte man Werte – auch wenn sie nicht fehlend sind – pauschal durch andere ersetzen. Im Originalkurs wird z. B. ein geänderter Twitter‑Handle einer/s Rezensent:in erwähnt. Ein anderes Beispiel (hierzulande vielleicht greifbarer):

Angenommen, in Südkorea würde der nördliche Teil der Provinz Gyeonggi als neue Verwaltungseinheit abgetrennt und als Gyeonggibuk-do eingeführt, und ein Datensatz verwendet schon diesen Namen. Nun kommt jemand auf die „glorreiche“ Idee, Gyeonggibuk-do in Pyeonghwanuri Special Self-Governing Province umzubenennen – und setzt es tatsächlich durch. Dann müsste man im bestehenden Datensatz "Gyeonggibuk-do" durch "Pyeonghwanuri State" bzw. "Pyeonghwanuri Special Self-Governing Province" ersetzen. In Pandas bietet sich dafür die Methode replace() an.

1
rok_2030_census.province.replace("Gyeonggibuk-do", "Pyeonghwanuri Special Self-Governing Province")

Mit dem obigen Code ersetzt man in der Spalte province des DataFrames rok_2030_census alle Vorkommen von "Gyeonggibuk-do" durch den langen neuen Namen.

Solche String‑Ersetzungen sind auch bei der Datenbereinigung hilfreich: Missing Values erscheinen in der Praxis oft nicht als NaN, sondern als Strings wie "Unknown", "Undisclosed" oder "Invalid". Insbesondere bei OCR‑Erfassung älterer Dokumente ist das eher die Regel als die Ausnahme.

Lektion 6. Umbenennen und Kombinieren

Mitunter möchte man Spalten- oder Indexnamen ändern. Häufig muss man auch mehrere DataFrames/Series zusammenführen.

Umbenennen

Mit rename() lassen sich Spalten- oder Indexnamen anpassen. rename() akzeptiert verschiedene Eingabeformen; am bequemsten ist meist ein Python‑Dict. Beispiel: Im DataFrame reviews die Spalte points in score umbenennen sowie die Indexwerte 0, 1 in firstEntry, secondEntry:

1
reviews.rename(columns={'points': 'score'})
1
reviews.rename(index={0: 'firstEntry', 1: 'secondEntry'})

Spaltennamen ändert man recht häufig; Indexwerte umzubenennen ist selten und für solche Zwecke nutzt man meist – wie zuvor gesehen – die Methode set_index().

Zeilen- und Spaltenachsen besitzen jeweils ein eigenes name‑Attribut; mit rename_axis() kann man diese Achsennamen setzen. Beispielsweise lässt sich die Zeilenachse wines und die Spaltenachse fields nennen:

1
reviews.rename_axis("wines", axis='index').rename_axis("fields", axis='columns')

Datensätze kombinieren

Häufig gilt es, DataFrames oder Series zu kombinieren. Pandas bietet dafür drei zentrale Werkzeuge – vom Einfachen zum Komplexen: concat(), join() und merge(). Der Kaggle‑Kurs konzentriert sich auf concat() und join(), weil sich vieles, was mit merge() möglich ist, mit join() einfacher erledigen lässt.

concat() ist am einfachsten: Es hängt mehrere DataFrames/Series entlang einer Achse aneinander. Besonders nützlich, wenn die zu kombinierenden Objekte dieselben Felder (Spalten) haben. Standardmäßig wird entlang der Indexachse (Zeilen) verkettet; mit axis=1 bzw. axis='columns' entlang der Spalten.

1
2
3
4
5
6
7
8
>>> s1 = pd.Series(['a', 'b'])
>>> s2 = pd.Series(['c', 'd'])
>>> pd.concat([s1, s2])
0    a
1    b
0    c
1    d
dtype: object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> df1 = pd.DataFrame([['a', 1], ['b', 2]],
...                    columns=['letter', 'number'])
>>> df1
  letter  number
0      a       1
1      b       2
>>> df2 = pd.DataFrame([['c', 3], ['d', 4]],
...                    columns=['letter', 'number'])
>>> df2
  letter  number
0      c       3
1      d       4
>>> pd.concat([df1, df2])
  letter  number
0      a       1
1      b       2
0      c       3
1      d       4
>>> df4 = pd.DataFrame([['bird', 'polly'], ['monkey', 'george']],
...                    columns=['animal', 'name'])
>>> df4
   animal    name
0    bird   polly
1  monkey  george
>>> pd.concat([df1, df4], axis=1)
  letter  number  animal    name
0      a       1    bird   polly
1      b       2  monkey  george

Laut Pandas‑Doku sollte man viele Zeilen nicht in einer Schleife einzeln an einen DataFrame anhängen. Stattdessen die Zeilen in einer Liste sammeln und einmalig mit concat() zusammenfügen.

join() ist etwas komplexer: Es hängt einen DataFrame anhand des Index an einen anderen an. Falls Spaltennamen kollidieren, müssen mit den Parametern lsuffix und rsuffix Suffixe angegeben werden, die an die jeweiligen Spaltennamen angehängt werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> df = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'],
...                    'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})
>>> df
  key   A
0  K0  A0
1  K1  A1
2  K2  A2
3  K3  A3
4  K4  A4
5  K5  A5
>>> other = pd.DataFrame({'key': ['K0', 'K1', 'K2'],
...                       'B': ['B0', 'B1', 'B2']})
>>> other
  key   B
0  K0  B0
1  K1  B1
2  K2  B2
>>> df.join(other, lsuffix='_caller', rsuffix='_other')
  key_caller   A key_other    B
0         K0  A0        K0   B0
1         K1  A1        K1   B1
2         K2  A2        K2   B2
3         K3  A3       NaN  NaN
4         K4  A4       NaN  NaN
5         K5  A5       NaN  NaN
Dieser Eintrag ist vom Autor unter CC BY-NC 4.0 lizensiert.