Sent from Hauptstadt!

ein Blog für den geneigten Leser

SciPy und Jupyter Notebooks

Tags: ,

Kategorie Software Engineering | 5 Kommentare »

Als klassischer Software Ingenieur frage ich mich, ob es langsam Zeit wird, auf KI und Data Science umzuschulen?! Rollt da ein Strukturwandel auf mich zu? Schaden kann es zumindest nicht, wenn ich mich etwas näher mit dem Neuland beschäftige. Unterwegs mache ich ein paar Notizen, die ich hier ablege. Als ersten Schritt schaue ich mir Jupyter Notebooks und SciPy an.

Motivation

Vor vielen Äonen, während meines Studiums, hatte ich 3 Semester Statistik, Stochastik und Kombinatorik. Bei Kombinatorik hat es bis heute bei mir nicht Klick gemacht – hoffnungslos. Die klassische Statistik hingegen habe ich in guter Erinnerung. Ich kann mich noch an Übungen in einem Labor mit alten Unix Terminals und schweren Vorhängen erinnern, in dem wir irgendein damals verfügbares Statistikpaket unter X-Windows gequält haben (oder umgekehrt). Wir haben für Datenreihen typische Kennzahlen wie Standardabweichung und Median ermittelt, Regressionsanalysen gefahren und schöne Grafiken wie Boxplots und Scatter Plots gerendert. Ja, das gab es auch alles schon vor 15 Jahren :-)

Heute gibt es einen unüberschaubaren Wust an viel besseren Werkzeugen – kommerziell und OpenSource. Im Umfeld der Künstlichen Intelligenz (KI) und des Maschinellen Lernens (ML) tauchen als Kerntechnologie immer wieder Jupyter Notebooks und SciPy auf. Auch über R, Octave und Julia hört man viel. Wie immer ist die IT voll von tollen Buzzwords für das vermeintlich Neue.

Data Scientists, Neudeutsch für Statistiker, nutzen diese Werkzeuge für die explorative Datenanalyse und Implementierung statistischer Verfahren. Denn tatsächlich ist Statistik ein wesentliches Kernelement von Maschinellem Lernen. Doch dazu vielleicht später mal mehr.

Überblick SciPy und Jupyter Notebooks

Jupyter Notebooks und SciPy sind die bevorzugten Werkzeuge im Python Umfeld für solche Datenanalysen. Da ich beruflich täglich Python verwende, hoffe ich, mein langweiliges Software Engineering Wissen übertragen zu können, statt eine Umschulung auf komplett neue Sprachen und Welten wie R und Julia machen zu müssen.

SciPy ist der Oberbegriff für eine Vielzahl von Python Bibliotheken, die wichtige Funktionen für die Datenanalyse bereitstellen. Hier ein paar Kernelemente:

  • NumPy – erweitert Python um eine performante Implementierung für n-dimensionale Arrays
  • Pandas – eine MS Excel-artige API für Python, um große Datentabellen effizient zu verwalten (etwa zeitliche Aggregation der Spalten)
  • Matplotlib, seaborn, ggplot2, Altair, etc. pp. – verschiedene Bibliotheken zur Visualisierung von Daten
  • StatsModels – Funktionen für statistische Analyse, etwa Regressionsanalyse und parametrische Tests
  • scikit-learn – Funktionen für maschinelles Lernen, aber auch statistische Basisfunktionen wie Regressionsanalyse oder Klassifikation von Daten

Während der Einsatzzweck von NumPy und Pandas relativ klar abgrenzbar ist, kann man für die Visualisierung zwischen verschiedenen Bibliothen wählen. Matplotlib ist der technische Unterbau für einige Visualisierungspakete, aber zu rudimentär, um es direkt zu verwenden. Die Pandas Bibliothek kann auch einige Visualisierungen wie etwa Histogramme erstellen. Besser benutzbar ist seaborn. Noch moderner sind ggplot2 und Altair. Bis jetzt war seaborn für mich ausreichend.

