Zu Beginn ein kurzer Rückblick in unserem ersten Blogbeitrag zum Thema Data Science mit Python. Wir haben uns mit mit einigen grundlegenden Python-Werkzeugen auseinander gesetzt haben, die uns es ermöglicht, mit IPython oder auch mit Jupyter Notebooks sehr interaktiv zu arbeiten. In diesem Teil stellen wir Euch nun Möglichkeiten vor Zahlen und Variablen eine Struktur zu geben sowie Berechnungen von Array/Matrizen durchzuführen. Schauen wir uns also zuerst einmal an, welche Möglichkeiten uns ‘Out of the box’ zur Verfügung stehen.
Vorstellung von Datenstrukturen in Python
Um mehrere Objekte, diese können z.B. Zahlen, Zeichen, Wörter, Sätze bzw. jegliches Python-Objekt sein, in eine Art Container zu packen, bietet uns Python unterschiedliche Möglichkeiten an, so gibt es:
- Tupel
- Sets
- Listen
- Dictionaries
Data Science impliziert bereits durch seinen Namen, dass viel mit Daten gearbeitet wird, so ist ein wichtiges Kriterium für eine Datenstruktur, dass sich Daten verändern lassen und sie zudem indiziert sind. Diese Anforderungen wird nur von Listen und Dictonaries erfüllt. Bei Tupeln sind die Daten zwar indiziert aber können nicht verändert werden. Sets erfüllen weder die Anforderung der Indizierung noch der Datenmanipulation. So lassen sich zwar Elemente hinzufügen und entfernen, aber nicht direkt verändern. Ihr Anwendungsbereich liegt vor allem in der Mengenlehre wie man sie aus der Mathematik kennt. Für einen schnellen Einstieg in Data Science stellen wir Euch nun Dictionaries und Listen als praktische Datenstrukturen in Python vor.
Dictionaries
Ein Dictonary zu Deutsch Lexikon bzw. Wörterbuch könnt ihr Euch Wort wörtlich so vorstellen. Es verbindet allgemein gesagt, ein Objekt – dieses kann beliebiger Natur sein – mit einem einzigartigen Schlüssel. Dopplungen innerhalb eines Dictonarys werden daher ausgeschlossen. Somit bieten sich diese eher für die Strukturierung von verschiedenen Variablen zu einem Datensatz, als jeden Eintrag einzeln zu Speichern. Wie ein dict()
beispielhaft aufgebaut ist, seht ihr im folgenden Code Auszug:
# Beispiel Aufbau ohne der Funktion 'dict()'
example_dict_1 = {'Zahl': 1, 'Satz': 'Beispiel Satz in einem Dict'}
# Beispiel Aufbau mit der Funktion 'dict()'
example_dict_2 = dict([('Zahl', 1), ('Satz', 'Beispiel Satz in einem Dict')])
Vergleichen wir die verschiedenen Möglichkeiten ein Dictonary zu erstellen, so fällt auf, dass die erste Möglichkeit einfacher aufgebaut ist. Das eindeutige Erkennungsmerkmal für ein Dictonary sind die geschweiften Klammern. Ein Richtig oder Falsch wie man das Dictonary erstellt gibt es allerdings nicht.
Nachdem wir nun ein Dictonary erstellt haben, möchten wir Euch zuerst zeigen, wie man zum einen Elemente aufruft und zum anderen sie ersetzen kann. Abschließend seht ihr noch ein Beispiel wie man die Existenz eines Elements überprüft.
# Auswahl eines Elements aus einem dict
example_dict_1['Zahl']
# Ausgabe: 1
# Ändern des Inhaltes eines Elements aus einem dict
example_dict_2['Satz'] = 'Dies ist nun ein neuer Satz'
# Überprüfen der Existenz eines Elements in einem dict
'Satz' in example_dict_2
# Ausgabe: True, da Element in dict vorhanden
'Zahl1' in example_dict_2
# Ausgabe: False, da Element in dict vorhanden
Listen
Kommen wir nun zu unserer zweiten “Data Science” Datenstruktur in Python: Listen. Sie lassen sich ähnlich zu Dictionaries in einer Zeile erstellen, zeichnen sich im Gegensatz aber dazu aus, dass sie keine feste Zuweisung von Elementen über einen Schlüssel vornehmen. Die Elemente einer Liste lassen sich daher über ihren Index aufrufen. An dieser Stelle eine kurze Anmerkung zu Indizierung in Python. Der Index beginnt mit der Zahl 0 und wird im Sinne der natürlichen Zahlen stufenweisen hochgezählt: 0,1,2,3,… der letzte Index kann zwar eine beliebige hohe, natürliche Zahl kann aber auch einfach über die Zahl -1 aufgerufen werden. Die Funktionsweise verdeutlichen wir gleich.
Erstellen wir eine Liste, so wird der Beginn und das Ende einer Liste durch eine eckige Klammer verdeutlicht. An dieser Stelle sei noch einmal betont, dass der Datentyp der in der Liste gespeichert wird, nicht für jedes Element identisch sein muss. Zahlen, Strings und Co. lassen sich beliebig mischen.
# Erstellen einer Liste
demo_list = [1, 2, 4, 5, 6, 'test']
Die Auswahl von Elementen gliedert sich in zwei Punkte auf:
- Auswahl einzelner Elemente
- Auswahl mehrere Elements
Ersteres funktioniert über den Index sehr einfach, für Zweiteres muss ein Doppelpunkt bis zu dem jeweiligen nächsten Index gesetzt werden. Möchte man also die ersten drei Elemente (Index: 0,1,2) auswählen, so muss der Index nach dem Doppelpunkt 3 betragen. Die Zuweisung von neuen Daten/Elementen an eine bestimmte Indexstelle einer Liste erfolgt ähnlich zu einem Dictionary.
# Auswahl eines Elements (genauer: Auswahl des ersten Elements)
demo_list[0]
# Ausgabe: 1
# Auswahl mehrerer Elementen (genauer: Auswahl der ersten drei Elemente)
demo_list[:3]
# Ausgabe: 1, 2, 3
# Auswahl des letzen Elements der Liste
demo_list[-1]
# Ausgabe: 'test'
# Zuweisung eines neuen Elements
demo_list[3] = 3
# Die Liste hat danach folgende Struktur [1, 2, 4, 3, 6, 'test']
Einen Nachteil von Listen ist allerdings, dass sie im Wesentlich nur zum Speichern von Daten geeignet sind. Einfache mathematische Funktionen können zwar von Element zu Element angewandt werden, für komplexe Matrizen- oder Vektor-Algebra bedarf es anderer Werkzeuge wie z.B. die Bibliothek NumPy.
Einführung in NumPy
NumPy ermöglicht es uns durch seine eingeführten Multi-Dimensionalen-Arrays (kurz ndarrays) ist, komplexe mathematische Operationen und Algorithmen einfach und effizient durchzuführen. Da NumPy im Normalfall nicht direkt installiert ist, müssen wir dieses z.B. durch pip
oder conda
manuell erledigen. Sofern eine aktuelle Python-Version ( >=3.3) installiert ist, sollte pip direkt verfügbar sein. Wir können sodann einfach im Terminal per pip install numpy
btw. pip3 install numpy
NumPy installieren. Für diejenigen die Anaconda nutzten, sollte NumPy direkt verfügbar sein. Um sicher zu gehen kann man jedoch per conda install NumPy
sicherstellen, dass NumPy vorhanden ist bzw. es updaten.
Ein einfaches Beispiel kann zeigen wie effizient und nützlich NumPy ist. Nehmen wir an, wir haben einige Datenpunkte und wollen eine mathematische Operation vornehmen, wie etwa die Wurzel ziehen. Hierzu soll unsere Liste li
dienen.
li = [1,3,5,6,7,6,4,3,4,5,6,7,5,3,2,1,3,5,7,8,6,4,2,3,5,6,7]
Da Pythons Mathe-Modul math
jeweils nur eine Zahl als Input nimmt, bleibt uns nichts anderes übrig, als die Wurzel per Listcomprehension zu zuweisen. Listcomprehension ermöglich eine sehr kompakte Form der Listenerstellung.
import math
s = [math.sqrt(i) for i in li]
Währendessen können wir mit NumPy direkt auf dem gesamten Array mit einem geringen Aufwand arbeiten zu betreiben.
import numpy as np
arr = np.array(li)
s = np.sqrt(arr)
Wertet man die Laufzeiten der Operationen aus, so dauert es mit dem math
Modul 3,3 Mikrosekunden, verwenden wir hingegen NumPy so reduziert sich die Laufzeit um ein Drittel auf 0,9 Mikrosekunden. Dieser Aspekt unterstreicht die effiziente Implementierung von Arrays in NumPy. Sie eignen sich daher sehr gut, um auch mit relativ vielen Daten gut zurecht zu kommen. Darüber hinaus ermöglichen eine Vielzahl von Funktionen, Möglichkeiten zur Konstruktion, Transformation und Restrukturierung von Arrays ohne vorab Listen zu definieren. Hierüber möchten wir Euch abschließend noch einen Überblick geben.
Eine Matrix mit Zufallszahlen können wir sehr schnell erstellen. Ist man sich über die Struktur seiner Daten unsicher, so kann man sich diese über das Attribut shape
ausgeben lassen.
# 25x1 große Matrix mit einem Mittelwert von 20 und Standartabweichung von 10
ran = np.random.randn(25,1) * 10 + 20
# Struktur eines Arrays/Matrix
print(ran.shape)
In der Praxis kommt es allerdings häufig vor, dass die vorliegenden Daten nicht unbedingt der gewünschten Struktur entsprechen. NumPy bietet für dieses Problem verschiedene Funktionen an, so können die Arrays mit reshape
transformiert werden oder mit hstack
/ vstack
horizontal oder vertikal angeordnet werden. Bei reshape
wird die gewünschte Struktur als Liste übergeben.
# Umstrukturierung der Zufallszahlen
ran = ran.reshape([5,5])
# Zweite Zufallsmatrix
ran2 = np.random.randn(25,1) * 5 + 1
# Stapeln zu 25x2
vstack = np.vstack([ran, ran2])
# Verbinden zu 50x1
hstack= np.hstack([ran, ran2])
NumPy bildet somit ein solides Grundgerüst um schnell mit Zahlen zu hantieren. Für diejenigen, die Erfahrung mit linearer Algebra haben muss an dieser Stelle noch dazu gesagt werden, dass ndarrays
keine Matrizen sind! Worauf ich hier hinaus will ist, dass ndarrays
sich nicht wie Matrizen verhalten wenn es z.B. um Multiplikation geht. ndarrays
multiplizieren Element für Element. Somit kann auch ein 4×1 Array quadriert werden ohne es zu transponieren. Jedoch lässt NumPy dennoch die Standard Matrizenmultiplikation zu mit der np.dot()
-Funktion, oder der Operation @
# Element * Element
np.ones([4,1]) * np.ones([4,1])
# oder Matrizenmultiplikation
np.ones([1,4]) @ np.ones([4,1]) == np.dot(np.ones([1,4]) , np.ones([4,1]) ) == np.ones([1,4]).dot(np.ones([4,1]))
Fazit
In diesem Blogbeitrag haben wir die wesentlichen Datenstrukturen, die sich zum Arbeiten mit unterschiedlichen Datenelement eignen, kennengelernt diese sind Listen und Dictonary. Ihr solltet diese sowohl Erstellen wie auch Manipulieren können. Auch das Abrufen von Elementen sollte für Euch ab sofort kein Problem mehr darstellen. Für die Verarbeitung von Zahlen und Matrizen hat die Bibliothek NumPy bewiesen, dass sie eine performant Umsetzung von Berechnung ermöglicht.
Vorschau
Im nächsten Teil dieser Reihe werden wir uns noch etwas tiefer gehend mit NumPy beschäftigen. Da NumPy und die ndarrays
den Kern der wissenschaftlichen Umgebung in Python darstellen und wir immer wieder auf Sie stoßen werden ist daher ein gutes Verständnis dieser von fast schon obligatorisch. Im folgenden Teil werden wir uns genauer mit den wichtigsten Eigenschaften – Attributen und Methoden – vertraut machen.