Comment et pourquoi j'ai utilisé Plotly (au lieu de D3) pour visualiser mes données Lollapalooza

D3.js est une bibliothèque JavaScript géniale, mais elle a une courbe d'apprentissage très raide. Cela rend la tâche de créer une visualisation précieuse quelque chose qui peut demander beaucoup d'efforts. Cet effort supplémentaire est acceptable si votre objectif est de créer de nouvelles visualisations de données créatives, mais ce n'est souvent pas le cas.

Souvent, votre objectif peut simplement être de créer une visualisation interactive avec des graphiques bien connus . Et si vous n'êtes pas un ingénieur front-end, cela peut devenir un peu délicat.

En tant que data scientists, l'une de nos principales tâches est la manipulation des données. Aujourd'hui, le principal outil que j'utilise pour cela est Pandas (Python). Et si je vous disais que vous pouvez créer de superbes graphiques interactifs pour le Web directement à partir de vos dataframes Pandas ? Eh bien, vous pouvez! Nous pouvons utiliser Plotly pour cela.

Pour mémoire, il existe également des bibliothèques d'API Plotly pour Matlab, R et JavaScript, mais nous allons nous en tenir à la bibliothèque Python ici.

Pour être honnête, Plotly est construit sur d3.js (et stack.gl). La principale différence entre D3 et Plotly est que Plotly est spécifiquement une bibliothèque de graphiques .

Construisons un graphique à barres pour savoir comment fonctionne Plotly.

Créer un graphique à barres avec plotly

Il y a 3 concepts principaux dans la philosophie de Plotly:

  • Les données
  • Disposition
  • Figure

Les données

L'objet Data définit ce que nous voulons afficher dans le graphique (c'est-à-dire les données). Nous définissons une collection de données et les spécifications pour les afficher sous forme de trace . Un objet Data peut avoir de nombreuses traces. Pensez à un graphique en courbes avec deux lignes représentant deux catégories différentes: chaque ligne est une trace.

Disposition

L'objet Layout définit des fonctionnalités qui ne sont pas liées aux données (comme le titre, les titres des axes, etc.). Nous pouvons également utiliser la mise en page pour ajouter des annotations et des formes au graphique.

Figure

L'objet Figure crée l'objet final à tracer. C'est un objet qui contient à la fois des données et une mise en page.

Les visualisations Plotly sont créées avec plotly.js. Cela signifie que l'API Python n'est qu'un package pour interagir avec la bibliothèque plotly.js . Le plotly.graph_objsmodule contient les fonctions qui vont générer des objets graphiques pour nous.

Ok, maintenant nous sommes prêts à créer un graphique à barres:

import plotly.graph_objs as goimport pandas as pdimport plotly.offline as offline
df = pd.read_csv("data.csv")
df_purchases_by_type = df.pivot_table( index = "place", columns = "date", values = "price", aggfunc = "sum" ).fillna(0)
trace_microbar = go.Bar( x = df_purchases_by_type.columns, y = df_purchases_by_type.loc["MICROBAR"])
data = [trace_microbar]
layout = go.Layout(title = "Purchases by place", showlegend = True)
figure = go.Figure(data = data, layout = layout)
offline.plot(figure)

Remarque: dans cet article, nous ne parlerons pas de ce que je fais avec les dataframes. Mais si vous souhaitez un article à ce sujet, faites-le moi savoir dans les commentaires?

D'accord, nous voulons d'abord afficher les barres d'une catégorie (un endroit appelé "MICROBAR"). Nous créons donc un objet de données (une liste) avec go.Bar()(une trace) spécifiant les données pour les axes x et y. Trace est un dictionnaire et les données sont une liste de dictionnaires. Voici le trace_microbarcontenu (notez la clé de type):

{'type': 'bar', 'x': Index(['23/03/2018', '24/03/2018', '25/03/2018'], dtype="object", name="date"), 'y': date 23/03/2018 0.0 24/03/2018 0.0 25/03/2018 56.0 Name: MICROBAR, dtype: float64}

Dans l'objet Layout, nous définissons le titre du graphique et le paramètre showlegend. Ensuite, nous enveloppons les données et la mise en page dans une figure et appelons plotly.offline.plot()pour afficher le graphique. Plotly a différentes options pour afficher les graphiques, mais restons ici avec l'option hors ligne. Cela ouvrira une fenêtre de navigateur avec notre graphique.

Je veux tout afficher dans un graphique à barres empilées, nous allons donc créer une liste de données avec toutes les traces (lieux) que nous voulons afficher et définir le barmodeparamètre à empiler .

import plotly.graph_objs as goimport pandas as pdimport plotly.offline as offline
df = pd.read_csv("data.csv")
df_purchases_by_place = df.pivot_table(index="place",columns="date",values="price",aggfunc="sum").fillna(0)
data = []
for index,place in df_purchases_by_place.iterrows(): trace = go.Bar( x = df_purchases_by_place.columns, y = place, name=index ) data.append(trace)
layout = go.Layout(, showlegend=True, barmode="stack" )
figure = go.Figure(data=data, layout=layout)
offline.plot(figure)

Et ce sont les bases de Plotly. Pour personnaliser nos graphiques, nous définissons différents paramètres pour les traces et la mise en page. Maintenant, allons-y et parlons de la visualisation Lollapalooza.

Mon expérience Lollapalooza

Pour l'édition 2018 de Lollapalooza Brésil, tous les achats ont été effectués via un bracelet RFID. Ils envoient les données à votre adresse e-mail, j'ai donc décidé d'y jeter un coup d'œil. Que pouvons-nous apprendre de moi et de mon expérience en analysant les achats que j'ai effectués au festival?

Voici à quoi ressemblent les données:

  • date d'achat
  • heure d'achat
  • produit
  • quantité
  • étape
  • lieu où j'ai fait l'achat

Sur la base de ces données, répondons à quelques questions.

Où suis-je allé pendant le festival?

Les données ne nous indiquent que le nom du lieu où j'ai effectué l'achat et le festival a eu lieu à Autódromo de Interlagos. J'ai pris la carte avec les étapes d'ici et j'ai utilisé l'outil de géoréférencement de georeference.com pour obtenir les coordonnées de latitude et de longitude des étapes.

Nous devons afficher une carte et les marqueurs pour chaque achat, nous utiliserons donc Mapbox et la scattermapboxtrace. Commençons par tracer uniquement les étapes pour voir comment cela fonctionne:

import plotly.graph_objs as goimport plotly.offline as offlineimport pandas as pd
mapbox_token = "" #//www.mapbox.com/help/define-access-token/
df = pd.read_csv("stages.csv")
trace = go.Scattermapbox( lat = df["latitude"], lon = df["longitude"], text=df["stage"], marker=go.Marker(size=10), mode="markers+text", textposition="top" )
data = [trace]
layout = go.Layout( mapbox=dict( accesstoken=mapbox_token, center=dict( lat = -23.701057, lon = -46.6970635 ), zoom=14.5 ) )
figure = go.Figure(data = data, layout = layout)
offline.plot(figure)

Apprenons un nouveau paramètre de mise en page: updatemenus. Nous allons l'utiliser pour afficher les marqueurs par date. Il existe quatre méthodes de mise à jour possibles:

  • "restyle": modifier les données ou les attributs de données
  • "relayout": modifier les attributs de mise en page
  • "update": modifier les données et les attributs de mise en page
  • "animate": démarrer ou mettre en pause une animation)

Pour mettre à jour les marqueurs, il suffit de modifier les données, nous allons donc utiliser la "restyle"méthode. Lors du restylage, vous pouvez définir les modifications pour chaque trace ou pour toutes les traces. Ici, nous définissons chaque trace pour être visible uniquement lorsque l'utilisateur modifie l'option du menu déroulant:

import plotly.graph_objs as goimport plotly.offline as offlineimport pandas as pdimport numpy as np
mapbox_token = ""
df = pd.read_csv("data.csv")
df_markers = df.groupby(["latitude","longitude","date"]).agg(dict(product = lambda x: "%s" % ", ".join(x), hour = lambda x: "%s" % ", ".join(x)))df_markers.reset_index(inplace=True)
data = []update_buttons = []
dates = np.unique(df_markers["date"])
for i,date in enumerate(dates): df_markers_date = df_markers[df_markers["date"] == date] trace = go.Scattermapbox( lat = df_markers_date["latitude"], lon = df_markers_date["longitude"], name = date, text=df_markers_date["product"]+"

"+df_markers_date["hour"], visible=False ) data.append(trace)

 visible_traces = np.full(len(dates), False) visible_traces[i] = True
 button = dict( label=date, method="restyle", args=[dict(visible = visible_traces)] ) update_buttons.append(button)
updatemenus = [dict(active=-1, buttons = update_buttons)]
layout = go.Layout( mapbox=dict( accesstoken=mapbox_token, center=dict( lat = -23.701057, lon = -46.6970635), zoom=14.5), updatemenus=updatemenus )
figure = go.Figure(data = data, layout = layout)
offline.plot(figure)

Comment ai-je dépensé mon argent?

To answer that, I created a bar chart with my spendings for food and beverage by each day and built a heatmap to show when I bought stuff. We already saw how to build a bar chart, so now let’s build a heatmap chart:

import plotly.graph_objs as goimport pandas as pdimport plotly.offline as offline
df = pd.read_csv("data.csv")
df_purchases_by_type = df.pivot_table(index="place",columns="date",values="price",aggfunc="sum").fillna(0)df["hour_int"] = pd.to_datetime(df["hour"], format="%H:%M", errors="coerce").apply(lambda x: int(x.hour))
df_heatmap = df.pivot_table(index="date",values="price",columns="hour", aggfunc="sum").fillna(0)
trace_heatmap = go.Heatmap( x = df_heatmap.columns, y = df_heatmap.index, z = [df_heatmap.iloc[0], df_heatmap.iloc[1], df_heatmap.iloc[2]] )
data = [trace_heatmap]
layout = go.Layout(title="Purchases by place", showlegend=True)
figure = go.Figure(data=data, layout=layout)
offline.plot(figure)

Which concerts did I watch?

Now let’s go to the coolest part: could I guess the concerts I attended based only on my purchases?

Ideally, when we are watching a show, we are watching the show (and not buying stuff), so the purchases should be made before or after each concert. I then made a list of each concert happening one hour before, one hour after, and according to the time the purchase was made.

Pour savoir à lequel de ces spectacles j'ai assisté, j'ai calculé la distance entre le lieu d'achat et chaque étape. Les spectacles auxquels j'ai assisté devraient être ceux avec la distance la plus courte par rapport aux concessions.

Comme nous voulons montrer chaque point de données, le meilleur choix pour une visualisation est une table. Construisons-en un:

import plotly.graph_objs as goimport plotly.offline as offlineimport pandas as pd
df_table = pd.read_csv("concerts_I_attended.csv")
def colorFont(x): if x == "Yes": return "rgb(0,0,9)" else: return "rgb(178,178,178)"
df_table["color"] = df_table["correct"].apply(lambda x: colorFont(x))
trace_table = go.Table( header=dict( values=["Concert","Date","Correct?"], fill=dict( color=("rgb(82,187,47)")) ), cells=dict( values= [df_table.concert,df_table.date,df_table.correct], font=dict(color=([df_table.color]))) )
data = [trace_table]
figure = go.Figure(data = data)
offline.plot(figure)

Trois concerts manquaient et quatre étaient incorrects, ce qui nous donne une précision de 67% et un rappel de 72%.

Tout rassembler: dash

Nous avons tous les graphiques, mais le but est de les rassembler tous sur une page. Pour ce faire, nous utiliserons Dash (par Plotly).

«Dash est un framework Python pour la création d'applications Web analytiques. Aucun JavaScript requis. Dash est idéal pour créer des applications de visualisation de données avec des interfaces utilisateur hautement personnalisées en Python pur. Il est particulièrement adapté à quiconque travaille avec des données en Python. » - Site de Plotly

Dash is written on top of Flask, Plotly.js, and React.js. It works in a very similar way to the way we create Plotly charts:

import dashimport dash_core_components as dccimport dash_html_components as htmlimport plotly.graph_objs as goimport pandas as pd app = dash.Dash()
df_table = pd.read_csv("concerts_I_attended.csv").dropna(subset=["concert"])def colorFont(x): if x == "Yes": return "rgb(0,0,9)" else: return "rgb(178,178,178)"
df_table["color"] = df_table["correct"].apply(lambda x: colorFont(x))
trace_table = go.Table(header=dict(values=["Concert","Date","Correct?"],fill=dict(color=("rgb(82,187,47)"))),cells=dict(values=[df_table.concert,df_table.date,df_table.correct],font=dict(color=([df_table.color]))))
data_table = [trace_table]
app.layout = html.Div(children=[ html.Div( [ dcc.Markdown( """ ## My experience at Lollapalooza Brazil 2018 *** """.replace(' ', ''), className="eight columns offset-by-two" ) ], className="row", style=dict(textAlign="center",marginBottom="15px") ),
html.Div([ html.Div([ html.H5('Which concerts did I attend?', style=dict(textAlign="center")), html.Div('People usually buy things before or after a concert, so I took the list of concerts, got the distances from the location of the purchases to the stages and tried to guess which concerts did I attend. 8 concerts were correct and 3 were missing from a total of 12 concerts.', style=dict(textAlign="center")), dcc.Graph(id='table', figure=go.Figure(data=data_table,layout=go.Layout(margin=dict(t=30)))), ], className="twelve columns"), ], className="row")])
app.css.append_css({ 'external_url': '//codepen.io/chriddyp/pen/bWLwgP.css'})
if __name__ == '__main__': app.run_server(debug=True)

Cool right?

I hosted the final visualization here and the all the code is here.

There are some alternatives to hosting the visualizations: Dash has a public dash app hosting and Plotly also provides a web-service for hosting graphs.

Did you found this article helpful? I try my best to write a deep dive article each month, you can receive an email when I publish a new one.

I had a pretty good experience with Plotly, I’ll definitely use it for my next project. What are your thoughts about it after this overview? And what other tools do you use to build visualizations for the web? Share them in the comments! And thank you for reading! ?