Auch bei den statistischen Standardverfahren wie Regressionsanalysen gibt es verschiedene Bibliotheken. Anscheinend beherrscht keine Bibliothek alle Verfahren, weshalb wohl vorerst noch ein Best of Breed Ansatz (yeah!) sinnvoll ist. Es gibt noch viel Bewegung in der SciPy Community und nicht in jeder Kategorie einen klaren Sieger.

Die genannten Bibliotheken sind keine Programme, sondern lediglich Erweiterungen für die Programmiersprache Python. Ich könnte also ein Python Programm schreiben, das die Bibliotheken nutzt, um dann eine Ausgabe zu generieren und mir irgendwie anzuschauen. Das wäre für den Data Scientist, der per Definition kein Software Ingenieur ist, aber zu kompliziert, zumal andere Werkzeuge wie Octave, SPSS oder Matlab nicht nur Bibliotheken sondern intuitiv bedienbare Softwarepakete inklusive Editor und Ausgabe sind.

An dieser Stelle kommen die bereits erwähnten Jupyter Notebooks ins Spiel. Jupyter Notebooks sind eine webbasierte Umgebung, um Datenanalysen zu programmieren und auszuführen. Besonders ist dabei, dass Quellcode und Ausgabe gemischt werden. Visualisiert man zum Beispiel die Daten einer mittels Pandas definierten Tabelle, dann wird die erzeugte Grafik direkt unter dem entsprechendem seaborn Befehl gerendert.

Jupyter Notebooks sind keine neue Erfindung, sondern gehen zurück auf IPython Notebooks, deren Ursprung wiederum in einem Zoo von interaktiven Python Shells wie bpython liegt. Der geneigte Leser mag sich an die Kommandozeilen Shell von Amiga Basic erinnern. Man gibt einen Befehl ein und nachdem man die Zeile mit Return beendet hat, gibt die Shell direkt das Ergebnis aus. Solche Umgebungen bezeichnet man gemeinhin als REPLs (read-eval-print loops).

Während die ersten REPLs noch sehr rudimentär waren, bieten moderne Shells zum Beispiel Autovervollständigung, integrierte Dokumentation, Syntaxhervorhebung und eine Befehlschronik. Jupyter Notebooks übertragen das REPL Prinzip für Python in eine moderne Weboberfläche, was nicht nur textuelle Ausgaben, sondern auch formatierte Texte und Grafiken ermöglicht.

Kritik an Jupyter Notebooks

Damit das alles funktioniert, erweitert Jupyter den Sprachumfang von Python um einige Direktiven, über die sich die Ausgabe steuern lässt. Soweit, so gut. Warum man sich aber bei Jupyter Notebooks (*.ipynb) von klassischen Quellcodedateien verabschiedet und stattdessen eine JSON Struktur eingeführt hat, ist mir völlig unbegreiflich. Im Jupyter Notebook Editor seines Vertrauens sieht man zum Beispiel folgende Zeilen, um eine CSV Datei in ein Pandas DataFrame zu laden:

In der zugehörigen Jupyter Notebook Datei steht dies aber nicht als Text drin, sondern verschachtelt in einer JSON Struktur:

Neben dem Quellcode werden in dieser Struktur auch noch Metadaten, wie die Anzahl der Aufrufe sowie die letzte Ausgabe, gespeichert. Besteht die Ausgabe aus einer Grafik, so wird diese als Base64 kodierte Zeichenkette ebenfalls in der JSON Struktur abgelegt.

Dies verhindert natürlich den Einsatz jeglicher Textwerkzeuge wie grep, diff, cat & Co. Auch für eine Versionsverwaltung wie GIT ist das Horror, da sich die Quelldatei bei jeder Ausführung ändert. Das ist natürlich maximaler Unsinn. Man hätte auch gleich eine Binärdatei verwenden können!

Beispiel: Klimawandel in Berlin Tegel

Doch genug genörgelt, schließlich entwickel ich als Data Scientist kein neues Betriebssystem, sondern führe lediglich eine explorative Datenanalyse in wenigen Zeilen Quellcode durch. Und das geht mit Jupyter Notebooks flott von der Hand, wie folgendes kleines Beispiel demonstriert.

