8.1 Series und DataFrame#

Einfache Listen reichen nicht aus, um größere Datenmengen oder Tabellen effizient zu speichern. Dazu benutzen Data Scientists die Datentypen Series oder DataFrame aus dem Modul Pandas. Daher werden wir uns in diesem Kapitel mit diesen beiden Datentypen beschäftigen. Darüber hinaus lernen wir das häufig verwendete Datenformat csv kennen.

Lernziele#

Lernziele

  • Sie können Pandas mit der üblichen Abkürzung pd importieren.

  • Sie können aus einer Liste das Datenobjekt Series erzeugen.

  • Sie kennen das CSV-Dateiformat.

  • Sie können eine csv-Datei mit read_csv() einlesen.

  • Sie konnen mit .info() sich einen Überblick über die importierten Daten verschaffen.

Import von pandas#

Pandas ist eine Bibliothek zur Verarbeitung und Analyse von Daten in Form von Datenreihen und Tabellen. Die beiden grundlegenden Datenstrukturen sind Series und DataFrame. Dabei wird Series für Datenreihen genommen, also sozusagen die Verallgemeinerung von Vektoren bzw. eindimensionalen Arrays. Der Datentyp DataFrame repräsentiert Tabellen, also sozusagen Matrizen bzw. verallgemeinerte zweidimensionale Arrays.

Um das Modul pandas benutzen zu können, müssen wir es zunächst importieren. Es ist üblich, dabei dem Modul die Abkürzung pd zu geben, damit wir nicht immer pandas schreiben müssen, wenn wir eine Funktion aus dem pandas-Modul benutzen.

import pandas as pd # kürze das Modul pandas als pd ab, um Schreibarbeit zu sparen

Series aus Liste erzeugen#

Der Datentyp Series speichert Datenreihen. Liegt beispielsweise eine Reihe von Daten vor, die in einer Variable vom Datentyp Liste gespeichert ist, so wird über die Methode pd.Series(liste) ein neues Series-Objekt erzeugt, dass die Listenelemente enthält. Im folgenden Beispiel haben wir Altersangaben in einer Liste, also [25, 22, 43, 37] und initialisieren über pd.Series() die Variable alter:

alter = pd.Series([25, 22, 43, 37])
print(alter)
0    25
1    22
2    43
3    37
dtype: int64

Was ist aber jetzt der Vorteil von Pandas? Warum nicht einfach bei der Liste bleiben oder aber, wenn Performance wichtig sein sollte, ein eindimensionales Numpy-Array nehmen? Der wichtigste Unterschied ist der Index.

Bei einer Liste oder einem Numpy-Array ist der Index implizit definiert. Damit ist gemeint, dass bei der Initialisierung automatisch ein Index 0, 1, 2, 3, … angelegt wird. Wenn bei einer Liste l = [25, 22, 43, 37] auf das zweite Element zugegriffen werden soll, dann verwenden wir den Index 1 (zur Erinnerung: Python zählt ab 0) und schreiben

l = [25, 22, 43, 37]
print("2. Element der Liste: ", l[1])
2. Element der Liste:  22

Die Datenstruktur Series ermöglich es aber, einen expliziten Index zu setzen. Über den optionalen Parameter index= speichern wir als Zusatzinformation noch ab, von welcher Person das Alter abgefragt wurde. In dem Fall sind es die vier Personen Alice, Bob, Charlie und Dora.

alter = pd.Series([25, 22, 43, 30], index=["Alice", "Bob", "Charlie", "Dora"])
print(alter)
Alice      25
Bob        22
Charlie    43
Dora       30
dtype: int64

Jetzt ist auch klar, warum beim ersten Mal, als wir print(alter) ausgeführt haben, die Zahlen 0, 1, 2, 3 ausgegeben wurden. Zu dem Zeitpunkt hatte das Series-Objekt noch einen impliziten Index wie eine Liste. Was noch an Informationen ausgegeben wird, ist das Attribut dtype. Darin gespeichert ist der Datentyp der gespeicherten Werte. Auf dieses Attribut kann auch direkt mit dem Punktoperator zugegegriffen werden.

