Entwickelt als eine Open-Source-Bibliothek von Plotly, baut das Python Framework Dash auf Flask, Plotly.js und React.js auf. Das Framework ermöglicht die Erstellung von interaktiven Webapplikationen in purem Python und eignet sich besonders für das Teilen von Datenanalysen.
Solltest du Interesse an der Erstellung von interaktiven Grafiken mit Python haben, kann ich den Blog meines Kollegen Markus, , sehr empfehlen.
Ein grundlegendes Verständnis von HTML und CSS ist für die Erstellung von Webapplikationen empfohlen. Um diesem Blog folgen zu können, werden wir alle notwendigen externen Ressourcen zur Verfügung stellen und die Rollen von HTML und CSS in einem Dash(board) erläutern.
Der Source Code ist auf verfügbar.
Vorraussetzungen
Das Projekt besteht aus einem Stylesheet style.css
, den Beispieldaten stockdata2.csv
und der eigentlichen Dash-Applikation app.py
.
Laden des Stylesheet
Die Verwendung eines Stylesheets ist für die Funktionalitäten des Dashboards nicht notwendig. Damit das Dashboard jedoch so aussieht, wie in unseren Beispielen, kann die Datei von unserem heruntergeladen werden. Das verwendete Stylesheet is eine leicht veränderte Version des Stylesheets der . Dash lädt automatisch .css-Dateien, die sich im Unterordner assets
befinden.
dashapp
|--assets
|-- style.css
|--data
|-- stockdata2.csv
|-- app.py
Die Dokumentation zu externen Ressourcen (u.a. Stylesheets) kann unter folgendem Link gefunden werden: https://dash.plot.ly/external-resources
Laden der Daten
Für unser Dashboard verwenden wir den Datensatz . Der Datensatz hat folgende Struktur:
date | stock | value | change |
---|---|---|---|
2007-01-03 | MSFT | 23.95070 | -0.1667 |
2007-01-03 | IBM | 80.51796 | 1.0691 |
2007-01-03 | SBUX | 16.14967 | 0.1134 |
import pandas as pd
# Load data
df = pd.read_csv('data/stockdata2.csv', index_col=0, parse_dates=True)
df.index = pd.to_datetime(df['Date'])
Erste Schritte – Wie man eine Dash-App startet
Nach der Installation von Dash (Anleitung kann gefunden werden) können wir die App starten. Die folgenden Zeilen importieren die benötigten Pakete dash
und dash_html_components
. Ohne eine Definition eines Layouts der App, kann die Applikation nicht gestartet werden. Eine leere html.Div
genügt, um die App zu starten.
import dash
import dash_html_components as html
Web Applikation Framework
# Initialise the app
app = dash.Dash(__name__)
# Define the app
app.layout = html.Div()
# Run the app
if __name__ == '__main__':
app.run_server(debug=True)
Wie eine .css-Datei das Layout beeinflusst
Das Modul dash_html_components
beinhaltet verschiedene HTML-Komponenten. Mehr Informationen zu den Komponenten unter: https://dash.plot.ly/dash-html-components
HTML-Komponenten können über das children
-Attribut verschachtelt werden.
app.layout = html.Div(children=[
html.Div(className='row', # Define the row element
children=[
html.Div(className='four columns div-user-controls'), # Define the left element
html.Div(className='eight columns div-for-charts bg-grey') # Define the right element
])
])
Das erste html.Div()
hat ein untergeordnetes Element (eng. child). Das Element is ein weiteres html.Div()
mit dem Namen (className
) row
, welches der Container für unseren Inhalt sein wird. Die weiteren untergeordneten Elemente sind four-columns div-user-controls
und eight columns div-for-charts bg-grey
.
Der Stil der div
-Komponenten stammt aus unserer style.css
-Datei.
Wir starten damit, dass wir der App weitere Informationen zur Verfügung stellen, wie Titel und Beschreibung. Zum Element four columns div-user-controls
fügen wir die Komponenten H2
für die Überschrift und P
als Paragraph hinzu.
children = [
html.H2('Dash - STOCK PRICES'),
html.P('''Visualising time series with Plotly - Dash'''),
html.P('''Pick one or more stocks from the dropdown below.''')
]
Die Basics des App-Layouts
Ein weiteres Feature von Flask (und somit Dash) ist das . Das Feature macht es möglich, eine Änderung im Code ohne einen Neustart der App sehen zu können.
Zusätzlich wird durch debug=True
ein Feld rechts in der unteren Ecke der App angezeigt, worin wir auf Fehlermeldungen und den Callback Graph
zugriff haben. Wir werden im letzten Abschnitt des Artikels auf Callback Graph
zurück kommen, nachdem wir interaktive Funktionalitäten implementiert haben.
Erstellen von Grafiken in Dash – Wie man eine Plotly-Grafik anzeigt
Nachdem wir die grundlegenden Container für unsere App erstellt haben, erstellen wir jetzt einen Plotly-Graphen. Die Komponente dcc.Graph
aus den dash_core_components
nutzt das gleiche figure
-Argument wie das plotly.py Paket. Dash übersetzt jeden Aspekt des Charts zu einem Schlüssel-Wert-Paar, welches von der darunterliegenden JavaScript-Bibliothek Plotly.js verarbeitet wird.
Im folgenden Abschnitt verwenden wir die Expressversion von plotly.py
und das Paket Dash Core Components. Nach der Installation von Dash sollte plotly in der Entwicklungsumgebung bereits verfügbar sein.
import dash_core_components as dcc
import plotly.express as px
beinhaltet eine Kollektion von nützlichen und einfach zu bedienenden Komponenten, welche zur Interaktivität und Funktionalität des Dashboard beitragen.
ist die Expressversion von plotly.py
, welche die Erstellung von Plotly-Grafiken vereinfacht, mit der Einschränkung dass es die Flexibilität verringert.
Um einen Plot in der rechten Seite unserer App zu zeichnen, wird ein dcc.Graph()
als ein Unterelement dem html.Div()
mit dem Namen eight columns div-for-charts bg-grey
hinzugefügt. Die Komponente dcc.Graph()
kann für jede von plotly-gestützte Visualisierung genutzt werden. In unserem Fall wird die figure
von px.line()
aus dem Paket plotly.express
erstellt. Das Layout der Grafik ändern wir durch die Methode update_layout()
. Um den Hintergrund der Grafik transparent zu machen, setzen wir die Farbe auf rgba(0, 0, 0, 0)
. Ohne den Hintergrund transparent zu setzen, würde eine große weiße Box in der Mitte unserer App stehen. Da dcc.Graph()
lediglich die Grafik anzeigt, können wir nach der Erstellung die Eigenschaften der Grafik nicht ohne weiteres ändern.
dcc.Graph(id='timeseries',
config={'displayModeBar': False},
animate=True,
figure=px.line(df,
x='Date',
y='value',
color='stock',
template='plotly_dark').update_layout(
{'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)'})
)
Erstellen einer Dropdown-Liste
Eine weitere Komponente ist dcc.Dropdown()
, welche genutzt wird – du kannst es dir vielleicht denken –, um eine Dropdown-Liste zu erstellen. Die verfügbaren Optionen in einem Dropdown werden entweder durch eine (Python) Liste gegeben oder innerhalb einer Funktion definiert.
Für unsere Dropdown-Liste erstellen wir eine Funktion, welche eine Liste von Dictionaries ausgibt. Die Liste besteht aus den Dictionaries mit den Schlüsseln label
und value
. Hierdurch werden die Optionen unserer Dropdown-Liste definiert. Der Wert von label
ist der angezeigte Text in der App und der Wert von value
kann innerhalb der App als Input für Funktionen verwendet werden. Solltest du beispielsweise den vollen Namen des Unternehmens anzeigen, kann der Wert von label
auf Microsoft
gesetzt werden. Der Einfachheit halber wählen wir für label
und value
die gleichen Werte.
Füge die folgende Funktion zum Skript hinzu, bevor du das Layout der App definierst:
# Creates a list of dictionaries, which have the keys 'label' and 'value'.
def get_options(list_stocks):
dict_list = []
for i in list_stocks:
dict_list.append({'label': i, 'value': i})
return dict_list
Nach unserer Funktion get_option
, können wir das Element dcc.Dropdown()
von den Dash Core Components zu unserer App hinzufügen. Füge html.Div()
als untergeordenetes Element zur Liste der Elemente in four columns div-user-controls
hinzu und setze das Argument className=div-for-dropdown
. html.Div()
hat ein Unterelement, dcc.Dropdown()
.
Wir möchten die Möglichkeit habe, nicht nur einen einzelnen Wert, sondern mehrere Werte zur gleichen Zeit auszuwählen und setzen daher das Argument multi=True
. Da die App jedoch nicht leer erscheinen soll, setzen wir zudem einen initialen Wert für value
.
html.Div(className='div-for-dropdown',
children=[
dcc.Dropdown(id='stockselector',
options=get_options(df['stock'].unique()),
multi=True,
value=[df['stock'].sort_values()[0]],
style={'backgroundColor': '#1E1E1E'},
className='stockselector')
],
style={'color': '#1E1E1E'})
id
und options
Argumente des dcc.Dropdown()
sind wichtig für den nächsten Abschnitt. Um unterschiedliche Styles der Dropdown-Liste auszuprobieren, folge diesem
Mit Callbacks arbeiten
Wie man der App interaktive Funktionalitäten hinzufügt
Callbacks (eng. für Rückruffunktion) sind für die Interaktivität in einem Dashboard verantwortlich. Sie nehmen Inputs entgegen, z.B. die ausgewählten Optionen einer Dropdown-Liste und leiten die Werte zu einer Funktion weiter. Der Output der Funktion wird durch den Callback an ein definiertes Element geleitet. Wir werden im nächsten Schritt eine Funktion schreiben, welche eine Grafik basierend auf den Namen von Unternehmen ausgibt. In unserer Implementierung wird die erstellte Grafik an eine dcc.Graph()
-Komponente geleitet.
Momenten hat die Auswahl der Dropdown-Liste noch keinen Einfluss auf die angezeigte Grafik. Um diese Funktionalität zu implementieren werden wir einen Callback nutzen. Der Callback übernimmt die Kommunikation zwischen der Dropdown-Liste mit der ID id='stockselector'
und dem Graphen 'timeseries'
. Zur Vorbereitung entfernen wir die zuvor erstellte Grafik, da wir die figure
dynamisch erstellen möchten.
In unserer App möchten wir zwei Graphen haben, weshalb wir eine weitere dcc.Graph()
-Komponente erstellen und mit dem Argument id='change'
identifizierbar machen.
- Entfernen der
figure
von der Komponentedcc.Graph(id='timeseries')
- Hinzufügen der Komponente
dcc.Graph(id='change')
- Beide Komponenten sollten Unterelemente von
eight columns div-for-charts bg-grey
sein.
dcc.Graph(id='timeseries', config={'displayModeBar': False})
dcc.Graph(id='change', config={'displayModeBar': False})
Callbacks erhöhen die Interaktivität deiner Anwendung. Sie können Eingaben von Komponenten entgegennehmen, z. B. bestimmte Aktien, die über ein Dropdown-Menü ausgewählt werden, diese Eingaben an eine Funktion weitergeben und die von der Funktion zurückgegebenen Werte an Komponenten zurückgeben.
In unserer Implementierung wird ein Callback ausgelöst, sobald der Nutzer eine Aktie im Dropdown auswählt. Der Callback nimmt den Wert des dcc.Dropdown()
entgegen (Input) und leitet den Wert an die Funktionen update_timeseries()
und update_change()
weiter. Die Funktionen filtern die Daten und erstellen eine auf den Inputs basierende Grafik. Der Callback leitet anschließend den Output der Funktionen (Grafik) an die als Output spezifizierten Elemente weiter.
Der Callback ist als Decorator für eine Funktion implementiert. Mehrere Inputs und Outputs sind möglich. Wir werden jedoch bei einem Input und einem Output bleiben. Wir importieren die Objekte dash.dependencies import Input, Output
.
Füge die folgende Zeile zu deinemSkript hinzu.
from dash.dependencies import Input, Output
und Output()
nehmen die id
einer Komponente (bspw. dcc.Graph(id='timeseries')
hat die id 'timeseries'
) sowie die Eigenschaft einer Komponente (hier figure
) als Argument entgegen.
Beispiel Callback:
# Update Time Series
@app.callback(Output('id of output component', 'property of output component'),
[Input('id of input component', 'property of input component')])
def arbitrary_function(value_of_first_input):
'''
The property of the input component is passed to the function as value_of_first_input.
The functions return value is passed to the property of the output component.
'''
return arbitrary_output
Wenn wir durch unseren stockselector
eine Zeitreihe für einen oder mehrere Aktien anzeigen möchten, benötigen wir eine Funktion. Der value
unseres Inputs ist die Liste von ausgewählten Unternehmen aus der Dropdown-Liste.
Implementieren von Callbacks
Die Funktion zeichnet die Linien (eng. traces) der Plotly-Grafik, basierend auf den übergebenen Aktiennamen werden die Linien gezeichnet und zusammen als eine figure
ausgegeben, welche von dcc.Graph()
angezeigt werden kann. Die Inputs für unsere Funktion werden in der Reihenfolge übergeben, in welcher Sie im Callback gesetzt wurden. (Bemerkung: Seit Dash 2.0 muss dies nicht immer der Fall sein.)
Update der Grafik (figure
) für die time series
:
@app.callback(Output('timeseries', 'figure'),
[Input('stockselector', 'value')])
def update_timeseries(selected_dropdown_value):
''' Draw traces of the feature 'value' based one the currently selected stocks '''
# STEP 1
trace = []
df_sub = df
# STEP 2
# Draw and append traces for each stock
for stock in selected_dropdown_value:
trace.append(go.Scatter(x=df_sub[df_sub['stock'] == stock].index,
y=df_sub[df_sub['stock'] == stock]['value'],
mode='lines',
opacity=0.7,
name=stock,
textposition='bottom center'))
# STEP 3
traces = [trace]
data = [val for sublist in traces for val in sublist]
# Define Figure
# STEP 4
figure = {'data': data,
'layout': go.Layout(
colorway=["#5E0DAC", '#FF4F00', '#375CB1', '#FF7400', '#FFF400', '#FF0056'],
template='plotly_dark',
paper_bgcolor='rgba(0, 0, 0, 0)',
plot_bgcolor='rgba(0, 0, 0, 0)',
margin={'b': 15},
hovermode='x',
autosize=True,
title={'text': 'Stock Prices', 'font': {'color': 'white'}, 'x': 0.5},
xaxis={'range': [df_sub.index.min(), df_sub.index.max()]},
),
}
return figure
STEP 1
- Ein
trace
wird für jede Aktie gezeichnet. Erstellen einer leerenlist
für jedentrace
der Plotly-Grafik.
STEP 2
In einem for-loop, wird ein trace
für die Plotly-Grafik von go.Scatter
erstellt. (go
stammt aus dem Import import plotly.graph_objects as go
)
- Iteriere über die selektierten Aktien im Drodown, zeichne ein
trace
und hänge dastrace
an die Liste aus Step 3 an.
STEP 3
- Ebne (eng. flatten) die Liste.
STEP 4
Plotly-Grafiken sind Dictionaries mit den Schlüsseln data
und layout
. Der Wert von data
ist unsere Liste mit den erstellten traces
. Das layout
wird mit go.Layout()
definiert.
- Füge die
traces
zu unsererfigure
(Grafik) hinzu - Definiere das Layout der
figure
Diese gleichen Schritte werden für unsere zweite Grafik durchgeführt, mit dem Unterschied, dass die Daten auf der y-Achse auf change
gesetzt werden.
Update der Grafik change
:
@app.callback(Output('change', 'figure'),
[Input('stockselector', 'value')])
def update_change(selected_dropdown_value):
''' Draw traces of the feature 'change' based one the currently selected stocks '''
trace = []
df_sub = df
# Draw and append traces for each stock
for stock in selected_dropdown_value:
trace.append(go.Scatter(x=df_sub[df_sub['stock'] == stock].index,
y=df_sub[df_sub['stock'] == stock]['change'],
mode='lines',
opacity=0.7,
name=stock,
textposition='bottom center'))
traces = [trace]
data = [val for sublist in traces for val in sublist]
# Define Figure
figure = {'data': data,
'layout': go.Layout(
colorway=["#5E0DAC", '#FF4F00', '#375CB1', '#FF7400', '#FFF400', '#FF0056'],
template='plotly_dark',
paper_bgcolor='rgba(0, 0, 0, 0)',
plot_bgcolor='rgba(0, 0, 0, 0)',
margin={'t': 50},
height=250,
hovermode='x',
autosize=True,
title={'text': 'Daily Change', 'font': {'color': 'white'}, 'x': 0.5},
xaxis={'showticklabels': False, 'range': [df_sub.index.min(), df_sub.index.max()]},
),
}
return figure
Starte die App erneut. In der App können nun über die Dropdown-Liste die verschiedenen Aktien ausgewählt werden. Für jedes selektierte Element wird ein Line-Plot gezeichnet und angezeigt. Standardmäßig hat die Dropdown-Komponente einige Funktionalitäten, wie bspw. die Suchfunktion, weshalb die Selektion von einzelnen Elementen auch bei vielen Auswahlmöglichkeiten sehr einfach ist.
Visualisieren von Callbacks – Callback Graph
Mit den implementierten Callbacks ist die App fertig. Werfen wir nun einen Blick auf den sogenannten Callback Graph. Wenn die App mit debug=True
gestartet wird, erscheint in der unteren rechten Ecke ein Feld. Hierüber kann eine visuelle Repräsentation des Callback Graph angezeigt werden, welchen wir im Skript definiert haben. Der Graph zeigt, dass die Komponenten timeseries
und change
eine figure
anzeigen, die auf den ausgewählten Werten im stockselector
basieren.
Sollten Callbacks nicht so funktionieren, wie es geplant wurde, ist dieses Tool sehr hilfreich beim Debuggen.
Fazit
Fassen wir die wichtigsten Elemente von Dash zusammen. Um die App zu starten, benötigt man nur ein paar Zeilen Code. Ein grundlegendes Verständnis von HTML und CSS genügt, um den Stil eines Dashboards anzupassen. Interaktive Grafiken können ohne Probleme implementiert werden, da Dash für das Arbeiten mit interaktiven Plotly-Grafiken entwickelt wurde. Über Callbacks, welche den Input von Nutzenden an Funktionen weiterleiten, werden unterschiedliche Komponenten miteinander verbunden.
Wenn dir der Blogartikel gefallen hat, kontaktiere mich gerne über oder per . Ich bin neugierig, welche weiteren Anwendungsmöglichkeiten das Framework bietet und freue mich auf Fragen zu Daten, Machine Learning und KI sowie den spannenden Lösungen, an denen wir bei arbeiten.
Vielen Dank!