Ich habe beim Deutschen Wetterdienst (DWD) die historischen Messdaten für die DWD Wetterstation an meinem Wohnort Berlin Tegel heruntergeladen. Die Daten umfassen für jeden Kalendertag seit 1963 unter anderem die mittlere Tagestemperatur (TMK) und tägliche Niederschlagsmenge (RSK). Ich möchte herausfinden, ob es den Klimawandel auch in Berlin gibt und zum Beispiel die Temperaturen über die Jahre angestiegen sind.

Daten in Pandas Tabelle laden und bereinigen

Zunächst lade ich die Daten in ein Pandas DataFrame. Würde ich die Analyse manuell durchführen, entspräche dies dem Import der Daten nach MS Excel. Das DataFrame ist sozusagen meine Excel Tabelle.

Über die angedeutete Schleife könnte ich auch mehrere CSV Dateien in eine Tabelle laden. Pandas würde sich um die Bereinigung von Duplikaten kümmern.

In der CSV Datei gibt es ein paar kleine Unstimmigkeiten. So enthält die erste Zeile mit den Spaltennamen Leerzeichen. Das will ich korrigieren und ignoriere deshalb die erste Zeile und lege eigene Spaltennamen fest.

Statt bei fehlenden Messwerten gar keinen Wert anzugeben, wird in den DWD CSV Dateien der fiktive Messwert -999 gepflegt. Dies teile ich Pandas mit, damit es die fehlenden Werte erkennt. Das ist wichtig, damit zum Beispiel der Mittelwert richtig berechnet wird.

Tatsächlich benötige ich für mein Beispiel nur die Spalten

  • Messdatum (MESS_DATUM)
  • tägliche Durchschnittstemperatur (TMK) und
  • tägliche Regenmenge (RSK).

Alle anderen Spalten importiere ich nicht.

Pandas erkennt beim Import, dass die Spalte „MESS_DATUM“ eine Zeitangabe ist. Ich lege diese Spalte als meinen Index fest. In MS Excel entspricht dies den Nummern der einzelnen Zeilen. Durch diesen Zeitindex kann ich später zum Beispiel die Regenmenge pro Jahr elegant ermitteln.

Einen Blick in die Daten werfen

Nun möchte ich mir einen Überblick über die Daten verschaffen. Ich lasse mir zunächst die ersten 5 Zeilen meiner Tabelle anzeigen, um sicherzustellen, dass ich die richtigen Spalten importiert habe und der Index passt.

Ich beginne eine neue Notebook Zelle, eingeleitet durch die Jupyter Direktive „#%%“. Ohne weitere Angabe handelt es sich um eine Zelle für Python Code. Mit der Direktive „#%% md“ könnte ich eine Markdown Zelle für formatierten Text einfügen. Natürlich gibt es in der Web-Oberfläche auch Schaltflächen, um Zellen einzufügen, zu verschieben, kopieren, den Typ zu ändern, etc. pp.

Führe ich diese Zelle in Jupyter aus, sehe ich sofort das Ergebnis unter meiner Eingabe. Hier ein Screenshot:

Eingabe und Ausgabe Zellen in einem Jupyter Notebook

Am 1.1.1963 hat es also schon mal nicht geregnet und es war mit -12,4°C im Tagesmittel verdammt kalt.

Ich bearbeite in Jupyter eine Zelle, bis ich das gewünschte Ergebnis habe. Anschließend füge ich eine weitere Zelle ein und setze meine Analyse fort. Wie in einem Python Programm gehen mir Variablen zwischen den Zellen nicht verloren. In meiner ersten Zelle habe ich den Inhalt der CSV Datei in die Variable „df“ geladen, um anschließend in der nächsten Zelle die ersten 5 Zeilen dieser Pandas Tabelle auszugeben. Ich habe die Möglichkeit, eine Zelle einzeln oder das gesamte Notebook auszuführen. Ich kann auch zu jeder Zeit mir den aktuellen Wert einer Variable anschauen.