print(alter.dtype)
int64

Offensichtlich sind die gespeicherten Werte Integer.

Mini-Übung

Erzeugen Sie ein Series-Objekt mit den Wochentagen als Index und der Anzahl der Vorlesungs/Übungs-Stunden an diesem Wochentag.

# Hier Ihr Code:

Lösung

stundenplan = pd.Series([4, 0, 4, 6, 8], index=["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag"])
print(stundenplan)

DataFrame für Tabellen#

Bei Auswertung von Messungen ist aber der häufigste Fall der, dass Daten in Form einer Tabelle vorliegen. Ein DataFrame-Objekt entspricht einer Tabelle, wie man sie beispielsweise von Excel, LibreOffice oder Numbers kennt. Sowohl Zeile als auch Spalten sind indiziert. Typischerweise werden die Daten in der Tabelle zeilenweise angeordnet. Damit ist gemeint, dass jede Zeile einen Datensatz darstellt und die Spalten die Eigenschaften speichern.

Ein DataFrame kann direkt über mehrere Pandas-Series-Objekte oder verschachtelte Listen erzeugt werden. Da es in der Praxis nur selten vorkommt und nur für sehr kleine Datenmengen praktikabel ist, Daten händisch zu erfassen, fokussieren wir gleich auf die Erzeugung von DataFrame-Objekten aus einer Datei.

Import von Tabellen#

Tabellen liegen werden oft in dem Dateiformat abgespeichert, das die jeweilige Tabellenkalkulationssoftware Excel, Numbers oder OpenOfficeCalc als Standard eingestellt hat. Wir betrachten in dieser Vorlesung aber primär Tabellen, die in einem offenen Standardformat vorliegen und damit unabhängig von der verwendeten Software und dem verwendeten Betriebssystem sind. Der Import von Excel wird kurz gestreift.

Import von Tabellen im CSV-Format#

Das Dateiformat CSV speichert Daten zeilenweise ab. Dabei steht CSV für “comma separated value”. Die Trennung der Spalten erfolgt durch ein Trennzeichen, normalerweise durch das Komma. Im deutschsprachigen Raum wird gelegentlich ein Semikolon verwendet, weil Dezimalzahlen das Komma zum Abtrennen der Nacchkommastellen verwenden.

Um Tabellen im csv-Format einzulesen, bietet Pandas eine eigene Funktion namens read_csv an (siehe Dokumentation/read_csv). Wird diese Funktion verwendet, um die Daten zu importieren, so wird automatisch ein DataFrame-Objekt erzeugt. Beim Aufruf der Funktion wird der Dateiname übergeben, aber beispielweise könnte auch ein anderes Trennzeichen eingestellt werden.

Am besten sehen wir uns die Funktionsweise von read_csv an einem Beispiel an. Sollten Sie mit einem lokalen JupyterNotebook arbeiten, laden Sie bitte die Datei bundesliga_top7_offensive.csv herunter und speichern Sie sie in denselben Ordner, in dem auch dieses JupyterNotebook liegt. Die csv-Datei stammt von Kaggle. Wie der Name schon verrät, sind darin Spielerdaten zu den Top7-Fußballvereinen der Bundesligasaison 2020/21 enthalten.

Führen Sie dann anschließend die folgende Code-Zelle aus.

import pandas as pd
data = pd.read_csv('bundesliga_top7_offensive.csv')

Es erscheint keine Fehlermeldung, aber den Inhalt der geladenen Datei sehen wir trotzdem nicht. Dazu verwenden wir die Methode .head().

data.head()
Name Club Nationality Position Age Matches Starts Mins Goals Assists Penalty_Goals Penalty_Attempted xG xA Yellow_Cards Red_Cards
0 Manuel Neuer Bayern Munich GER GK 34 33 33 2970 0 0 0 0 0.00 0.01 1 0
1 Thomas Müller Bayern Munich GER MF 30 32 31 2674 11 19 1 1 0.24 0.39 0 0
2 David Alaba Bayern Munich AUT DF,MF 28 32 30 2675 2 4 0 0 0.04 0.08 4 0
3 Jérôme Boateng Bayern Munich GER DF 31 29 29 2368 1 1 0 0 0.01 0.02 6 0
4 Robert Lewandowski Bayern Munich POL FW 31 29 28 2458 41 7 8 9 1.16 0.13 4 0

