Nachdem mein Kollege Marvin in seinem Artikel die Bibliothek NumPy vorgestellt hat, wird sich dieser STATWORX Blog Beitrag rund um die Bibliothek Pandas drehen. Pandas fußt zu einem großen Teil auf NumPy, bietet allerdings gerade für einen Einsteiger in den Data Science Bereich eine einfache Möglichkeit, Daten in Python einzulesen sowie zu manipulieren. Wer die Funktionsweise von NumPy verstanden hat, wird mit Pandas auch keine Probleme haben.
Übersicht der Datenstrukturen in Pandas
Bevor wir auf die verschiedenen Möglichkeiten mit Pandas zu arbeiten eingehen, möchten wir kurz eine Einführung in die verschiedenen Strukturen, die unsere Daten in Pandas haben können, geben. Hierbei gibt es drei verschiedene Möglichkeiten:
Series
( 1-Dimensional)DataFrames
( 2-Dimensional)Panels
( 3-Dimensional)
Liegen beispielsweise Zeitreihendaten vor, so kann es ebenso vorteilhaft sein, nur eine Series zu verwenden. Mit DataFrames
haben wir in der Praxis am meisten zu tun, da sie einer typischen Tabelle bzw. n x m
Matrix entsprechen. DataFrames
vereinigen somit verschiedene Serien, sodass wir nicht nur einen Datenstrang repräsentieren können. Hierbei ist nun die Größe mutierbar, wobei die einzelnen Spalten logischerweise gleich langsein müssen. Mit DataFrames
werden wir in diesem Artikel primär arbeiten.
Panels
ergänzen DataFrames
wieder um eine weitere Dimension. Man kann sie sich somit als eine Sammlung von 2D-DataFrames vorstellen, wobei man sie aber in dieser Anordnung nicht wirklich visuell repräsentieren kann.
Installation und Erstellen eines DataFrames
Aber nun genug der Theorie! Pandas kann man wie jede andere Python Bibliothek über pip install pandas/ pip3 install pandas
bzw. conda install pandas
installieren. Der Import von Pandas erfolgt dann häufig mit der Abkürzung pd. Letztere ist sehr verbreitet und gibt jedem Data Scientist sofort die Information, dass in dem jeweiligen Skript mit Pandas gearbeitet wird.
import pandas as pd
Bevor wir darauf eingehen, wie man Datenformate mit Pandas einlesen kann, wollen wir kurz darstellen, wie man selbst einen DataFrame
in Pandas erstellen kann. Ein DataFrame
besteht aus zwei Bausteinen:
- Daten für die jeweiligen Spalten
- Spaltennamen
Die einfachste Möglichkeit zum Erstellen eines DataFrames
ist die Übergabe der Daten in Form eines arrays und die Vorgabe der Spaltennamen (columns) wie im folgenden Beispiel zu sehen.
import numpy as np
import pandas as pd
df = pd.DataFrame(data=np.random.randn(5, 3),
columns=['Spalte 1', 'Spalte 2', 'Spalte 3'])
Alternativ ist es ebenso möglich einen DataFrame
über ein Dictionary zu erstellen. In diesem Fall werden automatisch aus den Keys des Dictionaries, die Spaltennamen extrahiert:
df = pd.DataFrame(data={'Spalte 1': np.arange(start=10, stop=15),
'Spalte 2': np.arange(start=5, stop=10)})
Laden von Daten in Pandas
Da wir unsere Daten bekanntermaßen nicht alle manuell in Python eintragen wollen, existieren für die meisten populären Datenformate (*.csv, *.xlsx, *.dta, …) oder Datenbanken (SQL) bereits fertige Funktion in Pandas. Diese sind in den IO-Tools zusammengefasst. Einen Überblick findet ihr hier. Mit diesen Tools können nicht nur Daten eingelesen, sondern ebenfalls im passenden Format gespeichert werden. Die Funktionen sind vom Muster her alle sehr ähnlich und beginnen mit pd.read_...
, worauf die jeweilige Spezifikation folgt. Beispielsweise für Excel-Dateien pd.read_excel()
oder Stata pd.read_stata()
. Um die verschiedenen Funktionen und Arbeitsweise mit und an einem DataFrame
zu demonstrieren, nutzen wir den Titanic-Datensatz der Website Kaggle. Den Datensatz kann man auch über die Standford-Universität herunterladen. Nun laden wir einmal die Daten:
import pandas as pd
# Datensatz laden
df = pd.read_csv('.../titanic.csv')
Wie sind meine Daten aufgebaut?
Arbeitet man mit zunächst unbekannten Daten, kann man sich mit Pandas schnell einen Überblick über die Daten geben lassen, dabei greifen wir sowohl auf DataFrame
-Eigenschaften wie auch Funktionen zu. Als kleine Checkliste sollen folgende Befehle dienen:
# Datenüberblick
df.shape
df.columns
df.head()
df.tail()
df.describe()
Mit df.shape
fragen wir die Struktur des DataFrame
ab, d.h. wie viele Spalten und Zeilen der DataFrame
besitzt, in diesem Fall liegen 15 Spalten und 891 Zeilen bzw. Beobachtungen in dem Datensatz vor. Es ist also ein vergleichsweiser kleiner Datensatz. Mit df.columns
kann man sich nun die Spaltennamen ausgeben lassen. Jetzt wissen wir zwar wie die Spalten heißen, aber nicht wie die Daten konkret aussehen, das kann man einfach mit der head
oder tail
-Funktion ändern. Mit diesen Funktionen werden im Normalfall die ersten fünf oder letzten fünf Zeilen des Datensatzes zurückgegeben. Über den Funktionsparameter n können auch mehr Zeilen zurückgegeben werden. Wie so ein DataFrame
dann aussieht, seht ihr nun. Übersichtshalber sind hier nur die ersten fünf Spalten des Datensatz dargestellt, wenn ihr selbst die Funktion aufruft, werden deutlich mehr Spalte angezeigt.
Index | survived | pclass | sex | age | sibsp |
---|---|---|---|---|---|
0 | 0 | 3 | male | 22 | 1 |
1 | 1 | 1 | female | 38 | 1 |
2 | 1 | 3 | female | 26 | 0 |
3 | 1 | 1 | female | 35 | 1 |
4 | 0 | 3 | male | 35 | 0 |
Anhand dieser ersten Übersicht erkennt man, dass unterschiedliche Skalen in den Daten enthalten sind. Die letzte Funktion, die wir nun vorstellen möchten, um einen ersten Eindruck über Daten zu gewinnen, ist describe
. Mit dieser werden typische statistische Metriken wie der Durchschnitt und Median von denjenigen Spalten, die eine metrische Skala besitzen, zurückgegeben. Spalten wie z.B. die Spalte Geschlecht (sex) werden von dieser Funktion nicht berücksichtigt. Das Ergebnis gestaltet sich wie folgt:
survived | pclass | age | sibsp | parch | fare | |
---|---|---|---|---|---|---|
count | 891 | 891 | 714 | 891 | 891 | 891 |
mean | 0.383838 | 2.30864 | 29.6991 | 0.523008 | 0.381594 | 32.2042 |
std | 0.486592 | 0.836071 | 14.5265 | 1.10274 | 0.806057 | 49.6934 |
min | 0 | 1 | 0.42 | 0 | 0 | 0 |
25% | 0 | 2 | 20.125 | 0 | 0 | 7.9104 |
50% | 0 | 3 | 28 | 0 | 0 | 14.4542 |
75% | 1 | 3 | 38 | 1 | 0 | 31 |
max | 1 | 3 | 80 | 8 | 6 | 512.329 |
Neben den verschiedenen Metriken für die Spalten fällt doch deutlich auf, dass in der Spalte Alter (age) die Anzahl an Beobachtungen (count) von der Gesamtanzahl aller Beobachtungen abweicht. Um dieser Feststellung tiefer auf den Grund zu gehen, müssen wir uns die Spalte einmal genauer angucken.
Datenauswahl
Für die Auswahl von Spalten mit Pandas gibt es zwei Möglichkeiten:
# Spaltenauswahl
# 1. Moeglichkeit
df['age']
# 2. Moeglichkeit
df.age
Mit der ersten Möglichkeit kann man immer eine Spalte auswählen. Mit der zweiten Möglichkeit ist dies nicht immer garantiert, da Spaltennamen natürlich auch Leer- oder Sonderzeichen enthalten können. Führt man nun diesen Befehl aus, bekommt man eine Series
als Ergebnis zurück, also ein nx1
– Vektor. Dabei sieht man, dass einige Beobachtung fehlen und mit sogenannten NaN
Werte versehen sind. Wie man die NaNs
korrigieren kann, werden wir euch zum Ende des Artikels zeigen. Zunächst wollen wir noch darauf eingehen, wie man mehrere Spalten oder auch einen bestimmten Bereich eines DataFrame
auswählen kann.
# Auswahl von mehreren Spalten
df[['age', 'sex']]
# Auswahl eines Bereichs auf Indexbasis
df.iloc[:2, :3]
# Auswahl eines Bereichs mit z.B. Spaltennamen
df.loc[:3, ['class', 'age']]
Die Auswahl von mehreren Spalten ist ebenso einfach möglich, dabei ist es wichtig zu beachten, dass man eine Liste der auszuwählenden Spaltennamen erstellt bzw. übergibt. Bei der Auswahl eines Ausschnittes des DataFrames
bedient man sich der Funktionen iloc
oder loc
, erstere ist für eine indexbasierte Auswahl gedacht, d.h. man übergibt an die Funktion keine Spaltennamen, sondern die jeweilige Position der Spalte bzw. einen Bereich von Spalten bzw. Indizes. Bei der Funktion loc
kann man dann wiederrum mit Listen und einzelnen Spaltennamen arbeiten. In dem obigen Beispiel würden so zunächst alle Zeilen bis zum Index 2 und die ersten drei Spalten ausgewählt werden. Bei dem loc
-Beispiel werden hingegen Zeilen bis zum Index 3 in Verbindung mit den Spalten age
und class
ausgewählt. Mit diesen Funktionen schafft man einen Einstieg in die Auswahl von verschiedenen Datensätzen, die Beispiele kratzen nur an der Oberfläche der Möglichkeiten, wie man Daten auswählen kann.
Datenmanipulation
In der Praxis perfekt aufbereitete Daten vorzufinden, wäre eine super Sache, leider sieht die Realität dann doch etwas anders aus. Ein erstes Beispiel hatten wir bereits oben mit der Spalte age
. Um unsere Daten später für Auswertungen oder Visualisierungen zu nutzen, wollen wir nun eine Möglichkeit darstellen, die Werte zu korrigieren. Hierzu bietet sich die Verwendung der fillna
-Funktion an. Mit dieser werden die NaN
-Werte automatisch ausgewählt und mit Werten, die man vorher festlegt hat, aufgefüllt. In unserem Fall füllen wir als Wert einfach das Durchschnittsalter auf. Wir fügen somit dem Alter keine Ausreißer hinzu und der Durchschnitt verändert sich nicht.
# Auffüllen von NaN-Werten
df.age.fillna(value=df.age.mean())
Nach Aufruf der Funktionen müssen wir natürlich noch die neue Altersspalte in unseren Datensatz einfügen, da ansonsten die Transformation nicht gespeichert wird. Die Zuweisung funktioniert sehr einfach, indem wir den neuen Spaltennamen so übergeben, als ob wir eine existierende Spalte auswählen würden. Im Anschluss kann man dann die neuen Werte einfach aufrufen. Alternativ könnte man die bisherige Altersspalte überschreiben, dies würde aber dazu führen, dass die Rohdaten nicht mehr ersichtlich wären.
# Zuweisen von neuen Werte zu einer Spalte / Erstellen einer neuen Spalte
df['age_new']=df.age.fillna(value=df.age.mean())
Zusammenfassung
Am Ende des Artikels wollen wir kurz noch wichtige Informationen über den Einstieg mit Pandas zusammenfassen:
- Einlesen von Daten funktioniert einfach mit den Funktionen
pd.read_..
- Daten liegen in der Regel als zweidimensionaler
DataFrame
vor - Mit den Funktionen
df.shape
,df.head()
,df.tail()
unddf.describe()
erhält man einen ersten Überblick über die Daten - Spalten können sowohl einfach als auch mehrfach mit dem zugehörigen Namen ausgewählt werden
- Bereiche eines DataFrame werden entweder mit
iloc
oder mitloc
ausgewählt - Mit der Funktion
fillna
können fehlende Beobachtungen ersetzt werden
Diese Vorstellung stellt wie gesagt nur einen ersten Einstieg in die Bibliothek Pandas dar.
Als kleiner Ausblick in unseren nächsten Artikel in der Reihe Data Science mit Python werden wir uns dann mit der Visualisierungsbibliothek Matplotlib beschäftigen.