Das fertige Jupyter Notebook inklusive Ausgabe der einzelnen Zellen kann der geneigte Leser auf GitHub bewundern. Da die Jupyter Notebook Dateien immer auch die Ausgabe enthalten, zeigt GitHub direkt die Ausgabe an. Die tatsächliche JSON Struktur des Notebooks sieht man nach einem Klick auf „Raw„.

Saisonalität der Daten entfernen

In meiner Analyse habe ich mir zunächst einige statistische Kennzahlen für meine Spalten wie die Standardabweichung mittels „df.describe()“ ausgegeben.

Ausgabe statistischer Kennzahlen mittels Pandas describe() Funktion

Die Werte in den Spalten streuen zu stark, um irgendeine Schlussfolgerung daraus abzuleiten. Das ist auch kein Wunder, denn schließlich schwankt Temperatur und Niederschlag durch die Jahreszeiten.

Um diesen saisonalen Effekt aus meinen Daten zu entfernen, habe ich mir überlegt, nicht die einzelnen Tagestemperaturen zu verwenden, sondern die mittlere Tagestemperatur für jedes Jahr zu bilden. Da meine Tabelle einen Zeitindex hat, kann ich solch eine Aggregation sehr elegant erzeugen, ohne mir Gedanken über fehlende Messwerte oder Schaltjahre machen zu müssen.

Beim Regen ist nicht die durchschnittliche Menge pro Tag interessant, sondern die Summe aller Niederschläge im Jahr.

Nach diesem Kniff sehen die Daten schon wesentlich besser aus. Die meisten Werte liegen jetzt nah am Mittelwert, was man sowohl an der Standardabweichung als auch visuell am Boxplot ablesen kann.

ein Boxplot der jährlichen Durschnittstemperatur in Berlin Tegel seit 1963

Daten auf Normalverteilung testen

Ich vermute, das Klima hat sich nicht geändert, denn sonst hätten die in Berlin ansässigen Bundespolitiker sicher schon vor Jahren Maßnahmen ergriffen. Auch wenn es mal wärmere/kältere sowie trockenere/nassere Jahre gibt, sollten sich die Messwerte gleichmäßig um das langjährige Mittel verteilen. Statistisch gesprochen, möchte ich testen, ob die Messwerte normalverteilt sind. Dies ist meine Null-Hypothese.

Histogramm der jährlichen Durchschnittstemperatur in Berlin Tegel seit 1963

Das Histogramm für die jährliche Durschnittstemperatur sieht wie eine klassische Käseglocke aus, was immer ein gutes Zeichen dafür ist, dass die Daten höchstwahrscheinlich normalverteilt sind.

Neben dieser visuellen Überprüfung gibt es eine Vielzahl statistischer Tests, um eine Menge auf Normalverteilung zu überprüfen. Der Shapiro-Wilk-Test ist ein gängiges Verfahren, um zu prüfen, ob eine Wertereihe normalverteilt ist. Dies geht in SciPy folgendermaßen:

Ergebnis ist, dass mit hoher Wahrscheinlichkeit (über 95%) die Temperaturwerte normalverteilt sind und damit zwar um das langfristige Mittel schwanken, nicht aber einem Trend folgen.

Zeitlichen Verlauf in Diagramm darstellen

Ich glaube natürlich nur, was ich sehe. Deshalb generiere ich mir ein Diagramm mit dem zeitlichen Verlauf der jährlichen Durschschnittstemperaturen. Zusätzlich möchte ich eine lineare Trendlinie einzeichnen. Bleiben die Temperaturen über die Jahre gleich, sollte diese Trendlinie mehr oder weniger parallel zur x-Achse verlaufen. Hier zunächst der Code:

Im oberen Teil generiere ich mittels seaborn (importiert als „sns“) ein Liniendiagramm für meine Temperaturwerte über die Jahre.