Die Methode .head() zeigt uns die ersten fünf Zeilen der Tabelle an. Wenn wir beispielsweise die ersten 10 Zeilen anzeigen lassen wollen, so verwenden wir die Methode .head(10)mit dem Argument 10.

data.head(10)
Name Club Nationality Position Age Matches Starts Mins Goals Assists Penalty_Goals Penalty_Attempted xG xA Yellow_Cards Red_Cards
0 Manuel Neuer Bayern Munich GER GK 34 33 33 2970 0 0 0 0 0.00 0.01 1 0
1 Thomas Müller Bayern Munich GER MF 30 32 31 2674 11 19 1 1 0.24 0.39 0 0
2 David Alaba Bayern Munich AUT DF,MF 28 32 30 2675 2 4 0 0 0.04 0.08 4 0
3 Jérôme Boateng Bayern Munich GER DF 31 29 29 2368 1 1 0 0 0.01 0.02 6 0
4 Robert Lewandowski Bayern Munich POL FW 31 29 28 2458 41 7 8 9 1.16 0.13 4 0
5 Joshua Kimmich Bayern Munich GER MF 25 27 25 2194 4 10 0 0 0.10 0.27 4 0
6 Kingsley Coman Bayern Munich FRA FW,MF 24 29 23 1752 5 10 0 0 0.21 0.34 1 0
7 Benjamin Pavard Bayern Munich FRA DF 24 24 22 1943 0 0 0 0 0.02 0.09 3 0
8 Alphonso Davies Bayern Munich CAN DF 19 23 22 1763 1 2 0 0 0.01 0.04 2 1
9 Serge Gnabry Bayern Munich GER FW,MF 25 27 20 1644 10 2 0 0 0.44 0.25 4 0

Offensichtlich wurde beim Import der Daten wieder ein impliziter Index 0, 1, 2, usw. gesetzt. Das ist nicht weiter verwunderlich, denn Pandas kann nicht wissen, welche Spalte wir als Index vorgesehen haben. Und manchmal ist ein automatisch erzeugter impliziter Index auch nicht schlecht. In diesem Fall würden wir aber gerne als Zeilenindex die Namen der Spieler verwenden. Daher modifizieren wir den Befehl mit index_col=. Die Namen stehen in der 1. Spalte, was in Python-Zählweise einer 0 entspricht.

data = pd.read_csv('bundesliga_top7_offensive.csv', index_col=0)
data.head(10)
Club Nationality Position Age Matches Starts Mins Goals Assists Penalty_Goals Penalty_Attempted xG xA Yellow_Cards Red_Cards
Name
Manuel Neuer Bayern Munich GER GK 34 33 33 2970 0 0 0 0 0.00 0.01 1 0
Thomas Müller Bayern Munich GER MF 30 32 31 2674 11 19 1 1 0.24 0.39 0 0
David Alaba Bayern Munich AUT DF,MF 28 32 30 2675 2 4 0 0 0.04 0.08 4 0
Jérôme Boateng Bayern Munich GER DF 31 29 29 2368 1 1 0 0 0.01 0.02 6 0
Robert Lewandowski Bayern Munich POL FW 31 29 28 2458 41 7 8 9 1.16 0.13 4 0
Joshua Kimmich Bayern Munich GER MF 25 27 25 2194 4 10 0 0 0.10 0.27 4 0
Kingsley Coman Bayern Munich FRA FW,MF 24 29 23 1752 5 10 0 0 0.21 0.34 1 0
Benjamin Pavard Bayern Munich FRA DF 24 24 22 1943 0 0 0 0 0.02 0.09 3 0
Alphonso Davies Bayern Munich CAN DF 19 23 22 1763 1 2 0 0 0.01 0.04 2 1
Serge Gnabry Bayern Munich GER FW,MF 25 27 20 1644 10 2 0 0 0.44 0.25 4 0

