© Krall.Evelyne/Shutterstock.com, © Arizzona Design/Shutterstock.com
Interaktive Dashboards mit Python und Dash von plotly

Wir visualisieren die Corona-Pandemie


Seit Februar werden wir in den Medien mit Diagrammen und Grafiken zur Ausbreitung des Coronavirus überschüttet. Die Daten kommen aus frei zugänglichen Quellen und sind für jeden nutzbar. Doch wie wird aus den Quelldaten ein Datensatz, mit dem beispielsweise ein Dashboard erstellt werden kann? Mit Python und Modulen wie pandas ist das keine Zauberei.

Es sind schon verrückte Zeiten, in denen wir seit Anfang 2020 leben. Eine Viruspandemie hat das öffentliche Leben auf den Kopf gestellt. Nachrichtenseiten bieten Liveticker mit den neuesten Meldungen zu Infektionen, Genesungen und Sterberaten. Es existiert kein Medium, in dem nicht ein Diagramm zur Visualisierung benutzt wird. Institute wie das Robert-Koch-Institut (RKI) oder die Johns-Hopkins-Universität stellen Dashboards zur Verfügung. Wir leben in einer von Daten dominierten Welt, auch während einer Pandemie.

Das Gute: Die meisten Daten zur Pandemie sind öffentlich zugänglich. Die Johns-Hopkins-Universität etwa stellt ihre Daten in einem offenen GitHub-Repository zur Verfügung. Was liegt also näher, als mit diesen frei zugänglichen Daten ein eigenes Dashboard zu erstellen? Wie man von der Datenbereinigung über die Anreicherung der Daten aus anderen Quellen bis zur Dashboarderstellung mittels Dash von Plotly gelangt, beleuchtet dieser Artikel anhand der Daten zu Corona. Vorab ein wichtiger Hinweis: Die Daten werden in keiner Weise interpretiert oder inhaltlich analysiert. Das muss Fachleuten wie Virologen überlassen werden, da sonst falsche Rückschlüsse gezogen werden können. Auch wenn die Daten für annähernd alle Länder vorliegen, sind diese nicht unbedingt vergleichbar. Jedes Land nutzt andere Methoden zum Testen der Infektionen. Manche Länder haben gar zu wenige Tests, sodass hier kein einheitliches Bild entstehen kann. Der Datenbestand dient nur als Beispiel.

Erst die Arbeit

Um die Daten nutzen zu können, müssen wir sie für unsere Zwecke in eine einheitliche Form bekommen. Die Daten der Johns-Hopkins-Universität werden tagesaktuell in einem GitHub-Repository [1] abgelegt. Prinzipiell sind sie in zwei Kategorien unterteilt: zum einen fortlaufend als Time-Series-Data und zum anderen als täglicher Report in einer eigenen CSV-Datei. Für das Dashboard benötigen wir beide Quellen. Mit den zeitlich eingeordneten Daten ist es ein Leichtes, Liniendiagramme zu erzeugen und Steigerungen, Kurvenverläufe etc. zu plotten. Daraus generieren wir später den zeitlichen Verlauf der Fallzahlen als Liniendiagramm. Weiterhin können wir die Wachstumsraten aus den Daten berechnen und darstellen.

Umgebung einrichten

Um die Daten aufzubereiten, verwenden wir Python und die Bibliothek pandas [2]. pandas ist das Schweizer Offiziersmesser für Python in Sachen Datenanalyse und -bereinigung. Da wir einige Module für Python installieren müssen, empfehle ich, eine Multi-Environment-Umgebung für Python einzurichten. Ich persönlich benutze Anaconda [3], es gibt aber auch Alternativen wie Virtualenv [4]. Durch die Benutzung von isolierten Umgebungen wird nichts an der systemweiten Installation von Python verändert, daher rate ich dringend dazu. Weiterhin kann man mit verschiedenen Python-Versionen arbeiten und für das Deployment die Abhängigkeiten leichter exportieren. Unabhängig vom genutzten System besteht der erste Schritt darin, die Umgebung zu aktivieren. Mit Anaconda und dem Kommandozeilentool conda funktioniert das wie folgt:

$ conda create -n corona-dashboard python=3.8.2

Die Umgebung mit dem Namen corona-dashboard wird angelegt, Python 3.8.2 und die notwendigen Module werden installiert. Um die Umgebung zu nutzen, aktivieren wir sie mit:

$ conda activate corona-dashboard

Tiefer wollen wir hier nicht in Anaconda einsteigen, sondern verweisen auf die offizielle Dokumentation [5].

Haben wir unsere Umgebung aktiviert, installieren wir die notwendigen Module. Im ersten Schritt sind das pandas und Jupyter Notebook [6]. Jupyter Notebooks ist, vereinfacht gesagt, ein digitales Notizbuch. Die Notebooks beinhalten Markdown und Codeschnipsel, die auch unmittelbar ausgeführt werden können. Sie eignen sich hervorragend zur iterativen Durchführung einer Datenbereinigung und zur Entwicklung der notwendigen Schritte. Bei der Entwicklung von Diagrammen können die Notebooks ebenfalls sinnvoll eingesetzt werden, bevor sie in die endgültigen Skripte überführt werden.

$ conda install pandas $ conda install -c conda-forge notebook

Im Folgenden führen wir alle Schritte in Jupyter Notebooks durch. Die Notebooks sind im Repository zu diesem Artikel auf GitHub zu finden [7]. Um sie zu verwenden, muss der Server gestartet werden:

$ jupyter notebook

Nach dem Start wird im Browser die Übersicht im aktuellen Verzeichnis angezeigt. Ein Klick auf new, und die Auswahl der gewünschten Umgebung öffnet ein neues Jupyter Notebook. Wir können mit dem Data Cleaning beginnen.

Zeitbasierte Daten bereinigen

Im ersten Schritt importieren wir pandas, das Methoden zum Einlesen und manipulieren von Daten zur Verfügung stellt. Hier benötigen wir einen Parser für CSV-Dateien, wofür die Methode read_csv zur Verfügung gestellt wird. Als Parameter wird mindestens ein Pfad zu einer Datei oder ein Buffer erwartet. Gibt man als Parameter einen URL zu einem CSV an, liest und verarbeitet pandas ihn ebenfalls ohne Umstände. Um konsistente, nachvollziehbare Daten zu gewährleisten, greifen wir auf eine heruntergeladene Datei zu, die im Repository [7] verfügbar ist.

df = pd.read_csv("time_series_covid19_confirmed_global.csv")

Zur Kontrolle lassen wir uns mit der Anweisung df.head() die ersten fünf Zeilen des Dataframes ausgeben (Abb. 1).

sandtner_corona_1.tif_fmt1.jpgAbb. 1: Das unbereinigte Dataframe

Um sich der Struktur bewusst zu werden, können wir uns die Spaltennamen ausgeben lassen. Das erfolgt mit der Anweisung df.cloumns. Man sieht in Abbildung 2, dass für jeden Tag in der Tabelle eine Spalte vorgesehen ist. Weiterhin gibt es Geokoordinaten zu den jeweiligen Ländern. Die Länder sind zum Teil aufgeteilt in Provinzen und Bundesstaaten. Für die zeitbasierten Daten benötigen wir keine Geokoordinaten, und wir entfernen auch die Spalte mit den Bundesstaaten. Das erreichen wir in pandas mit folgenden Methoden auf dem Data-frame:

df.drop(columns=['Lat', 'Long', 'Province/State'], inplace = True)

Die Methode drop erwartet als Parameter die Daten, die wir loswerden möchten. In diesem Fall sind das drei Spalten: Lat, Long und Province/State. Wichtig ist, dass die Namen inklusive Groß-/Kleinschreibung und eventuellen Leerzeichen genau angegeben werden müssen. Der zweite Parameter inplace dient dazu, die Operation direkt auf unser Dataframe anzuwenden. Ohne diesen Parameter gibt uns pandas das modifizierte Dataframe zurück, ohne das Original zu verändern. Schaut man sich mit df.head() den Frame an, sieht man, dass die gewünschten Spalten verworfen worden sind.

Durch die Aufteilung mancher Länder in Provinzen oder Bundesstaaten entstehen für einige mehrere Einträge. Beispielhaft ist China zu nennen. Daher ist es sinnvoll, die Daten nach Ländern zu gruppieren. Dafür stellt pandas eine mächtige Gruppierungsfunktion zur Verfügung.

df_grouped = df.groupby(['Country/Region'], as_index=False).sum()

Mit der Funktion groupby und der Übergabe, nach welcher Spalte gruppiert werden soll, werden die Reihen zusammengefasst. Das verkettete .sum() summiert die Werte der jeweiligen zusammengefassten Gruppen. Die Rückgabe ist ein neues Dataframe mit den gruppierten und summierten Daten.Damit wir für alle Länder auf die zeitbezogenen Daten zugreifen können, müssen die Daten noch transponiert werden. Wir tauschen also Zeilen und Spalten, um für jeden Tag (Reihe) einen Eintrag für jedes Land (Spalten) zu erhalten.

df_grouped.reset_index(level=0, inplace=True) df_grouped.rename(columns={'index': 'Date'}, inplace=True)

Wir setzen vor dem Transponieren den Index auf Country/Region, um ein sauberes Frame zu erhalten. Der Nachteil dabei ist, dass im Anschluss der neue Index die Bezeichnung Country/Region hat.

Die nächste Anpassung besteht darin, das Datum in eine eigene Spalte zu setzen. Um das zu korrigieren, resetten wir wieder den Index. Dadurch wird aus unserem alten Index (Country/Region) eine Spalte mit Namen Index. Diese enthält die Datumsangaben und muss umbenannt und auf den korrekten Datentyp gesetzt werden. Die Bereinigung ist abgeschlossen (Abb. 3).

df_grouped.reset_index(level=0, inplace=True) df_grouped.rename(columns={'index': 'Date'}, inplace=True) df_grouped['Date'] = pd.to_datetime(df_grouped['Date'])
sandtner_corona_2.tif_fmt1.jpgAbb. 2: Übersicht der Spalten im Dataframe
sandtner_corona_3.tif_fmt1.jpgAbb. 3: Das fertig bereinigte Dataframe

Dass der Index weiterhin die Bezeichnung Country/Region hat, stört im weiteren Verlauf nicht. Das liegt daran, dass die finale CSV-Datei ohne Index gespeichert wird.

df_grouped.to_csv('../data/worldwide_timeseries.csv', index=False)

Damit sind die zeitlichen Daten bereinigt und können für jedes Land benutzt werden. Möchten wir bspw. nur die Daten für Deutschland benutzen, kann ein neues Dataframe als Kopie mit der Auswahl der gewünschten Spalten erstellt werden.

df_germany = df_grouped[['Date', 'Germany']].copy()

Verpackt in eine Funktion erhalten wir für die Bereinigung der zeitlichen Daten den Quellcode aus Listing 1.

Listing 1

def clean...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang