3. Les dashboards
L’interaction avec les graphiques est possible dans un contexte statique mais limitée car elle n’agit que sur la présentation de données pré chargées dans le fichier JavaScript. C’est là qu’intervient le dashboard, qui est en fait une application client serveur, le client se chargeant de la présentation des données alors que la sélection est gérée côté serveur.
Dans l’éco système Python la technologie Dash/Plotly est utilisée. C’est l’équivalent de R/Shiny.
3.1. Dash overview
Dash est un framework Python permettant de développer des applications web pour la visualisation des données. Il est construit sur des technologies matures et largement déployées:
Flask est un micro framework web Python
Plotly est une bibliothèque graphique basée sur D3.js, la référence en matière de data visualisation
React. est une bibliothèque JavaScript développée par Facebook
3.1.1. Architecture
Dash propose une couche d’abstraction au dessus des technologies présentées ci dessus et des protocoles requis pour développer ce type d’application. Les applications sont déployées et partagées à travers un navigateur web.
Chris Parmer, le fondateur de Dash, expose les principales caractéristiques du framework dans une présentation vidéo YouTube.
Côté front end, Les composants HTML sont générés par React dont l’ensemble des composants est donc disponible pour le design de la page web.
Côté back end, une application Dash est une instance d’une application Flask dont toutes les propriétés sont configurables. L’ensemble des plugins Flask est donc disponible pour une utilisation avancée. Les composants Dash sont des classes Python permettant de packager les composants React. Ceux ci sont sérialisés au format JSON et transférés au front end via des requêtes HTTP.
3.2. Un premier dashboard
Un dashboard Dash est une application Flask. Le code ci dessous permet de produire un graphique élémentaire. Il est écrit avec la version 1.13.3
de Dash.
1# filename = 'dash-01.py'
2
3#
4# Imports
5#
6
7import plotly_express as px
8
9import dash
10import dash_core_components as dcc
11import dash_html_components as html
12
13#
14# Data
15#
16
17year = 2002
18
19gapminder = px.data.gapminder() # (1)
20years = gapminder["year"].unique()
21data = { year:gapminder.query("year == @year") for year in years} # (2)
22
23#
24# Main
25#
26
27if __name__ == '__main__':
28
29 app = dash.Dash(__name__) # (3)
30
31 fig = px.scatter(data[year], x="gdpPercap", y="lifeExp",
32 color="continent",
33 size="pop",
34 hover_name="country") # (4)
35
36
37 app.layout = html.Div(children=[
38
39 html.H1(children=f'Life expectancy vs GDP per capita ({year})',
40 style={'textAlign': 'center', 'color': '#7FDBFF'}), # (5)
41
42 dcc.Graph(
43 id='graph1',
44 figure=fig
45 ), # (6)
46
47 html.Div(children=f'''
48 The graph above shows relationship between life expectancy and
49 GDP per capita for year {year}. Each continent data has its own
50 colour and symbol size is proportionnal to country population.
51 Mouse over for details.
52 '''), # (7)
53
54 ]
55 )
56
57 #
58 # RUN APP
59 #
60
61 app.run_server(debug=True) # (8)
On collecte les données (1)
On crée un dictionnaire dont les clés sont les années et les valeurs, les données de l’année concernée (2)
On crée une instance de la classe Dash (3)
on crée la figure destinée à être affichée dans le dashboard (4)
la page est composée d’éléments HTML standard (5)(7)
la bibliothèque Dash Core Components fournit l’élément Graph qui permet d’inclure un graphique Plotly/Plotly Express (6)
lancement de l’application (8)
Cette application est lancée dans une console:
$ python3 dash-01.py
Dash is running on http://127.0.0.1:8050/
Warning: This is a development server. Do not use app.run_server
in production, use a production WSGI server like gunicorn instead.
* Serving Flask app "dash-01" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
Et le résultat s’observe dans la fenêtre d’un navigateur à l’adresse indiquée dans la console (ici http://127.0.0.1:8050/
)