Laut Lizenzbedingungen des DWD muss bei der Verwendung der DWD Daten immer die Quelle angegeben werden. Ich habe mir deshalb eine kleine Hilfsfunktion „add_source“ geschrieben, die jedem Diagramm den entsprechenden Schriftzug hinzufügt.

Im unteren Teil, ab Zeile 6, lass ich mir durch Numpy ein lineares Polynom (Paramter mit Wert „1“ gibt die Dimension des Polynoms an) suchen, das die Werte der Zeitreihe am besten vorhersagt. Es wird also eine Gleichung für eine Funktion gesucht, bei der die Summe der Fehler zwischen den tatsächlichen Messwerten und dem Wert der Funktion am geringsten sind. Diese Funktion zeichne ich als schwarze Gerade ebenfalls in das Diagramm ein. Seaborn erlaubt es mir auf sehr einfache Weise, mehrere Funktionen in einem Diagramm zu kombinieren.

Liniendiagramm mit hinzugefügter Trendlinie auf Basis einer Regressionsanalyse

Houston, haben wir nun doch ein Klimaerwärmungsproblem? Wie kann das sein? Die Politiker unternehmen doch gar nichts!

Trend in Zeitreihen mit KPSS und Augmented Dickey-Fuller Tests prüfen

An dieser Stelle beginnt die hohe Kunst der statistischen Analyse: Ich fälsche die Statistik solange, bis ich das gewünschte Ergebnis erhalte. Schließlich will ich weiter bedenken- und gedankenlos mit meinem nicht vorhandenen SUV sonntags Brötchen holen dürfen!

Es gibt eine Reihe von statistischen Tests, um zu überprüfen, ob eine Zeitreihe einem Trend folgt oder ob die Werte stationär sind. Dazu muss ich meine Werte in eine logarithmische Darstellung umwandeln:

Perfekt! Anschließend führe ich den Augmented Dickey-Fuller Test durch. Klingt obskur? Ist obskur, aber keine Herausforderung für SciPy! Es reichen wenige Zeilen Code, um den Test durchzuführen und auszuwerten.

Das Ergebnis ist beruhigend:

An dieser Stelle verschweige ich lieber, dass der KPSS Test ein gegenteiliges Ergebnis liefert :-)

Falls es den geneigten Leser interessiert: Ich habe ähnliche Analysen auch für die Regenmenge durchgeführt. Die Ergebnisse sieht man im Jupyter Notebook auf GitHub. Dort sind die Ergebnisse eindeutiger. Bis jetzt ist keine Änderung der jährlichen Niederschläge statistisch nachweisbar.

Auswertung der Analyse Klimawandel in Berlin Tegel

Meine kleine statistische Analyse der historischen Wetterdaten für Berlin Tegel liefert zu diesem Zeitpunkt also kein eindeutiges Bild, ob es den Klimawandel gibt oder nicht. Im Netz findet man eine Vielzahl weiterer Analysen. Diese Analysen nutzen meist die Bodentemperatur, denn die ist entscheidend, ob das Eis an den Polen abschmilzt und die Permafrostböden tauen und dadurch das gespeicherte CO2 freigeben. Werte für die Bodentemperatur habe ich allerdings nicht.

Problematisch an meinem Vorgehen ist auch, dass ich lediglich gut 50 Messwerte habe, da ich die Daten des DWD jährlich aggregiert habe. Unklar ist auch, ob der KPSS und der Augemented Dickey-Fuller Test überhaupt geeignet sind, den von mir vermuteten Trend zu erkennen oder ob sie nicht für andere nicht-lineare Trends optimiert wurden.

Trotz der umfangreichen Werkzeuge werde ich also nicht über Nacht zum Data Scientist, weil mir schlicht tiefergehendes statistisches Wissen fehlt :-/

Fazit

Jupyter Notebooks und SciPy sind ein extrem mächtiger Werkzeugkasten, um in kurzer Zeit explorative Datenanalysen durchzuführen. Die Aufbereitung und der Umgang mit den Daten ist extrem komfortabel. Numpy und Pandas sind ausgereifte Werkzeuge. Als Software Ingenieur mit akutem NIH Syndrom könnte man schon allein mit dem richtigen Import und Bereinigung der Eingabedaten Tage verbringen. Pandas erledigt das in wenigen Zeilen Code.

