Pandas
Pandas ist eine leistungsstarke und weit verbreitete Bibliothek für Datenanalyse und -manipulation in der Programmiersprache Python. Entwickelt auf Basis von NumPy bietet Pandas Datenstrukturen, die ideal für die Verarbeitung und Analyse von strukturierten Daten wie Tabellen oder CSV-Dateien geeignet sind. Diese Bibliothek erleichtert das Arbeiten mit großen Datensätzen und ermöglicht es, komplexe Operationen auf Daten schnell und effizient durchzuführen. Pandas wird in den Bereichen Datenwissenschaft, Finanzanalyse, Machine Learning und anderen datenintensiven Anwendungen eingesetzt. In dieser Einführung werden wir einen Blick auf die Schlüsselfunktionen und -konzepte von Pandas werfen, um ein Verständnis für die Vielseitigkeit dieser Bibliothek zu entwickeln.
pip install pandas
import pandas as pd
Series und DataFrame
Pandas führt zwei grundlegende Datenstrukturen ein: DataFrames
und Series
. Ein DataFrame kann als eine zweidimensionale Datenstruktur betrachtet werden, die in Form einer Tabelle organisiert ist. Diese Tabelle besteht aus Zeilen und Spalten, wobei jede Spalte einen bestimmten Datentyp repräsentiert. Einzelne Spalten in einem DataFrame werden als Series bezeichnet. Series hingegen kann als eindimensionales Array oder eine Liste betrachtet werden und enthält Daten eines bestimmten Datentyps.
Series
Eine Series
ist eine grundlegende eindimensionale, beschriftete Datenstruktur, vergleichbar mit einer Liste oder einem Array, jedoch mit einem Index, der jedem Element zugeordnet ist. In Pandas ist die Series eine essentielle Datenstruktur und bildet die Grundlage für DataFrames. Sie kann als eindimensionales Array oder Liste betrachtet werden und enthält Daten unterschiedlicher Datentypen wie numerische Werte, Zeichenketten oder Zeitstempel. Die Series ermöglicht den Zugriff auf Daten durch einen Index und unterstützt eine Vielzahl von Funktionen zur Datenmanipulation. Sie erlaubt es, spezifische Datenpunkte in einem DataFrame zu isolieren und gezielte Operationen auf einzelnen Spalten durchzuführen, was sie zu einem unverzichtbaren Werkzeug für die Arbeit mit strukturierten Daten in Pandas macht.
Eine Series kann in Python so erstellt werden:
import pandas as pd
s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
Diese Series ist dann eine Spalte, welche so aussieht (die Buchstaben sind in diesem Fall die Indizes, sprich keine aussagekräftigen Daten, sondern nur Werkzeuge der Series):
a 1b 3c 5d 7e 9dtype: int64
Zugriff auf Daten
Um nun auf eine Zelle mithilfe des Indexes zuzugreifen, kann man die Syntax mit den eckigen Klammern ([]
) oder die Methode loc()
verwenden.
print(s['a']) # Zugriff auf Element mit Index 'a'print(s.loc['a']) # Zugriff auf Element mit Index 'a'
Man kann Zellen auch ohne den vergebenen Index selektieren (iloc()
-Methode).
print(s.iloc[0]) # Zugriff auf Element an erster Stelle (0)
DataFrame
Ein DataFrame
ist eine zweidimensionale, tabellarische Datenstruktur mit beschrifteten Achsen (Zeilen und Spalten), die die Hauptdatenstruktur in Pandas darstellt. Diese Struktur organisiert Daten in Zeilen und Spalten, wobei jede Spalte eine Series darstellt. DataFrames sind äußerst vielseitig und ermöglichen die effiziente Verarbeitung sowie Analyse von strukturierten Daten unterschiedlicher Datentypen. Sie erleichtern den Zugriff, die Filterung und Transformation von Daten, und werden häufig zur Darstellung von Tabellen in verschiedenen Formaten wie CSV-Dateien, Excel-Tabellen oder SQL-Abfragen verwendet. Operationen auf DataFrames können sowohl auf Zeilen als auch auf Spalten angewendet werden, was eine umfassende Datenmanipulation ermöglicht.
Erstellen eines DataFrames
Ein DataFrame kann ähnlich wie eine Series erstellt werden. Dabei kann man eine Vielzahl an verschiedenen Objekttypen übergeben.
-
Dictionary
In diesem Fall hat der Input für das DataFrame den TypenDictionary
, dessen Schlüssel ein String und dessen Wert eine Liste ist.data = {'A': [1, 2, 3, 4],'B': [5, 6, 7, 8],'C': [9, 10, 11, 12]}df = pd.DataFrame(data, index=["I","II","III","IV"]) -
Liste
In diesem Fall hat der Input für das DataFrame den Typen einer Liste, in welcher sich mehrere Dictionaries befinden.data = [{'A': 1, 'B': 5, 'C': 9},{'A': 2, 'B': 6, 'C': 10},{'A': 3, 'B': 7, 'C': 11},{'A': 4, 'B': 8, 'C': 12}]df = pd.DataFrame(data, index=["I","II","III","IV"]) -
Tuple
In diesem Fall hat der Input für das DataFrame den Typen einer Liste, mit mehreren Tuples darin. Man beachte, dass die Spaltennamen beim instantiieren des DataFrames angegeben werden müssen, dadata
selbst keine Informationen darüber enthält.data = [(1, 5, 9),(2, 6, 10),(3, 7, 11),(4, 8, 12)]df = pd.DataFrame(data, columns=['A', 'B', 'C'], index=["I","II","III","IV"]) -
NumPy
Man kann ein DataFrame auch mithilfe von einem Numpy-Array
erstellen.import numpy as npdata = np.array([[1, 5, 9],[2, 6, 10],[3, 7, 11],[4, 8, 12]])df = pd.DataFrame(data, columns=['A', 'B', 'C'], index=["I","II","III","IV"]) -
CSV
Man kann ein DataFrame auch mithilfe einer CSV-Datei erstellen. Dies kommt in der Praxis sehr häufig vor, da CSV-Dateien häufig als Eingabe für Analyseprogramme verwendet werden. Hierbei kann man auch angeben, dass eine Bestimmte Spalte Informationen bezüglich der Indizes enthält.file.csv ,A,B,CI,1,5,9II,2,6,10III,3,7,11IV,4,8,12df = pd.read_csv('file.csv', index_col=0) # Beispiel für das Lesen aus einer CSV-Datei -
JSON
Natürlich kann das Datenformat auch JSON sein:file.json {"A": { "I": 1, "II": 2, "III": 3, "IV": 4 },"B": { "I": 5, "II": 6, "III": 7, "IV": 8 },"C": { "I": 9, "II": 10, "III": 11, "IV": 12 }}df = pd.read_json('file.json') # Beispiel für das Lesen aus einer JSON-Datei -
SQL-Datenbank
Man kann auch direkt aus einer SQL-Datenbank lesen. In diesem Fall muss man zunächst eine Verbindung mit der Datenbank herstellen.Installing sqlite3 pip install pysqlite3import sqlite3con = sqlite3.connect("database.db")df = pd.read_sql_query("SELECT * FROM examples", con)
In allen Fällen sieht das DataFrame in diesem Beispiel zum Schluss so aus:
A B CI 1 5 9II 2 6 10III 3 7 11IV 4 8 12
Speichern eines DataFrames
Um ein DataFrame persistieren zu können, kann man die Daten in eine Datei oder Datenbank speichern. Dabei werden die Formate CSV
und JSON
am häufigsten verwendet, weswegen die Integration mit Pandas einfacher denn je ist.
-
CSV
df.to_csv('example.csv') -
JSON
df.to_json('example.json') -
SQL
df.to_sql('example.sql', con)
Zugriff auf Daten
Bei einem DataFrame ist der Zugriff auf die Daten nun ein wenig anders, da ein DataFrame quasi eine Liste von Series ist.
-
Zugriff auf bestimmte Spalten
Um auf eine Spalte zuzugreifen, können diese Möglichkeiten verwendet werden:print(df.A) # Property Aprint(df['A']) # Spalte Aprint(df.loc[:, 'A']) # Spalte A; alle Zeilenprint(df.iloc[:, 0]) # Spalte Index 0; alle ZeilenUm auf mehrere Spalten zuzugreifen, können diese Möglichkeiten verwendet werden:
print(df[['A', 'B']]) # Spalten A & Bprint(df.loc[:, ['A', 'B']]) # Spalten A & B; alle Zeilenprint(df.iloc[:, [0, 1]]) # Spalten Indizes 0 & 1; alle Zeilenprint(df.iloc[:, 0:2]) # Spalten Indizes 0 bis 1; alle Zeilenprint(df.iloc[:, :2]) # Spalten Indizes bis 1; alle Zeilenprint(df.iloc[:, :-1]) # Spalten Indizes bis vorletzte Spalte; alle Zeilen -
Zugriff auf bestimmte Zeilen
Um auf eine Zeile zuzugreifen, können diese Möglichkeiten verwendet werden:print(df.loc["I"]) # Zeile Iprint(df.iloc[0]) # Zeile Index 0Um auf mehrere Zeilen zuzugreifen, können diese Möglichkeiten verwendet werden:
print(df.loc[["I", "II"]]) # Zeilen I & IIprint(df.iloc[[0, 1]]) # Zeilen Indizes 0 & 1print(df.iloc[0:2]) # Zeilen Indizes 0 bis 1print(df.iloc[:2]) # Zeilen Indizes bis 1print(df.iloc[:-2]) # Zeilen Indizes bis vorvorletzte Zeile -
Zugriff auf bestimmte Zellen
Um auf eine Zelle zuzugreifen, können diese Möglichkeiten verwendet werden:print(df.loc["I", 'A']) # Zeile I; Spalte Aprint(df.iloc[0, 0]) # Zeile Index 0; Spalte Index 0Um auf mehrere Zellen zuzugreifen, können diese Möglichkeiten verwendet werden (bei
iloc()
sind die Angaben jeweilsinklusiv:exklusiv
):print(df.loc[["I","II","III"], ['A','B']]) # Zeilen I, II & III; Spalten A & Bprint(df.loc["I":"III", 'A':'B']) # Zeilen I bis III; Spalten A bis Bprint(df.iloc[0:3, 0:2]) # Zeilen Indizes 0 bis 2; Spalten Indizes 0 bis 1print(df.iloc[:3, :2]) # Zeilen Indizes bis 2; Spalten Indizes bis 1print(df.iloc[:-1, :-1]) # Zeilen Indizes bis vorletzte Zeile; Spalten Indizes bis vorletzte Spalte -
Zugriff mittels Bedingungen
Die Daten eines DataFrames können mithilfe von Bedingungen auch gefiltert werden:# Alle Zeilen, dessen Wert in Spalte 'A' größer 2 istprint(df[df['A'] > 2])"""A B CIII 3 7 11IV 4 8 12"""# Alle Series 'A', dessen Wert größer 2 istprint(df.loc[df['A'] > 2, 'A'])"""III 3IV 4"""Diese Bedingungen funktieren nur, weil die Bedingung eine Series selbst ist und somit die Berechnung pro Zeile durchgeführt werden.
df['A'] > 2"""I FalseII FalseIII TrueIV TrueName: A, dtype: bool"""
Operationen und Methoden
Pandas bietet eine Vielzahl von Methoden und Operationen zur Datenanalyse und -manipulation. Alle Methoden, welche die Daten des DataFrames verändern, verfügen über zwei Möglichkeiten, die Änderungen zu speichern:
-
Überschreiben
Einerseits kann man die alten Daten einfach überschreiben, indem man das DataFrame auf die neuen Daten setzt:df = df.dropna()Im Hintergrund wird der Parameter
inplace
der Methode aufFalse
gesetzt und die neuen Daten werden einfach returniert.df.dropna(inplace=False) # dieser Aufruf gibt das DataFrame zurück -
Inplace
Die elegantere Variante ist jedoch die Verwendung derinplace
-Option, welche die Änderungen direkt in dem DataFrame speichert:df.dropna(inplace=True)Wichtig zu beachten ist, dass beim positiven Setzen des
inplace
-Parameters kein DataFrame mehr zurückgegeben wird. Stattdessen returniert dieser Aufruf nunNone
.
head()
Mit der Methode head
kann man die ersten n Zeilen eines DataFrames anzeigen.
df.head(3) # Standardmäßig ersten 5 Zeilen
""" A B CI 1 5 9II 2 6 10III 3 7 11"""
tail()
Mit der Methode tail
kann man die letzten n Zeilen eines DataFrames anzeigen.
df.tail(3) # Standardmäßig letzten 5 Zeilen
""" A B CII 2 6 10III 3 7 11IV 4 8 12"""
shape
Mit der Eigenschaft shape
kann man die Dimensionen des DataFrames anzeigen.
df.shape
"""(4, 3)"""
Dabei ist die Reihenfolge der Anzahl an Dimensionen gleich der aufsteigenden Achsenzahl. In unserem Beispiel bedeutet das, wir haben vier Zeilen und drei Spalten, weil Achse 0 die Zeilen sind und Achse 1 die Spalten.
Die erste Zahl ist in den meisten Fällen der künstlichen Intelligenz die Anzahl an Beispielen. In unserem Fall gibt uns die zweite Zahl Auskunft über die Anzahl an Features (Eigenschaften) jedes Beispiels. Beim Supervised Learning
ist die letzte Spalte meistens die Antwort des Beispiels, weshalb es auch nur zwei Features sein könnten.
columns
Mit der Eigenschaft columns
kann man sich die Namen der Spalten ansehen.
df.columns
"""Index(['A', 'B', 'C'], dtype='object')"""
info()
Mit der Methode info
kann man sich allgemeine Informationen über ein DataFrame oder eine Series ausgeben lassen.
df.info()
"""<class 'pandas.core.frame.DataFrame'>Index: 4 entries, I to IVData columns (total 3 columns): # Column Non-Null Count Dtype--- ------ -------------- ----- 0 A 4 non-null int64 1 B 4 non-null int64 2 C 4 non-null int64dtypes: int64(3)memory usage: 128.0+ bytesNone"""
df['A'].info()
"""<class 'pandas.core.series.Series'>Index: 4 entries, I to IVSeries name: ANon-Null Count Dtype-------------- -----4 non-null int64dtypes: int64(1)memory usage: 64.0+ bytesNone"""
describe()
Mit der Methode describe
kann man sich Informationen der deskriptiven Statistik über ein DataFrame oder eine Series ausgeben lassen.
df.describe()
""" A B Ccount 4.000000 4.000000 4.000000mean 2.500000 6.500000 10.500000std 1.290994 1.290994 1.290994min 1.000000 5.000000 9.00000025% 1.750000 5.750000 9.75000050% 2.500000 6.500000 10.50000075% 3.250000 7.250000 11.250000max 4.000000 8.000000 12.000000"""
df['A'].describe()
"""count 4.000000mean 2.500000std 1.290994min 1.00000025% 1.75000050% 2.50000075% 3.250000max 4.000000Name: A, dtype: float64"""
corr()
Die Methode corr
errechnet die Korrelationen zwischen den Spalten.
""" A B CI 2 3 4II 4 9 3III 8 27 2IV 16 81 1"""
df.corr()
""" A B CA 1.000000 0.991934 -0.959166B 0.991934 1.000000 -0.916515C -0.959166 -0.916515 1.000000"""
isnull()
Die Methode isnull
gibt ein DataFrame mit boolischen Werte zurück, welche Auskunft über die fehlenden Werte eines DataFrames geben. Mithilfe der sum
-Methode kann man dadurch wichtige Informationen des DataFrames extrahieren.
""" A B CI NaN 5 9.0II 2.0 6 10.0III 3.0 7 11.0IV 4.0 8 NaN"""
df.isnull()
""" A B CI True False FalseII False False FalseIII False False FalseIV False False True"""
df.isnull().sum()
"""A 1B 0C 1dtype: int64"""
df.isnull().sum(axis=1)
"""I 1II 0III 0IV 1dtype: int64"""
df.isnull().sum().sum()
"""2"""
dropna()
Die Methode dropna
entfernt Zeilen (oder Spalten) mit fehlenden Werten (NaN
— Not a Number) aus dem DataFrame.
dropna
zählt zu den manipulativen Methoden, weswegen der Paramter inplace
zur Verfügung steht.
""" A B CI NaN 5 9.0II 2.0 6 10.0III 3.0 7 11.0IV 4.0 8 NaN"""
df.dropna() # automatically axis 0
""" A B CII 2.0 6 10.0III 3.0 7 11.0"""
df.dropna(axis=1)
""" BI 5II 6III 7IV 8"""
fillna()
Die Methode fillna
ersetzt Zellen mit fehlenden Werten (NaN
— Not a Number).
fillna
zählt zu den manipulativen Methoden, weswegen der Paramter inplace
zur Verfügung steht.
""" A B CI NaN 5 9.0II 2.0 6 10.0III 3.0 7 11.0IV 4.0 8 NaN"""
df.fillna(0)
""" A B CI 0.0 5 9.0II 2.0 6 10.0III 3.0 7 11.0IV 4.0 8 0.0"""
unique()
Die Methode unique
gibt die eindeutigen Werte in einer Spalte zurück.
""" A B CI 1 5 9II 2 6 10III 1 7 11IV 4 8 12"""
df['A'].unique()
"""[1 2 4]"""
nunique()
Die Methode nunique
gibt die Anzahl der eindeutigen Werte insgesamt zurück.
""" A B CI 1 5 9II 2 6 10III 1 7 11IV 4 8 12"""
df['A'].nunique()
"""3"""
value_counts()
Die Methode value_counts
gibt die Anzahl der eindeutigen Werte pro Spalte zurück.
""" A B CI 1 5 9II 2 6 10III 1 7 11IV 4 8 12"""
df['A'].value_counts()
"""A1 22 14 1Name: count, dtype: int64"""
apply()
Die Methode apply
wendet eine Funktion entlang einer Achse des DataFrame an. Sie kann auf Zeilen oder Spalten angewendet werden.
Standardmäßig werden Operationen auf eine Spalte angewandt (axis=0
).
# Durchschnitt pro Spaltedf.apply(lambda x: x.mean())
"""A 2.5B 6.5C 10.5dtype: float64"""
Dies kann man jedoch einfach ändern:
# Durchschnitt pro Zeiledf.apply(lambda x: x.mean(), axis=1)
"""I 5.0II 6.0III 7.0IV 8.0dtype: float64"""
map()
Die Methode map
wendet eine Funktion auf jedes Element einer Series an. Die Methode ist demnach ähnlich wie apply
pro Spalte mit dem Unterschied, dass map
nicht auf das gesamte DataFrame (alle Spalten auf einmal) angewandt werden kann.
# Funktion zur Quadratbildung auf jedes Element anwendendf['A'].map(lambda x: x ** 2)
"""I 1II 4III 9IV 16Name: A, dtype: int64"""
applymap()
Die Methode applymap
wendet eine Funktion auf jedes Element (jede Zelle) eines DataFrame an.
# Funktion zur Quadratbildung auf jedes Element des gesamten DataFrame anwendendf.applymap(lambda x: x ** 2)
""" A B CI 1 25 81II 4 36 100III 9 49 121IV 16 64 144"""
groupby()
Die Methode groupby
gruppiert das DataFrame nach den Werten einer oder mehrerer Spalten und ermöglicht Aggregationen wie bei SQL-Abfragen.
""" A B CI 1 5 9II 1 6 10III 3 7 11IV 3 8 12"""
df.groupby('A').sum() # Summe der Gruppen 1 und 3 berechnen
""" B CA1 11 193 15 23"""
rename()
Mithilfe der manipulativen Methode rename
kann man jegliche Eigenschaften eines DataFrames umbenennen. Meistens will man die Spaltennamen oder Indexes umbenennen.
rename
zählt zu den manipulativen Methoden, weswegen der Paramter inplace
zur Verfügung steht.
df.rename(columns={'A':'X', 'B':'Y', 'C':'Z'})
""" X Y ZI 1 5 9II 2 6 10III 3 7 11IV 4 8 12"""
df.rename(index=str.lower)
""" A B Ci 1 5 9ii 2 6 10iii 3 7 11iv 4 8 12"""
replace()
Mithilfe der manipulativen Methode replace
kann man beliebige alphanumerische Werte mit statischen oder dynamischen Werten ersetzen. Allerdings können diese Werte nur in den Daten, also nicht in den Spaltennamen und Indizes, vorkommen. Deswegen ist das replace
quasi das rename
für die Werte.
replace
zählt zu den manipulativen Methoden, weswegen der Paramter inplace
zur Verfügung steht.
df.replace(1, 2)
""" A B CI 2 5 9II 2 6 10III 3 7 11IV 4 8 12"""
Anwendungsbeispiele
Bereinigen von Nullwerten
Im Bereich der künstlichen Intelligenz sind Nullwerte oft unerwünscht, weshalb man diese meistens mit Werte ersetzen möchte, welche das Trainieren des Modells nicht behindern. Eine einfache Möglichkeit, Nullwerte nicht ganz so störend zu machen, ist das spaltenweise Ersetzen der Nullwerte durch den Durchschnitt der Werte in der Spalte.
for col in df.columns: df[col].fillna(df[col].mean(), inplace=True)
Meistens ist es auch noch sinnvoll, die Nullwerte nicht einfach mit dem Median aller Datensätze zu ersetzen, sondern nur mit dem Median der Datensätze, die beim Supervised Learning auch die gleichen Antworten haben. Somit sind die fehlenden Daten am wenigsten störend für das Trainieren.
""" A B C OutcomeI 31 34.0 32.0 0II 17 NaN 10.0 1III 13 NaN 11.0 1IV 14 18.0 12.0 1V 29 26.0 29.0 0VI 31 35.0 NaN 0"""
# Replace all NaN with median + watch out for Outcome valuesensible_col = ['A', 'B', 'C']masks = [ df['Outcome'] == 0, df['Outcome'] == 1 ]
for mask in masks: df.loc[mask, sensible_col] = df.loc[mask, sensible_col].replace(np.nan, df[df[mask] != np.nan].mean())
""" A B C OutcomeI 31 34.0 32.0 0II 17 18.0 10.0 1III 13 18.0 11.0 1IV 14 18.0 12.0 1V 29 26.0 29.0 0VI 31 35.0 30.5 0"""
Standardisierung der Spaltennamen
In Python gilt meistens snake_case
. Das bedeutet, dass alles klein geschrieben und Wörter mit Unterstrichen (_
) getrennt werden. Da viele Datensätze oft nicht diesen Konventionen folgen, ist es sinnvoll, die Spaltennamen zu standardisieren.
In diesem Beispiel werden alle Spaltennamen automatisch in Kleinbuchstaben konvertiert, Klammern entfernt und Leerzeichen durch Unterstriche ersetzt.
df.columns = [col.lower().replace("(", "").replace(")", "").replace(" ", "_") for col in df]