Le code source de la page est simple et structuré. Le rendu est confié aux fonctions JavaScript encapsulées dans le <footer>
.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
<title>Dash</title>
<link rel="icon" type="image/x-icon" href="/_favicon.ico?v=1.13.3">
</head>
<body>
<div id="react-entry-point">
<div class="_dash-loading">
Loading...
</div>
</div>
<footer>
<script id="_dash-config" type="application/json">{"url_base_pathname": null, "requests_pathname_prefix": "/", "ui": true, "props_check": true, "show_undo_redo": false, "suppress_callback_exceptions": false, "hot_reload": {"interval": 3000, "max_retry": 8}}</script>
<script src="/_dash-component-suites/dash_renderer/polyfill@7.v1_5_0m1592837063.8.7.min.js"></script>
<script src="/_dash-component-suites/dash_renderer/react@16.v1_5_0m1592837063.13.0.js"></script>
<script src="/_dash-component-suites/dash_renderer/react-dom@16.v1_5_0m1592837063.13.0.js"></script>
<script src="/_dash-component-suites/dash_renderer/prop-types@15.v1_5_0m1592837063.7.2.js"></script>
<script src="/_dash-component-suites/dash_core_components/dash_core_components.v1_10_1m1592837063.min.js"></script>
<script src="/_dash-component-suites/dash_core_components/dash_core_components-shared.v1_10_1m1592837063.js"></script>
<script src="/_dash-component-suites/dash_html_components/dash_html_components.v1_0_3m1592837063.min.js"></script>
<script src="/_dash-component-suites/dash_renderer/dash_renderer.v1_5_0m1592837063.dev.js"></script>
<script id="_dash-renderer" type="application/javascript">var renderer = new DashRenderer();</script>
</footer>
</body>
</html>
La capture d’écran ci dessus est statique et ne rend donc pas compte des capacités dynamiques apportées par le framework Dash/Plotly. Dans le navigateur local de votre machine, vérifier que les possibilités de sélection, de zoom et d’affichage des données au survol (hover) sont opérationnelles.
3.3. Interaction simple
Le dashboard précédent est totalement statique et n’apporte pas de valeur ajoutée par rapport à un graphique Plotly/Plotly Express. Nous allons remédier à ça en ajoutant un widget de sélection de l’année sur le front end.
html.Label('Year'),
dcc.Dropdown(
id="year-dropdown",
options=[
{'label': '1952', 'value': 1952},
{'label': '1957', 'value': 1957},
{'label': '1962', 'value': 1962},
{'label': '1967', 'value': 1967},
{'label': '1972', 'value': 1972},
{'label': '1977', 'value': 1977},
{'label': '1982', 'value': 1982},
{'label': '1987', 'value': 1987},
{'label': '1992', 'value': 1992},
{'label': '1997', 'value': 1997},
{'label': '2002', 'value': 2002},
{'label': '2007', 'value': 2007},
],
value=2007,
),
Il faut ensuite écrire la fonction permettant de modifier la figure affichée en fonction de l’année sélectionnée. Dash fonctionne avec un système de décorateur qui doit être défini après le layout du graphique:
les paramètres du décorateur
@app.callback()
font le lien entre les composants HTML du front end à lire/modifiercôté back end, la fonction décorée construit la figure à partir du paramètre passé en argument et la retourne
@app.callback(
Output(component_id='graph1', component_property='figure'), # (1)
[Input(component_id='year-dropdown', component_property='value')] # (2)
)
def update_figure(input_value): # (3)
return px.scatter(data[input_value], x="gdpPercap", y="lifeExp",
color="continent",
size="pop",
hover_name="country") # (4)
: l’objet’
Output
passé en paramètre au décorateur désigne le composant HTML (component_id
) que l’application va modifier dynamiquement et la propriété mise en jeu (component_property
).
: l’objet
Input
désigne le composant HTML (component_id
) via lequel l’utilisateur va interagir et sa propriété (component_property
).
: le paramètre
input_value
passé à la fonction de modification est lié dynamiquement à la propriété passée en paramètre àInput
. L’appel de la fonction est déclenché par une modification de la propriété du widget déclaré dansInput
. Le nom de la fonction est arbitraire.
: la valeur de retour de la fonction est affectée à la propriété du widget déclaré dans
Output
et le graphique est mis à jour en conséquence.
: les objets
Input
etOutput
doivent être importés depuis le module dash.dependencies.
3.4. Modifier plusieurs éléments
Le dashboard précédent est fonctionnel mais le titre et la légende ne reflètent pas le changement dynamique de l’année.
Dash permet de modifier plusieurs composants HTML, en passant ceux ci en argument du décorateur sous forme d’une list
l
d’objets Output
.
La fonction décorée doit alors retourner une list
de même taille, dont chacun des éléments sera passé aux composants de l
. L’ordre et le nombre des valeurs de retour doit être en bijection avec l
.
Exercice
Modifier les paramètres du décorateur et la fonction décorée pour mettre à jour dynamiquement le titre et la légende. Il fauudra ajouter un id
aux éléments à modifier pour pouvoir les référencer.
Le résultat doit être similaire au dashboard ci dessous.

Exercice
La définition de l’élément dcc.Dropdown
est statique. Utiliser les années présentes dans la dataframe pour renseigner les propriétés.
Exercice
L’utilisation d’un widget dcc.Dropdown
n’est peut être pas optimale en terme d’ergonomie. Utiliser plutôt le widget dcc.Slider
pour sélectionner dynamiquement l’année.
Cet exemple illustre le principe utilisé par Dash pour construire des dashboards interactifs.
Il est également possible de construire des widgets dynamiques tel que décrit dans le paragraphe “Dash App With Chained Callbacks” du document Basic Dash Callbacks
ou encore d’utiliser les informations de survol pour modifier dynamiquement un graphe tel que décrit dans le paragraphe “Update Graphs on Hover” du document Interactive Visualizations.
On peut également conditionner l’exécution des callbacks à l’état d’un widget. Ceci est décrit dans le paragraphe “Dash App With State” du document Basic Dash Callbacks.
D’autres exemples avancés sont décrits dans le chapitre Sharing Data Between Callbacks.
3.5. Animations
Le principe des callbacks est puissant mais nécessite une interaction pour rafraichir la page.
Le composant dcc.Interval
permet un rafraichissement automatique, ce qui autorise la création d’animations.
Note
dcc.Interval
is a component that will fire a callback periodically. Use dcc.Interval
to update your app in realtime without needing to refresh the page or click on any buttons. Voir la documentation.
Ce compteur peut modifier cycliquement le paramètre value
du dcc.Slider
qui modifie dynamiquement le graphique. Classiquement, on rajoute ce composant dans le layout de la page
dcc.Interval( id='interval',
interval=1*1000, # in milliseconds
n_intervals=0),
Et on établit la relation entre le compteur et la valeur du dcc.Slider
avec un callback:
@app.callback( Output('year-slider', 'value'),
[Input('interval', 'n_intervals')])
def on_tick(n_intervals):
if n_intervals is None: return 0
return years[(n_intervals+1)%len(years)]
Le dashboard est fonctionnel mais l’animation est automatique et est joué cycliquement dès le démarrage de l’application. On peut améliorer l’interactivité en rajoutant des éléments de contrôle.
Exercice
Ajouter deux composants dcc.Button
Play/Pause pour démarrer/stopper l’animation. On utilisera la propriété disabled
du composant dcc.Interval
. On peut également ajouter un contrôle dynamique de la vitesse de défilement en utilisant la propriété interval
.
Le résultat doit être similaire au dashboard ci dessous.

3.6. Aller plus loin
Le principe des dashboard a été présenté, ainsi que quelques possibilités d’interactions. Il sera très utile de porter un oeil attentif sur le User Guide qui contient quelques tutorials et des notions plus avancées…