Pandas stellt dem Anwender zwei Datenobjekte zur Verfügung, die das Verarbeiten von Vektoren und Matrizen sehr einfach machen: Series und DataFrame. Series ist eine eindimensionale Datenstruktur, die Daten jedes Typs enthalten kann (int, str, float, object usw.). Die Feldinformationen über die Series werden als 'index' bezeichnet und bei der Erzeugung oder danach angegeben:
s = pd.Series(data, index=index)
data kann ein Python dictionary, eine Python Liste, ein numpy array oder ein einzelner Wert (scalar) sein.
In [31]:
%matplotlib inline
#generisches import statement
import pandas as pd
#Erzeugen einer Series, Feldbezeichner werden über index gesetzt
d = [21,22,23,24,25]
s = pd.Series(d, index=["a","b","c","d","e"])
s
Out[31]:
In [32]:
#hier werden die Feldbezeichner als keys verwendet
d = dict(a=21,b=22,c=23,d=24,e=25)
s = pd.Series(d)
s
Out[32]:
In [33]:
import numpy as np
d = np.array([21,22,23,24,25])
s = pd.Series(d, index=["a","b","c","d","e"])
s
Out[33]:
Wie Sie sehen, hat eine Serie immer einen Datentyp. Wenn sie Daten mit verschiedenen Datentypen in eine Serie aufnehmen, versucht die Bibliothek damit intelligent umzugehen:
In [34]:
#kleinster gemeinsamer Nenner = float
a = [3,3.1]
d = pd.Series(a)
d
Out[34]:
In [35]:
#kleinster gemeinsamer Nenner: Objekt
#Achtung - damit sie können sie viele Operationen nicht mehr oder nicht sinnvoll ausführen!
a = ["a",3,4.2]
d = pd.Series(a)
d
Out[35]:
Noch mehr Metadaten: Sie können die Daten auch insgesamt benamsen. Hier nennen wir sie 'count':
In [36]:
d = [21,22,23,24,25]
s = pd.Series(d, name="count", index=["a","b","c","d","e"])
s
Out[36]:
Wir können den Inhalt einer Serie mit der Funktion .to_csv(dateinamen) in eine Datei schreiben. Mit dem keyword 'header' werden auch die Metadaten, in diesem Fall der Inhalt von name, in die Datei geschrieben.
In [37]:
s.to_csv("series.csv", header=True)
Werfen wir einen Blick in die Datei:
In [38]:
print(open("series.csv").read())
Das Lesen von Serien ist nicht ganz intuitiv, da man in pandas einen generischen Befehl für das Lesen von csv-Dateien hat, der normalerweise einen DataFrame zurückgibt und dem man mit squeeze=True sagt, dass das Ergebnis eine Serie ist. Außerdem muss man mit index_col kommunizieren, dass die erste Spalte als Index verwendet werden soll.
In [39]:
s = pd.read_csv("series.csv", squeeze=True, index_col=0)
s
Out[39]:
Erzeugen Sie eine Serie mit den Werten 1-20. index = a,b,c usw. Schreiben Sie das Ergebnis in eine Datei und lesen Sie die Datei dann wieder ein.
Das Auswählen von Daten (Slicen) funktioniert erst einmal ebenso wie bei numpy, d.h. wir können einfach mit der Position auf die Daten zugreifen:
In [40]:
s[:3]
Out[40]:
Außerdem können wir aber auch den Index dafür verwenden:
In [41]:
s["a"]
Out[41]:
In [42]:
s["a":"c"]
Out[42]:
Man kann jede Folge von Booleans als Selektionsindex verwenden.
In [43]:
s[[True, False, True, False, True]]
Out[43]:
Daher kann man auch jeden Ausdruck an dieser Stelle verwenden, der eine Folge von Wahrheitswerten erzeugt (die Anzahl der Wahrheitswerte muss der Anzahl der Elemente in der Serie entsprechen). Wir können also z.B. eine Bedingung so formulieren:
In [44]:
s > 23
Out[44]:
Hier wird die Bedingung auf jedes Element der Serie angewandt und das Ergebnis ausgegeben. Eine solche Formulierung können wir wiederum als Index verwenden:
In [45]:
s[s > 23]
Out[45]:
Ebenso üblich ist es, den Index des Elements als Selektionskriterium zu geben.
In [53]:
s[[0, 2, 4]]
Out[53]:
Auf diese Weise kann man aus einer Serie mit Werten randomisiert auswählen. Wenn ich aus der Serie d 3 Elemente zufällig auswählen will, dann erzeuge ich erst eine Liste aller Positionen in der Serie von 0 bis 4, dann wähle ich zufällig drei Zahlen aus der Liste und verwende diese dann als Index um so die Auswahl zu treffen.
In [54]:
import random
#create list of 0 to len(s)-1
index_of_list = range(len(s))
#random.sample(population, nr_of_elements)
indx = random.sample(index_of_list, 3)
s[indx]
Out[54]:
Serien verhalten sich wie mathematische Vektoren, d.h. man kann sie mit Skalaren (= einfache Zahlen) oder anderen Vektoren, die die gleiche Länge haben, multiplizieren.
In [55]:
#die Metadaten bleiben in der Ausgabe erhalten!
s * 3
Out[55]:
In [59]:
#Normalisierung, d.h. jedes Element der Serie wird durch die Gesamtsumme geteilt.
#Die Summe aller Elemente ergibt 1
x = s / s.sum()
print(x)
print("Summe aller Werte in der normalisierten Serie: ", x.sum())
In [61]:
#Multiplikation gleichlanger Vektoren
a = pd.Series([2,3,1,5,5])
b = pd.Series([3,4,1,5,6])
a * b
Out[61]:
In [62]:
#ACHTUNG
s * a
Out[62]:
In [64]:
#nur die Daten mit den gleichen Metadaten werden wie erwartet verarbeitet
c = pd.Series([2,5,1,3,5],["a","b","c","d","e"])
s * c
Out[64]:
In [65]:
s + c
Out[65]:
Außerdem können wir die statistischen Methoden aus numpy verwenden oder auch Pandas eigene
In [67]:
print(np.mean(s))
s.mean()
Out[67]:
Ein DataFrame ist eine zweidimensionale Datenstruktur mit Spalten, die unterschiedliche Datentypen haben können.
Wir schauen uns zuerst an, wie man DataFrames erstellt, auch wenn das in der Datenanalyse gar nicht so häufig vorkommt, da man zumeist irgendwo erzeugte Daten aus Dateien einliest.
Es mehrere Wege ein DataFrame zu erstellen.
In [70]:
#aus einem zwei dimensionalen array oder einer vergleichbaren numpy-Datenstruktur
#Zeilen (=index) und Spalten (=columns) Informationen werden dann über entsprechende Parameter gesetzt
data = pd.DataFrame([[2,6,3,4],[3,3,1,7]],index=["text 1", "text 2"], columns=["a","b","c","d"])
data
Out[70]:
In [71]:
#aus einer Liste von dictionaries
data = pd.DataFrame([dict(a=2,b=6,c=3,d=4),dict(a=2,b=6,c=3,d=4)],index=["text 1", "text 2"])
data
Out[71]:
In [81]:
import pandas as pd
#1. aus Serien'
a = pd.Series([2,6,3,4],["a", "b", "c", "d"], name="text 1")
b = pd.Series([3,3,1,7],["a", "b", "c", "d"], name="text 2")
c = pd.Series([1,7,2,2],["a", "b", "c", "d"], name="text 3")
d = pd.Series([5,6,8,1],["a", "b", "c", "d"], name="text 4")
df = pd.DataFrame([a, b, c, d])
df
Out[81]:
Beim Erstellen des DataFrames aus Serien kann man die besondere Leistungsfähigkeit der Metadaten für seine Zwecke nutzen. Im folgenden haben einige Serien nicht Instanzen aller Felder; achten Sie darauf, wie pandas damit umgeht:
In [82]:
a = pd.Series([2,6,4],["a","c","d"], name="text 1")
b = pd.Series([3,1,7],["a","b","e"], name="text 2")
c = pd.Series([1,7,2,2],["b","c","d","e"], name="text 3")
data = pd.DataFrame([a,b,c])
data
Out[82]:
Wie Sie sehen, werden die Serien automatisch aligniert, so dass alle Werte mit dem Namen 'a' in die entsprechende Spalte kommen. Fehlende Daten werden auf NaN (Not a Number) gesetzt.
Tokenisieren Sie 4 beliebige Erzähltexte, zählen Sie die Worte und erzeugen Sie daraus ein DataFrame, dass als Spaltennamen die Worte und als Zeilennamen die Dateinamen (=Textnamen) hat.
DataFrame kann man als ein Set von Dictionaries ansprechen, deren Daten gleich aufgebaut sind. Ich kann den key zur Auswahl des Dictionaries oder auch zur Erzeugung verwenden. Der key bezieht sich auf den Spaltennamen:
In [85]:
print(df)
df["a"]
Out[85]:
Man kann mit der gleichen Syntax auf die Zeilen zugreifen, aber davon rate ich ab, da das zu Verwirrung führt. Sehr viel klarer ist das System, Zeilen mit iloc bzw. loc anzuspreche. DataFrame stellt nämlich eine sehr mächtige Syntax zur Auswahl der Daten zur Verfügung. Hier funktioniert der Zugriff über spezifische Attribute der Objekte Series bzw. DataFrame:
df.loc[sel] - wählt Zeilen und Spalten über ihren Namen aus. Wird der Name nicht gefunden, gibt es einen Fehler.
df.iloc[sel] - wählt Zeilen und Spalten über die Position (als Zahl) aus - (i)ndex (loc)ation.
sel - ist das Auswahlkriterium. Es können Einzelwerte, Slices oder auch Listen übergeben werden. Achtung: Im fall von loc wird auch der Endwert in die ausgewählten Daten eingeschlossen! Funktioniert also anders als Python-Arrays. iloc dagegen funktioniert wie Arrays.
Die Syntax von sel bei DataFrames: [row,column]. Lässt man die Angaben zu row oder column weg, wird angenommen, sie lautet :
Die Rückgabewerte dieser ganzen Operationen sind entweder Serien oder DataFrames
Der folgende Befehl zeigt die Zeilen von 1 bis (aber ausschließlich) 3
In [96]:
df.iloc[1:3]
Out[96]:
Man kann mit iloc die gleiche Syntax verwenden, die wir oben bei den Serien diskutiert haben. Z.B. eine Reihe von Wahrheitswerten:
In [97]:
df.iloc[[True, False, True, False]]
Out[97]:
Außerdem kann man wiederum die Zeilennummer als Index verwenden.
In [98]:
df.iloc[[0, 2]]
Out[98]:
Man kann den Aufruf von Spalten- und Zeilenangaben kombinieren. Auf auf die Spalten wird mit ihrer Position verwiesen
In [119]:
df.iloc[:,0]
Out[119]:
In [120]:
df.shape
Out[120]:
In [123]:
df.iloc[:,1:3]
Out[123]:
In [128]:
df.iloc[[1,3], [1, 3]]
Out[128]:
In [129]:
df.iloc[1:3,2:3]
Out[129]:
Im Fall von 'loc' wählen wir Zeilen über den Indexnamen aus.
In [104]:
df.loc["text 1"]
Out[104]:
Die Auswahl mit 'loc' funktioniert sehr ähnlich, aber in einigen Details doch etwas anders als mit iloc. Erster Unterschied: Wenn ich eine Sequenz mit Anfangspunkt und Endpunkt definiere, dann ist der Endpunkt inbegriffen:
In [106]:
df.loc["text 1":"text 3"]
Out[106]:
Der zweite Unterschied besteht darin, dass ich die Auswahl von Spalten gleich in den Ausdruck aufnehmen kann, einfach durch ein Komma abgetrennt. Im Folgenden wähle ich die Zeilen von 'text 1' bis 'text 3', aber nur die Spalte c.
In [107]:
df.loc['text 1':'text 3', 'c']
Out[107]:
Und auch bei der Angabe von Spalten kann ich Spannen, bzw. Listen verwenden
In [112]:
df.loc['text 1':'text 3','b':'d']
Out[112]:
In [113]:
df.loc['text 1':'text 3',['b','d']]
Out[113]:
Alle Zeilen mit ausgewählten Spalten
In [130]:
df.loc[:,'b':'d']
Out[130]:
Erzeugen Sei folgendes Dataframe: 3 Zeilen (label: text 1, usw) sowie 7 Spalten (labels a, b, usw) mit beliebigen Werten. Selektieren Sie nun:
Aber noch einmal zurück zu den Spalten. Wenn man Daten Spalten zuweist, die es noch nicht gibt, werden diese eingerichtet. Achten Sie darauf, wie Pandas NaN-Felder einfach ignoriert:
In [132]:
data["a Quadrat"] = data["a"]**2
data
Out[132]:
Wenn der Name der Spalte ein gültiger Pythonidentifier ist, kann man ihn übrigens direkt verwenden. Im folgenden adressieren wir die Spalte, die den Namen 'a' hat => data["a"]
In [133]:
data.a
Out[133]:
Eine der wichtigsten Anwendungen von DataFrames ist die Auswahl bestimmter Daten aufgrund von bestimmten Werten. Das entspricht ungefähr dem select-Befehl in SQL. Das ist insgesamt ein sehr umfangreiches Feld, und wir schauen uns nur einige Grundbegriffe davon an. Im Grunde geht es, wie oben schon einmal erwähnt, zumeist darum, dass man eine Liste von True/False-Werten aufgrund der Auswahlbedingungen erzeugt und diese dann als Index für die Serie / den DataFrame verwendet.
In [134]:
df
Out[134]:
In [138]:
df.a >= 3
Out[138]:
In [140]:
df[df.a >= 3]
Out[140]:
Wenn wir nur die Spalte 'b' haben wollen, dann könnte man das so schreiben:
In [141]:
df[df.a >=3]['b']
Out[141]:
Das funktioniert, wird aber in komplexeren Anwendungen fast immer zu schwer durchschaubaren Fehlern führen. Deshalb sollte man hier immer mit 'loc' arbeiten (Ausführliche Erklärung)
In [142]:
df.loc[df.a >= 3, 'b']
Out[142]:
Das Folgende zeigt, wie man Bedingungen für Werte in zwei Spalten formulieren kann. Beachten Sie:
In [148]:
df.loc[(df.d > 3) & (df.c < 4)]
Out[148]:
Wenn wir mit DataFrames arbeiten, müssen wir uns wieder die Axen-Orientierung vor Augen halten, die wir schon aus numpy kennen:
Wenn wir Methoden und Funktionen broadcasten (funktioniert mit den pandas eigenen Methoden sowie mit numpy Methoden), müssen wir die Achsenorientierung beachten. Der Default-Wert ist zumeist axis=0:
In [149]:
df
Out[149]:
In [150]:
#identisch mit data.mean(axis=0)
df.mean()
Out[150]:
In [151]:
df.mean(axis=1)
Out[151]:
In [152]:
df["a"].mean()
Out[152]:
In [153]:
df.loc["text 2"].mean()
Out[153]:
Oft ist es sinnvoll, fehlende Werte durch einen Wert zu ersetzen, z.B. 0 (man kann auch einen beliebigen anderen Wert nehmen, der sinnvoll ist):
In [154]:
print(data)
data = data.fillna(0)
data
Out[154]:
In [155]:
data = data.drop("a Quadrat", axis=1)
data
Out[155]:
Broadcasting ist ausgesprochen mächtig und erlaubt es komplexe Vorgänge sehr kompakt auszudrücken. Wenn wir unsere Daten standardisieren wollen, d.h. sie sollen den Mittelwert 0 haben und eine Standardabweichung von 1, dann muss man von jedem Datenpunkt den Mittelwert abziehen und das Ergebnis durch die Standardabweichung teilen. Das kann man nun so ausdrücken:
In [156]:
(df - df.mean()) / df.std()
Out[156]:
Man kann sich die wichtigsten statistischen Beschreibungen für Daten ausgeben lassen (geht auch für Serien):
In [159]:
df.describe()
Out[159]:
In [160]:
data.to_csv("df.csv")
In [161]:
print(open("df.csv").read())
In [162]:
data = pd.read_csv("df.csv", index_col=0)
data
Out[162]:
In Serien und DataFrames kann man entweder die Daten oder den Index sortieren. Beginnen wir mit Serien und dem Sortieren von Daten. Voreingestellt ist das Sortieren innerhalb der gleichen Datenstruktur, d.h. es gibt keine Rückgabe, vielmehr wird die ursprüngliche Serie modifiziert. Verwendet wird das besonders bei großen Datenmengen sehr effektive quicksort, aber Sie können den Algorithmus selbst setzen. Außerdem können Sie bestimmen, ob aufsteigend sortiert werden soll oder nicht.
In [163]:
import pandas as pd
s = pd.Series([3,1,6,2,8,3])
s.sort_values()
s
Out[163]:
In [164]:
s.sort_values(ascending=False, kind="mergesort")
s
Out[164]:
Mit der Methode s.head(n) können wir uns die ersten n Werte der Serie ausgeben lassen:
In [165]:
s.sort_values().head(4)
Out[165]:
Falls es nur darum geht, die n größten oder kleinsten Werte zu erhalten, müssen wir die Serie gar nicht sortieren, sondern können die Methoden s.nsmallest(n) oder s.slargest(n) verwenden:
In [166]:
s.nlargest(4)
Out[166]:
Mit der Methode sort_index können wir den Index sortieren. Hier wird immer die sortierte Serie als Ergebnis zurückgegeben. Wir können ebenfalls das keyword 'ascending' verwenden.
In [167]:
s.sort_index()
Out[167]:
Diese Methode können wir auch mit DataFrames verwenden, um nach Zeilen- und Spaltenlabels sortieren zu lassen. Voreingestellt ist das Sortieren der Zeilenlabels (index). Erst einmal brauchen wir ein Dataframe:
In [168]:
data = pd.read_csv("df1.csv", index_col=0)
data
Out[168]:
Jetzt sortieren wir es aufgrund des Index (Achtung: Hier wird nicht inplace sortiert, sondern ein neuer DataFrame zurückgegeben):
In [169]:
data.sort_index()
Out[169]:
Durch das Hinzufügen der Axeninformation können wir die Spaltennamen sortieren:
In [170]:
data.sort_index(axis=1, ascending=False)
Out[170]:
Wir können eine Tabelle auch nach den Werten einer Spalte sortieren lassen. Dazu dient die Methode sort_values.
In [171]:
print(data)
data.sort_values(by="a")
Out[171]:
Sie können auch mehrere Spalten als Schlüssel verwenden, dann wird bei gleichen Werten in der ersten Spalte nach den Werten in der zweiten Spalte sortiert:
In [172]:
data.sort_values(by=["e","c"])
Out[172]:
In [173]:
pd.DataFrame(data, index=["yy","ww","zz","xx"])
Out[173]:
In [174]:
print(data)
data.iloc[0]
Out[174]:
In [175]:
data.sum(axis=1)
Out[175]:
In [176]:
#Shows
In [177]:
data.sum(axis=1).argsort()
Out[177]:
In [178]:
data.loc[data.sum(axis=1).sort_values(inplace=False, ascending=False).index]
Out[178]:
In [181]:
df = pd.DataFrame({
'nationality': ["usa", "usa", "usa", "usa", "germany", "germany", "germany", "germany"],
'gender':["m","f","f","m","f","m","m","f"],
'height':[183, 156,177,177,168,192,185,171],
'weight':[93, 56, 66, 75, 50, 102, 91, 68]
})
df
Out[181]:
In [182]:
print("Average height: {} cm\nAverage weight: {} kg".format(df["height"].mean(), df["weight"].mean()))
In [183]:
df.groupby("gender").mean()
Out[183]:
In [184]:
df.groupby("nationality").mean()
Out[184]:
Im folgenden erzeugen wir einen Plot
In [194]:
from matplotlib import pyplot as plt
df.groupby("nationality").boxplot()
plt.show()
In [195]:
gender = df.groupby("gender")
for group_name, group_data in gender:
print(group_name)
print(group_data)
print("---")
In [196]:
gender.get_group("f")
Out[196]:
Wir haben zwei Dataframes, die eine gemeinsame Spalte haben:
In [197]:
i1 = pd.Series(["a","b","c","d","g"], name="stadt")
i2 = pd.Series([3,5.5,22,2, 12], name="land")
d1 = pd.DataFrame([i1, i2])
d1 = d1.T
d1
Out[197]:
In [198]:
i1 = pd.Series(["a","b","c","d", "e", "f"], name="stadt")
i2 = pd.Series(["ich","du", "er", "sie", "uns", "wir"], name="pros")
d2 = pd.DataFrame([i1, i2])
d2 = d2.T
d2
Out[198]:
In [199]:
pd.merge(d1, d2, on="stadt", how="inner")
Out[199]:
In [200]:
pd.merge(d1, d2, on="stadt", how="outer")
Out[200]:
In [201]:
pd.merge(d1, d2, on="stadt", how="left")
Out[201]:
Neben den Tutorials in der Pandas-Dokumentation kann ich diese Video empfehlen, das besonders für diejenigen interessant sein wird, die sich schon ganz gut mit SQL auskennen: Greg Reda - Translating SQL to pandas. And back
Pandas Cheat Sheet by Marc Graph
In [ ]: