Interaktive Dashboards sind ideal, um vielfältige Daten übersichtlich darzustellen. In diesem Tutorial zu Plotly Dash zeige ich Euch, wie man diese in Python programmiert.

Die Aufgaben eines Data Scientist umfassen nämlich nicht nur Analysen, sondern häufig auch die Darstellung der Ergebnisse. Und wie cool ist es, wenn man nicht nur ein statisches PDF erzeugt oder sich manuell Powerpoint-Folien bastelt, sondern ein interaktives Dashboard baut. Mit diesem kann dann der Nutzer interagieren und sich so die Kennzahlen, gefiltert nach seinen Wünschen, anzeigen lassen. Zudem lassen sich Dashboards leicht aktualisieren, wenn neue Daten verfügbar sind. Wer monatlich oder wöchentlich Excel- oder Powerpoint-Reports bastelt, weiß, wovon ich rede.

Software, um Dashboards zu erstellen

Es gibt eine Reihe kommerzieller, aber auch kostenlose Angebote, mit denen man Dashboards erstellen kann.

  • Microsoft Power BI: Recht schwerfällig, eher für Business Intelligence geeignet als für polierte Dashboards mit viel Interaktivität. Die Desktop-Version ist kostenlos. Um jedoch Reports vernünftig zu teilen (also nicht nur Dateien hin- und herzuschieben), werden Lizenzen für die Cloud-Lösung fällig. Auf der anderen Seite haben viele Firmen eh ein Office-Paket, bei dem PowerBI enthalten ist.
  • Qlik bzw. QlikSense: Finde ich persönlich ziemlich gut, allerdings sind die Lizenzen relativ teuer. Fluch und Segen zugleich sind die umfangreichen Skripting-Möglichkeiten. Damit kann man richtiges Datenmanagement machen, aber die Mischung zwischen Programmieren und Zusammenklicken gestaltet die Übersicht schwierig.
  • Tableau: Hat in der Anfangszeit viel Lob bekommen. Die Stärken liegen vor allem in der Datenvisualisierung. Der Einsatz ist aber auch nicht ganz preiswert.
  • Grafana: Eine tolle kostenlose Software, die vor allem für Zeitreihen (Monitoring von Servers etc.) eingesetzt wird

Dashboards mit Python programmieren

Der Vorteil und gleichzeitig Nachteil von all diesen Klick-basierten Lösungen ist eben das visuelle Arbeiten. Das macht es für den Nicht-Programmierer zwar einfacher bzw. überhaupt erst möglich, Dashboards zu erstellen. Aber die Wartbarkeit ist unübersichtlich bis nicht vorhanden. Gerade bei komplexeren Projekten möchte man verschiedene Dateien für verschiedene Module und Definitionen usw.

Hier kommen die komplett programmierbaren Dashboards ins Spiel. Vorteil solcher Frameworks ist die Kontrollierbarkeit. Nachteil ist, dass man schon ein paar Programmier-Fähigkeiten besitzen muss und auch ein paar Grundlagen in HTML/CSS haben sollte.

Wer in R programmiert, für den ist Shiny das Framework, an dem man nicht vorbeikommt. Für Python gibt es zwei größere Frameworks, Plotly Dash und Bokeh. Die Beliebtheit von Dash steigt stetig und anscheinend ist es etwas einfacher zu lernen. Bokeh benötigt für einige Interaktionen auch Javascript-Kenntnisse. Deshalb geht dieses Einsteiger-Tutorial um Plotly Dash.

Was ist Plotly Dash?

Dash ist ein Framework der Firma Plotly, um Webapps zur Datenanalyse-/visualisierung in Python, R oder Julia zu programmieren. Dash basiert auf React, einem bekannten Javascript-Web-Framework und Flask, einem der bekanntesten Webserver in Python.

Was alles mit Dash möglich ist, kann man sich in der Dash App Galerie anschauen.

Dash ist komplett kostenlos und open-source. Plotly bietet aber kommerzielle Lösungen zum Hosting der Webapps an. Eines muss Euch aber klar sein: Damit jemand anderes Euer Dashboard im Browser sehen kann, braucht ihr einen Server, der Python unterstützt. Für erste Versuche gibt es ein paar kostenlose Schnupperangebote im Netz (pythonanywhere, Heroku). Wollt ihr mehrere Apps erstellen, dann solltet Ihr Euch eine virtuelle Maschine im Netz zulegen. Das kostet zum Glück nicht viel. Ich empfehle Euch dafür Linode*, bei denen man für 5$ pro Monat bereits eine kleine Maschine bekommt.

 

Installation von Plotly Dash

Die Installation ist ganz einfach, da man sie über pip bzw. conda machen kann.

pip install dash
bzw.
conda install dash

Das erste Dashboard mit Plotly Dash

Nach dem Import von Dash und zugehörigen HTML-Funktionen wird die App erzeugt. Anschließend definieren wir das Layout der Seite, welches erstmal nur aus der H1-Überschrift „Willkommen zu Dash!“ besteht. Und schließlich wird in der Main-Routine der Server gestartet.

import dash
import dash_html_components as html
 
app = dash.Dash(__name__)
 
app.layout = html.H1(children="Willkommen zu Dash!")
 
if __name__ == "__main__":
    app.run_server(debug=True)

Lässt Du nun dieses Programm laufen, sollte die Konsole folgendes ausgeben. Damit hast Du einen flask-Server auf Deinem Rechner gestartet, der unter http://127.0.0.1:8050 über den Webbrowser erreichbar ist (nur von Deinem Rechner aus, 127.0.0.1 ist die Adresse des localhosts, also des Rechners selbst)

Ausgabe des Plotly Dash Programms, wenn man es startet. Der Flask Server läuft.

Und so sollte das dann im Browser aussehen:

Screenshot des Browserinhalts der ersten Dash App
Ist noch ziemlich unspektakulär, aber immerhin haben wir schon mal eine Webseite erzeugt. Ändert Ihr etwas im Code und speichert die Datei ab, dann wird die Webseite automatisch neu geladen. Manchmal hängt es aber ein bisschen, dann einfach im Webbrowser nochmal laden.

Grundsätzlich wird das Layout über app.layout definiert, d.h. dort können wir alle möglichen HTML-Abschnitte & -Strukturen, Charts oder Gadgets einbauen. Das wollen wir jetzt im zweiten Beispiel ausbauen. Allerdings bleibt alles erstmal ohne Funktion, denn die bauen wir erst im nächsten Abschnitt ein.

Steuerungselemente wie Dropdowns in Plotly Dash

Wir ergänzen einige Elemente aus der HTML-Library dash_html_components und einige aus der Dash Core Library dash_core_components.

Zuerst müssen wir diese also importieren. Außerdem wollen wir noch eine Grafik mittels plotly.express und Zufallszahlen mit numpy erzeugen.

import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import numpy as np

 
Anschließend schreiben wir eine Funktion hist(verteilung, n), die eine Stichprobe aus einer Verteilung zieht und daraus ein Histogramm macht. Als Parameter wird der Name einer Verteilung (normal, binomial oder chisquared ) und die Stichprobengröße n übergeben. Per np.random.xxx wird dann die Zufallsstichprobe gezogen und mit der plotly-express-Funktion px.histogram das Histogramm erzeugt.

def hist(verteilung, n):
    data = None
    if verteilung == "normal":
        data = np.random.normal(loc=0, scale=1, size=n)
    elif verteilung == "binomial":
        data = np.random.binomial(n=10, p=0.5, size=n)
    elif verteilung == "chisquared":
        data = np.random.chisquare(df=5, size=n)
    else:
        print("Diese Verteilung wird noch nicht unterstützt!")
        return
    return px.histogram(data)

 
Nun kommt der Dash-Block, also

app = dash.Dash(__name__)
 
app.layout = …
 
if __name__ == "__main__":
    app.run_server(debug=True)

Und jetzt definieren wir das Layout, welches sich wie gesagt aus dash_html_components und dash_core_components zusammensetzt. Im Prinzip ist das Layout einfach eine Liste von solchen Objekten. Wer ein bisschen HTML kennt, erkennt diese Komponenten: html.H1 bzw. html.H2 für die Überschriften, html.Div für die Abschnitte (divisions). Komplexere Element sind dcc.Dropdown (ein Dropdown), dcc.Input (Inputfeld) und dcc.Graph für die Grafik.

Die Komponenten kann mit mittels des Parameters style per CSS formatieren. Dabei wird ein dictionary mit den CSS-Einstellungen übergeben. Der Syntax ist minimal verändert, da Bindestriche hier nicht funktionieren würden. Statt CSS „background-color“ benutzt man also „backgroundColor“.

Achtung, wir haben erstmal nur das Layout definiert. Verteilungs-Dropdown und Eingabefeld für die Stichprobengröße werden zwar angezeigt, sind aber noch nicht „verdrahtet“, machen also erstmal gar nichts.

So sieht die Layout-Definition dann aus:

app.layout = html.Div(
    children=[
        html.H1(children="Verteilungen"),
        html.Div(
            children=[
                html.H2("Inputs"),
                html.Div(
                    children=[
                        html.P("Verteilung"),
                        dcc.Dropdown(
                            id="verteilung-dropdown",
                            options=[
                                {"label": "Normal", "value": "normal"},
                                {"label": "Binomial", "value": "binomial"},
                                {"label": "Chi²", "value": "chisquared"},
                            ],
                            value="normal",
                        ),
                    ],
                ),
                html.Div(
                    children=[
                        html.P("Stichprobengröße"),
                        dcc.Input(
                            id="n-input",
                            placeholder="Stichprobengröße",
                            type="number",
                            value=100,
                        ),
                    ],
                ),
            ],
            # mit style kann man CSS-Formatierungen verwenden
            style={
                "backgroundColor": "#DDDDDD",
                "maxWidth": "800px",
                "padding": "10px 20px",
            },
        ),
        html.Div(
            children=[
                html.H2("Histogramm"),
                dcc.Graph(id="histogramm", figure=hist("normal", 100)),
            ],
            style={
                "backgroundColor": "#DDDDDD",
                "maxWidth": "800px",
                "marginTop": "10px",
                "padding": "10px 20px",
            },
        ),
    ]
)

 

Und so wird unser Dashboard im Browser dargestellt:

Python Dashboard mit Eingabeelementen und Grafik (Histogramm)

Erste Interaktivität in unserem Plotly Dash Dashboard

So, nun wird es langsam interessant. Denn nun geht es an die Interaktivität. Diese wird über Decorators bei den entsprechenden Funktionen erreicht.

Dazu definiert man zum einen den Output, also welches Element des Layouts von der Funktion zurückgegeben wird. In unserem Fall ist es der Parameter figure der Grafik dcc.Graph. Damit wir diese identifizieren können, benutzen wir die id-Parameter aus dem Layout.

Dann definieren wir die Inputs, also bei Änderung welcher Elemente die Funtion aufgerufen werden soll. Da das mehrere sein können, wird das als Liste realisiert. In unserem Fall ist es einmal das Verteilungsdropdown mit ID „verteilung-dropdown“ und das Input-Feld mit ID „n-input“.

Unsere Funktion hist bekommt also einfach den Decorator aufgesetzt. Dafür müssen wir noch zwei Komponenten importieren, nämlich Input und Output. Die Import-Zeile sollte bei den anderen Import-Zeilen oben im Skript stehen.

from dash.dependencies import Input, Output
 
@app.callback(
    Output("histogramm", "figure"),
    [Input("verteilung-dropdown", "value"), Input("n-input", "value")],
)
def hist(verteilung, n):
    data = None
    if verteilung == "normal":
        data = np.random.normal(loc=0, scale=1, size=n)
    elif verteilung == "binomial":
        data = np.random.binomial(n=10, p=0.5, size=n)
    elif verteilung == "chisquared":
        data = np.random.chisquare(df=5, size=n)
    return px.histogram(data)

Eine kleine Änderung machen wir noch in unserem Layout. In der dcc.Graph-Funktion definieren wir nur die id, lassen also den figure-Parameter weg. Durch unseren Decorator wird die Funktion hist sowieso beim Start aufgerufen und so vermeiden wir den zweifachen Aufruf.

dcc.Graph(id="histogramm")

Und nun können wir im Browser Dropdown und das Input-Feld nutzen. Die Grafik wird direkt aktualisiert.

Interaktives Dashboard in Python: Die Eingaben sind per Decorator mit dem Histogramm verknüpft

Deployment von Plotly Dash in der Cloud

Nun wollen wir unser Dashboard ja nicht nur auf unserem Rechner laufen lassen. Und dass jemand anderem erstmal Python und die benötigten Libraries installiert, ist auch ziemlich umständlich. Also wollen wir das Plotly Dashboard in der Cloud hosten, damit man bequem per Browser darauf zugreifen kann. Theoretisch kann man auch den eigenen Rechner oder einen Raspberry Pi als Server missbrauchen, aber der müsste dann ja immer laufen. Da ist eine Cloud-Lösung die bessere Wahl.

Leider ist das „Ins-Internet-stellen“ (deployment genannt) nicht ganz so einfach, denn man braucht eine vernünftige Umgebung mit den benötigten Packages und einen Webserver. Der Webserver, der bei dash bzw. flask dabei ist, ist für die produktive Umgebung nicht so geeignet.

Eine schöne, kostenlose Möglichkeit für die ersten Schritte war pythonanywhere. Das funktioniert aber aktuell nicht mehr vernünftig, denn das Package numpy zickt mit dem dortigen Webserver uWSGI herum. Aber als Data Scientist auf numpy und pandas zu verzichten, geht nicht wirklich.

Eine zweite kostenlose Möglichkeit ist heroku. Dafür muss man sich aber die Heroku-Kommandozeile herunterladen und ein bisschen was von git verstehen (hier mein Blogpost über Git). Das folgt dann mal in einem separaten Tutorial.

Als dritte Möglichkeit und gute Alternative zu AWS empfehle ich Euch Linode*, wo Ihr eine virtuelle Linux-Maschine für wenig Geld bekommt.

 

So long, so good. Viel Spaß beim Entdecken der unzähligen Möglichkeiten von Plotly Dash.

Euer Holger