Einfache statistisch Kennzahlen wie Median, Standardabweichung und Varianz sind im Handumdrehen ermittelt. Die Daten lassen sich in Pandas sehr elegant aggregieren. Numpy sorgt für die entsprechende Performance bei Operationen auf einzelnen Spalten. Mit seaborn kann man die wichtigsten Diagramme generieren und an die eigenen Vorstellungen anpassen.

Jupyter Notebooks bieten einen einfachen Einstieg in die Datenanalyse mittels SciPy. Das Speicherformat der Notebooks hingegen ist absoluter Unsinn und dürfte im Rahmen eines echten Projekts mit CI/CD Pipeline unendliche Kopfschmerzen verursachen. Mit der Bearbeitung (Programmierung) von Jupyter Notebooks bin ich auch nicht glücklich, da alle von mir getesteten Editoren Schwächen aufweisen. Das muss ich aber in einem anderen Beitrag ausführlich diskutieren.

Faszinierend ist die Fülle von statistischen Tests und Verfahren. SciPy bietet einen riesigen Fundus und alle Tests arbeiten sauber mit den Pandas und Numpy Datenstrukturen. Das macht einen extrem guten Eindruck.

Und bei statistischen Tests ist noch lange nicht Schluss. Auch Modelle für Maschinelles Lernen lassen sich mit den in Pandas gehaltenen Datenstrukturen trainieren und validieren. Der Übergang von statistischer Datenanalyse hin zu Maschinellem Lernen ist nahtlos.

Der geneigte Leser ahnt es sich schon – ich bin begeistert. Es besteht deshalb berechtigte Hoffnung für weitere Blog Posts :-)

5 Kommentare to “SciPy und Jupyter Notebooks”

  1. Matin sagt:

    Ich benutze bis jetzt nur NumPy und Matplotlib, um die zeitliche Entwicklung von Software-Projekt-Metriken zu visualisieren.

    Ich hatte auch schon einmal mit Notebooks und Pandas geliebäugelt, aber da ich keine großen Daten habe, deren Analyse mich reizen würde, bin ich bis jetzt da nicht weiter eingestiegen.

    Das gleiche gilt für KI. Bis jetzt fehlen mir die (real umsetzbaren) Anwendungsfälle, die mich dazu motivieren würden, da was zu machen.

  2. Sebastian sagt:

    Ich finde, inzwischen gibt es recht viele öffentlich verfügbare Datenquellen, die teils interessant sind. Die Wetterdaten vom DWD hatte ich schon lange im Auge. In Berlin haben sie auch gerade Daten zu Unfällen veröffentlicht. Aber klar, das muss einen alles nicht interessieren.

    Für deine SW Metriken könntest du statt matplotlib die Bibliothek plotly.js verwenden. Dann können deine Anwender mit den Grafiken interagieren. Setzt natürlich voraus, dass du die Grafiken per Web UI veröffentlichst. Könntest du sicher gut als internes Innovationsprojekt verkaufen :-)

    Naja, und für KI hätte ich eigentlich schon das Ding für dich: Zunächst programmierst du ein Spiel für deinen Weihnachtskalender. Anschließend baust du eine KI mittels Maschinelles Lernen, gegen die du und der Rest der Welt (Web-Anwendung, Android App, iOS App) spielen kannst. Dann wärst du für die nächsten Jahre ausgelastet :-)

  3. Matin sagt:

    Sollte ich für solch einen Fall neuronale Netze verwenden, wäre das ein Kündigungsgrund :-).

  4. Thomas Bensler sagt:

    Neuronale Netze und Blockchain!!1!

  5. […] meiner Erkundungstour von Jupyter Notebooks und SciPy habe ich diverse Editoren ausprobiert. Mit der Suche nach dem passenden Werkzeug kann man viel Zeit […]

Schreiben sie ein Kommentar