pandas
– eine Bibliothek für tabellarische Daten¶
Pandas ist eine Python-Bibliothek, die vorrangig zum Auswerten und Bearbeiten tabellarischer Daten gedacht ist. Dafür sind in Pandas drei Arten von Objekten definiert:
- Eine
Series
entspricht in vielerlei Hinsicht einer „eindimensionalen“ Liste, beispielsweise einer Zeitreihe, einer Liste, einem Dict, oder einem Numpy -Array. - Ein
Dataframe
besteht aus einer „zweidimensionalen“ Tabelle. Die einzelnen Reihen beziehungsweise Spalten dieser Tabelle können wieSeries
-Objekte bearbeitet werden. - Ein
Panel
besteht aus einer „dreidimensionalen“ Tabelle. Die einzelnen Ebenen dieser Tabelle bestehen wiederum ausDataframe
-Objekten.
In den folgenden Abschnitten sollen in Anlehnung an das berühmte 10 minutes to
pandas-Tutorial die
Series
- und die Dataframe
-Objekte als grundlegende und am häufigsten
verwendeten Pandas-Objekte kurz vorgestellt werden.
Arbeiten mit Series
-Objekten¶
Ein neues Series-Objekt kann mittels der gleichnamigen Funktion beispielsweise aus einer gewöhnlichen Liste generiert werden:
import pandas as pd
s = pd.Series( [5,10,15,20,25] )
s
# Ergebnis:
# 0 5
# 1 10
# 2 15
# 3 20
# 4 25
# dtype: int64
Das Series-Objekt erhält automatisch einen Index, so dass beispielsweise mittels
s[0]
auf das erste Element, mit s[1]
auf das zweite Element, usw.
zugegriffen werden kann. Neben diesen numerischen Indizes, die auch bei
gewöhnlichen Listen verwendet werden, können explizit auch andere Indizes
vergeben werden:
s.index = ['a','b','c','d','e']
s
# Ergebnis:
# a 5
# b 10
# c 15
# d 20
# e 25
# dtype: int64
Nun können die einzelnen Elemente zwar immer noch mit s[0]
, s[1]
, usw.,
aber zusätzlich auch mittels s['a']
, s['b']
usw. ausgewählt werden. [1]
Wird bei der Generierung eines Series-Objekts ein Dict angegeben,
so werden automatisch die Schlüssel als Indizes und die Werte als eigentliche
Listenelemente gespeichert.
Slicings
Sollen mehrere Elemente ausgewählt werden,so können die entsprechenden Indizes wahlweise als Liste oder als so genannter „Slice“ angegeben werden:
# Zweites und drittes Element auswählen:
s[ [1,2] ]
# Ergebnis:
# b 10
# c 15
# Identische Auswahl mittels Slicing:
s[ 1:3 ]
# Ergebnis:
# b 10
# c 15
Bei Slicings wird, ebenso wie bei range()-Angaben, die obere Grenze nicht in den Auswahlbereich mit eingeschlossen. Die Auswahl mittels Slicing hat bei Series-Objekten also die gleiche Syntax wie die Auswahl von Listenobjekten.
Zeitreihen
Zeitangaben in Series-Objekten können mittels der Pandas-Funktion
date_range()
generiert werden:
dates = pd.date_range('2000-01-01', '2000-01-07')
dates
# <class 'pandas.tseries.index.DatetimeIndex'>
# [2000-01-01, ..., 2000-01-07]
# Length: 7, Freq: D, Timezone: None
Als Start- und Endpunkt werden allgemein Datumsangaben mit einer gleichen Syntax
wie im datetime
-Modul verwendet. Zusätzlich kann angegeben werden, in
welchen Zeitschritten die Zeitreihe erstellt werden soll:
weekly = pd.date_range('2000-01-01', '2000-02-01', freq="W")
weekly
# Ergebnis:
# <class 'pandas.tseries.index.DatetimeIndex'>
# [2000-01-02, ..., 2000-01-30]
# Length: 5, Freq: W-SUN, Timezone: None
hourly = pd.date_range('2000-01-01 8:00', '2000-01-01 18:00', freq="H")
hourly
# Ergebnis:
# <class 'pandas.tseries.index.DatetimeIndex'>
# [2000-01-01 08:00:00, ..., 2000-01-01 18:00:00]
# Length: 11, Freq: H, Timezone: None
Die Elemente der Zeitreihe können explizit mittels list(zeitreihe
,
beispielsweise list(dates)
, ausgegeben werden; in Series-Objekten werden
Zeitreihen häufig als Index-Listen verwendet.
Arbeiten mit Dataframe
-Objekten¶
Ein neues Dataframe-Objekt kann mittels der Funktion DataFrame()
beispielsweise aus einer gewöhnlichen Liste generiert werden:
import pandas as pd
# 1D-Beispiel-Dataframe erstellen:
df = pd.DataFrame( [5,10,15,20,25] )
df
# Ergebnis:
# 0
# 0 5
# 1 10
# 2 15
# 3 20
# 4 25
#
# [5 rows x 1 columns]
Als Unterschied zu einem Series-Objekt werden bei einem Dataframe sowohl die Zeilen als auch die Spalten mit einem Index versehen.
Mehrspaltige Dataframes können auch über ein dict
-Objekt definiert werden,
wobei die Schlüsselwerte den Spaltennamen und die damit verbundenen Werte
einzelnen Daten entsprechen, aus denen der Dataframe generiert werden soll:
# 2D-Beispiel-Dataframe erstellen:
df2 = pd.DataFrame({
'A' : 1.,
'B' : pd.date_range('2000-01-01', '2000-01-07'),
'C' : pd.Series(range(7), dtype='float32'),
'D' : np.random.randn(7),
'E' : pd.Categorical(['on', 'off', 'on', 'off', 'on', 'off', 'on']),
'F' : 'foo' })
df2
# Ergebnis:
# A B C D E F
# 0 1 2000-01-01 0 -2.611072 on foo
# 1 1 2000-01-02 1 0.630309 off foo
# 2 1 2000-01-03 2 -1.645430 on foo
# 3 1 2000-01-04 3 1.056535 off foo
# 4 1 2000-01-05 4 2.194970 on foo
# 5 1 2000-01-06 5 0.537804 off foo
# 6 1 2000-01-07 6 1.011678 on foo
Wie man sieht, wird bei Angabe eines einzelnen Wertes für eine Spalte dieser als konstant für die ganze Spalte angenommen; listenartige Objekte hingegen müssen allesamt die gleiche Länge aufweisen.
Datentypen
Innerhalb einer Spalte eines Dataframe-Objekts müssen alle Werte den gleichen Datentyp aufweisen. Man kann sich die Datentypen der einzelnen Spalten folgendermaßen anzeigen lassen:
# Datentypen anzeigen:
df2.dtypes
# Ergebnis:
# A float64
# B datetime64[ns]
# C float32
# D float64
# E category
# F object
# dtype: object
Daten anzeigen und sortieren¶
Bei längeren Datensätzen kann es bereits hilfreich sein, nur einen kurzen Blick
auf den Anfang oder das Ende der Tabelle werfen zu können. Bei
Dataframe-Objekten ist dies mittels der Funktionen head()
beziehungsweise
tail()
möglich:
# Die ersten fünf Zeilen des Dataframes anzeigen:
df2.head()
# Ergebnis:
# A B C D E F
# 0 1 2000-01-01 0 -2.611072 on foo
# 1 1 2000-01-02 1 0.630309 off foo
# 2 1 2000-01-03 2 -1.645430 on foo
# 3 1 2000-01-04 3 1.056535 off foo
# 4 1 2000-01-05 4 2.194970 on foo
# Die letzten drei Zeilen des Dataframes anzeigen:
df2.tail(3)
# Ergebnis:
# A B C D E F
# 4 1 2000-01-05 4 2.194970 on foo
# 5 1 2000-01-06 5 0.537804 off foo
# 6 1 2000-01-07 6 1.011678 on foo
Standardmäßig geben head()
und tail()
je fünf Zeilen aus; ist eine andere
Anzahl gewünscht, so kann diese als Argument angegeben werden.
Spalten und Index-Werte
Die einzelnen Bestandteile eines Dataframes, d.h. die Spaltennamen, die Index-Werte
sowie die eigentlichen Daten, können über die Attribute columns
, index
und values
des Dataframes abgerufen werden:
# Spaltennamen, Index-Werte und Inhalt des Dataframes ausgeben:
df2.columns
# Ergebnis:
# Index(['A', 'B', 'C', 'D', 'E', 'F'], dtype='object')
df2.index
# Ergebnis:
# Int64Index([0, 1, 2, 3, 4, 5, 6], dtype='int64')
df2.values
# Ergebnis:
# array([[1.0, Timestamp('2000-01-01 00:00:00'), 0.0, -2.611072451193798, 'on', 'foo'],
# [1.0, Timestamp('2000-01-02 00:00:00'), 1.0, 0.6303090119623712, 'off', 'foo'],
# [1.0, Timestamp('2000-01-03 00:00:00'), 2.0, -1.645429619256174, 'on', 'foo'],
# [1.0, Timestamp('2000-01-04 00:00:00'), 3.0, 1.056535156797566, 'off', 'foo'],
# [1.0, Timestamp('2000-01-05 00:00:00'), 4.0, 2.1949702833421596, 'on', 'foo'],
# [1.0, Timestamp('2000-01-06 00:00:00'), 5.0, 0.5378036597920774, 'off', 'foo'],
# [1.0, Timestamp('2000-01-07 00:00:00'), 6.0, 1.01167812002758, 'on', 'foo']],
# dtype=object)
Statistische Übersicht
Eine Kurz-Analyse der Daten ist über die Methode describe()
des Dataframes
möglich. Man erhält als Ergebnis eine Übersicht über die jeweiligen Mittelwerte
sowie einige statistische Streuungsmaße (Standardabweichung, größter und
kleinster Wert, Quartile). Da sich diese Größen nur für quantitative (genauer:
invervall-skalierte) Merkmalswerte bestimmen lassen, werden die jeweiligen Werte
auch nur für die in Frage kommenden Spalten angezeigt:
# Statistische Kurz-Info anzeigen:
df2.describe()
# Ergebnis:
# A C D
# count 7 7.000000 7.000000
# mean 1 3.000000 0.167828
# std 0 2.160247 1.681872
# min 1 0.000000 -2.611072
# 25% 1 1.500000 -0.553813
# 50% 1 3.000000 0.630309
# 75% 1 4.500000 1.034107
# max 1 6.000000 2.194970
Sortiermethoden
Die Daten eines Dataframes können zudem wahlweise nach Zeilen oder Spalten oder auch anhand der jeweiligen Werte sortiert werden:
Mit der Methode
sort_index()
können die Daten nach Zeilen (axis=0
) oder Spalten (axis=1
) sortiert werden; mittelsascending=False
kann zudem die Reihenfolge der Sortierung umgekehrt werden.df2.sort_index(axis=1, ascending=False) # Ergebnis: # F E D C B A # 0 foo on -2.611072 0 2000-01-01 1 # 1 foo off 0.630309 1 2000-01-02 1 # 2 foo on -1.645430 2 2000-01-03 1 # 3 foo off 1.056535 3 2000-01-04 1 # 4 foo on 2.194970 4 2000-01-05 1 # 5 foo off 0.537804 5 2000-01-06 1 # 6 foo on 1.011678 6 2000-01-07 1
Wird zusätzlich das optionale Argument
inline=True
gesetzt, so wird nicht ein verändertes Resultat angezeigt (das beispielsweise in einer neuen Variablen gespeichert werden könnte); vielmehr wird in diesem Fall die Änderung auch im ursprünlichen Dataframe-Objekt übernommen.Mit der Methode
sort_value()
können die Daten ihrer Größe nach sortiert werden. Standardmäßig werden die Daten dabei zeilenweise (axis=0
) und in aufsteigender Reihenfolge (ascending=True
) sortiert; bei Bedarf können diese Variablen angepasst werden.df2.sort_values(by='D') # Ergebnis: # A B C D E F # 0 1 2000-01-01 0 -2.611072 on foo # 2 1 2000-01-03 2 -1.645430 on foo # 5 1 2000-01-06 5 0.537804 off foo # 1 1 2000-01-02 1 0.630309 off foo # 6 1 2000-01-07 6 1.011678 on foo # 3 1 2000-01-04 3 1.056535 off foo # 4 1 2000-01-05 4 2.194970 on foo
Auch bei dieser Sortiermethode können die Änderungen mittels
inline=True
nicht nur angezeigt, sondern direkt in den Original-Dataframe übernommen werden.
Daten auswählen¶
Dataframe-Objekte ähneln in gewisser Hinsicht dict
-Objekten: Die einzelnen
Spalten beziehungsweise Zeilen können mithilfe des Spalten- beziehungsweise
Index-Namens ausgewählt werden.
Ein Zugriff auf einzelne Zeilen oder Spalten ist beispielsweise mit Hilfe des
Index-Operators [ ]
möglich. Gibt man hierbei einen Spaltennamen oder eine
Liste mit Spaltennamen an, so werden die jeweiligen Spalten ausgewählt; gibt man
hingegen eine Zeilennummer oder einen Zeilenbereich an, so erhält man die
jeweilige(n) Zeile(n) als Ergebnis:
df2['B']
# Ergebnis:
# 0 2000-01-01
# 1 2000-01-02
# 2 2000-01-03
# 3 2000-01-04
# 4 2000-01-05
# 5 2000-01-06
# 6 2000-01-07
# Name: B, dtype: datetime64[ns]
df2[['B','D']]
# Ergebnis:
# B D
# 0 2000-01-01 -2.611072
# 1 2000-01-02 0.630309
# 2 2000-01-03 -1.645430
# 3 2000-01-04 1.056535
# 4 2000-01-05 2.194970
# 5 2000-01-06 0.537804
# 6 2000-01-07 1.011678
df2[1:3]
# A B C D E F
# 1 1 2000-01-02 1 0.630309 off foo
# 2 1 2000-01-03 2 -1.645430 on foo
Bei Bereichsangaben mittels Slicings ist wie gewöhnlich die untere Grenze im Bereich mit enthalten, die obere hingegen nicht.
Selektion mittels Labeln
Um auf einzelne Elemente eines Dataframes zugreifen zu können, muss sowohl eine
Zeilen- wie auch eine Reihenauswahl möglich sein. Für Dataframes ist dafür unter
anderem der .loc[]
-Operator definiert, mit dem eine Zeilen- beziehungsweise
Spaltenauswahl anhand der index
- beziehungsweise columns
-Bezeichnungen
möglich ist. Die Syntax lautet hierbei
dataframe.loc[zeilenbereich,spaltenbereich]
, wobei für die Bereichsangaben
sowohl einzelne Index-Werte, Werte-Listen oder auch Slicings erlaubt sind; eine
Bereichs-Angabe von :
bewirkt, dass der gesamte Zeilen- beziehungsweise
Spaltenbereich ausgewählt werden soll.
Beispiel:
# df2.loc[1:3, ['B','D']]
# B D
# 1 2000-01-02 0.630309
# 2 2000-01-03 -1.645430
# 3 2000-01-04 1.056535
Anders als beim gewöhnlichen Auswahloperator werden bei Benutzung des
.loc[]
-Operators bei Slicings beide Grenzen zum Bereich dazugerechnet.
Möchte man nur einen einzelnen Wert auswählen, als Resultat also einen Skalar
erhalten, so kann mit gleicher Syntax auch der .at[]
-Operator verwendet
werden, der für diese Aufgabe eine geringere Rechenzeit benötigt.
Selektion mittels Positionsangaben
Ein zweiter Auswahl-Operator für Dataframes ist der .iloc[]
-Operator. Das
„i“ steht dabei für „integer“ und soll darauf hinweisen, dass dieser Auswahl
sowohl für die Angabe des Zeilen- wie auch des Spaltenbereichs eine numerische
Positionsangabe erwartet. Wie bei einer Liste wird die erste Zeile
beziehungsweise Spalte eines Dataframes intern mit 0
, die zweite mit 1
,
usw. nummeriert, unabhängig von den index
- beziehungsweise
columns
-Bezeichnungen. Die Syntax für den .iloc
-Operator lautet also
dataframe.iloc[zeilenbereich,spaltenbereich]
, wobei wiederum einzelne Werte,
Werte-Listen oder auch Slicings zur Angabe der Positionen erlaubt sind:
Beispiel:
# df2.iloc[1:3,[1,3]]
# B D
# 1 2000-01-02 0.630309
# 2 2000-01-03 -1.645430
# 3 2000-01-04 1.056535
Auch beim .loc[]
-Operator werden bei Slicings beide Grenzen zum Bereich
dazugerechnet.
Möchte man nur einen einzelnen Wert auswählen, als Resultat also einen Skalar
erhalten, so kann mit gleicher Syntax auch der .iat[]
-Operator verwendet
werden, der für diese Aufgabe eine geringere Rechenzeit benötigt.
Eine Mischung zwischen dem .loc[]
und dem .iloc[]
-Operator stellt der
.ix[]
-Operator dar: Dieser versucht anhand der angegebenen Bereiche –
ebenso wie der .loc[]
-Operator – zunächst eine Auswahl anhand der
index
- beziehungsweise columns
-Werte zu erreichen; ist dies allerdings
nicht möglich, so versucht dieser Operator anschließend die fehlgeschlagene
Bereichsauswahl wie der .iloc[]
-Operator als Positionsangabe zu deuten.
Selektion mittels Bedingungen
Oftmals interessiert man sich nur für eine Teilmenge eines Dataframes, deren Daten bestimmte Bedingungen erfüllen; man weiß jedoch nicht unmittelbar, an welchen Stellen im Dataframe diese Daten abgelegt sind. Eine schnelle und elegante Methode für eine derartige Datenauswahl besteht darin, die obigen Auswahl-Operatoren mit der jeweiligen Bedingung anstelle einer Bereichsangabe zu verwenden.
Bei der Formulierung der Auswahl-Bedingungen kann genutzt werden, dass man bei der Anwendung von von Vergleichsoperatoren auf Dataframes boolesche Werte erhält:
Beispiel:
df2['D'] > 1
# 0 False
# 1 False
# 2 False
# 3 True
# 4 True
# 5 False
# 6 True
# Name: D, dtype: bool
Anstelle der obigen Syntax kann auch df['D'].gt(0)
geschrieben werden, wobei
gt()
für „greater than“ steht. Diese und ähnliche Methoden gibt es sowohl
für (mehrdimensionale) Dataframes als auch für (eindimensionale) Series-Objekte;
ihr Vorteil besteht darin, dass sie sich verketten lassen. Beispielsweise
liefert so df2['D'].gt(0).lt(2)
den booleschen Wert True
für alle Werte,
die größer als 0
und kleiner als 2
sind.
Boolesche Methode | Bedeutung |
gt() |
Größer als |
lt() |
Kleiner als |
ge() |
Größer gleich |
le() |
Kleiner gleich |
eq() |
Gleich |
Ein Series-Objekt mit booleschen Werten, wie man sie im obigen Beispiel erhalten hat, kann wiederum als Bereichsangabe für die oben genannten Auswahl-Operatoren genutzt werden:
df2[ df2['D'] > 1 ]
# Ergebnis:
# A B C D E F
# 3 1 2000-01-04 00:00:00 3 1.05654 off foo
# 4 1 2000-01-05 00:00:00 4 2.19497 on foo
# 6 1 2000-01-07 00:00:00 6 1.01168 on foo
df2.ix[ df2['D'] > 1, 'B' ]
# Ergebnis:
# 3 2000-01-04
# 4 2000-01-05
# 6 2000-01-07
# Name: B, dtype: datetime64[ns]
Durch die oben genannten Auswahl-Operatoren werden die ursprünglichen Dataframes nicht beeinflusst; man kann die Ergebnisse allerdings wiederum in extra Variablen ablegen und/oder erneut Auswahl-Operatoren auf die Resultate anwenden.
… to be continued …
Anmerkungen:
[1] | Die Index-Liste kann auch bereits bei der Erzeugung eines neuen
Series-Objekts mittels Series(datenliste, index=indexliste) angegeben
werden. |