Import von Tabellen im xlsx-Format#

Eine sehr bekannte Tabellenkalkulationssoftware ist Excel von Microsoft. Excel bringt sein eigenens proprietäres Datenformat mit, in der Regel erkennbar an der Dateiendung .xlsx. Laden Sie sich den Datensatz zu den Top7-Bundesligavereinen als Excel-Datei bundesliga_top7_offensive.xlsx herunter.

data = pd.read_excel('bundesliga_top7_offensive.xlsx', index_col=0)
data.head(5)
Club Nationality Position Age Matches Starts Mins Goals Assists Penalty_Goals Penalty_Attempted xG xA Yellow_Cards Red_Cards
Name
Manuel Neuer Bayern Munich GER GK 34 33 33 2970 0 0 0 0 0.00 0.01 1 0
Thomas Müller Bayern Munich GER MF 30 32 31 2674 11 19 1 1 0.24 0.39 0 0
David Alaba Bayern Munich AUT DF,MF 28 32 30 2675 2 4 0 0 0.04 0.08 4 0
Jérôme Boateng Bayern Munich GER DF 31 29 29 2368 1 1 0 0 0.01 0.02 6 0
Robert Lewandowski Bayern Munich POL FW 31 29 28 2458 41 7 8 9 1.16 0.13 4 0

Vermutlich erhalten Sie zunächst eine Fehlermeldung: Missing optional dependency 'openpyxl'.  Use pip or conda to install openpyxl. Falls das der Fall sein sollte und Sie interessiert daran sind, Excel-Dateien lesen und schreiben zu können, installieren Sie bitte das Modul openpyxl mit !conda install openpyxl oder !pip install openpyxl nach. In dieser Vorlesung verwenden wir nur CSV-Dateien, so dass ein Nachinstallieren für die Vorlesung/Übung nicht notwendig ist.

Übersicht verschaffen mit info#

Das obige Beispiel zeigt uns zwar nun die ersten 10 Zeilen des importierten Datensatzes, aber wie viele Daten insgesamt enthalten sind oder welche Vereine noch kommen, können wir mit der .head()-Methode nicht erfassen. Dafür stellt Pandas die Methode .info() zur Verfügung. Probieren wir es einfach aus.

data.info()
<class 'pandas.core.frame.DataFrame'>
Index: 177 entries, Manuel Neuer to Joshua Mees
Data columns (total 15 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   Club               177 non-null    object 
 1   Nationality        177 non-null    object 
 2   Position           177 non-null    object 
 3   Age                177 non-null    int64  
 4   Matches            177 non-null    int64  
 5   Starts             177 non-null    int64  
 6   Mins               177 non-null    int64  
 7   Goals              177 non-null    int64  
 8   Assists            177 non-null    int64  
 9   Penalty_Goals      177 non-null    int64  
 10  Penalty_Attempted  177 non-null    int64  
 11  xG                 177 non-null    float64
 12  xA                 177 non-null    float64
 13  Yellow_Cards       177 non-null    int64  
 14  Red_Cards          177 non-null    int64  
dtypes: float64(2), int64(10), object(3)
memory usage: 22.1+ KB

Mit .info() erhalten wir eine Übersicht, wie viele Spalten es gibt und auch die Spaltenüberschriften werden aufgelistet. Dabei sind Überschriften wie Name selbsterklärend, aber was xG bedeutet, erschließt sich nicht von selbst. Dazu brauchen wir mehr Informationen von den Autor:innen der Daten.

Weiterhin entnehmen wir der Ausgabe von .info(), dass in jeder Spalte 177 Einträge sind, die ‘non-null’ sind. Damit ist gemeint, dass diese Zellen beim Import nicht leer waren. Zudem wird bei jeder Spalte noch der Datentyp angegeben. Für die Namen, die als Strings gespeichert sind, wird der allgemeine Datentyp ‘object’ angegeben. Beim Alter/Age wurden korrektweise Integer erkannt und die mittlere erwartete Anzahl von Toren pro Spiel ‘xG’ (= expected number of goals from the player in a match) wird als Float angegeben.