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
Abschnitt betitelt „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.
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
Abschnitt betitelt „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
Abschnitt betitelt „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
Abschnitt betitelt „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
Abschnitt betitelt „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
Abschnitt betitelt „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
Abschnitt betitelt „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
.
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"""
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"""
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
Abschnitt betitelt „columns“Mit der Eigenschaft columns
kann man sich die Namen der Spalten ansehen.
df.columns
"""Index(['A', 'B', 'C'], dtype='object')"""
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()
Abschnitt betitelt „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"""
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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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"""
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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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()
Abschnitt betitelt „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
Abschnitt betitelt „Anwendungsbeispiele“Bereinigen von Nullwerten
Abschnitt betitelt „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
Abschnitt betitelt „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]