Cómo y por qué usé Plotly (en lugar de D3) para visualizar mis datos de Lollapalooza

D3.js es una biblioteca de JavaScript impresionante, pero tiene una curva de aprendizaje muy pronunciada. Esto hace que la tarea de crear una visualización valiosa sea algo que puede requerir mucho esfuerzo. Este esfuerzo adicional está bien si su objetivo es realizar visualizaciones de datos nuevas y creativas, pero a menudo ese no es el caso.

A menudo, su objetivo puede ser simplemente crear una visualización interactiva con algunos gráficos conocidos . Y si no es un ingeniero de front-end, esto puede resultar un poco complicado.

Como científicos de datos, una de nuestras principales tareas es la manipulación de datos. Hoy, la herramienta principal que utilizo para eso es Pandas (Python). ¿Qué pasa si te digo que se puede construir algunas cartas hermosas e interactivas para la Web desde sus tramas de datos pandas ? ¡Bueno, puedes! Podemos usar Plotly para eso.

Para el registro, también hay Bibliotecas de API Plotly para Matlab, R y JavaScript, pero nos quedaremos con la biblioteca de Python aquí.

Para ser justos, Plotly está construido sobre d3.js (y stack.gl). La principal diferencia entre D3 y Plotly es que Plotly es específicamente una biblioteca de gráficos .

Construyamos un gráfico de barras para conocer cómo funciona Plotly.

Construyendo un gráfico de barras con plotly

Hay 3 conceptos principales en la filosofía de Plotly:

  • Datos
  • Diseño
  • Figura

Datos

El objeto de datos define lo que queremos mostrar en el gráfico (es decir, los datos). Definimos una colección de datos y las especificaciones para mostrarlos como traza . Un objeto de datos puede tener muchos rastros. Piense en un gráfico de líneas con dos líneas que representan dos categorías diferentes: cada línea es un trazo.

Diseño

El objeto Layout define características que no están relacionadas con los datos (como título, títulos de eje, etc.). También podemos usar el Diseño para agregar anotaciones y formas al gráfico.

Figura

El objeto Figura crea el objeto final que se trazará. Es un objeto que contiene datos y diseño.

Las visualizaciones de trazados se crean con plotly.js. Esto significa que la API de Python es solo un paquete para interactuar con la biblioteca plotly.js . El plotly.graph_objsmódulo contiene las funciones que generarán objetos gráficos para nosotros.

Ok, ahora estamos listos para construir un gráfico de barras:

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)

Nota: en este artículo no hablaremos de lo que estoy haciendo con los marcos de datos. Pero si desea una publicación sobre eso, hágamelo saber en los comentarios.

Bien, primero queremos mostrar las barras de una categoría (un lugar llamado "MICROBAR"). Entonces creamos un objeto de datos (una lista) con go.Bar()(una traza) especificando los datos para los ejes xey. Trace es un diccionario y los datos son una lista de diccionarios. Aquí está el trace_microbarcontenido (observe la clave de tipo):

{'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}

En el objeto Layout, establecemos el título del gráfico y el parámetro showlegend. Luego, ajustamos los datos y el diseño en una figura y llamamos plotly.offline.plot()para mostrar el gráfico. Plotly tiene diferentes opciones para mostrar los gráficos, pero sigamos con la opción sin conexión aquí. Esto abrirá una ventana del navegador con nuestro gráfico.

Quiero mostrar todo en un gráfico de barras apiladas, por lo que crearemos una lista de datos con todas las trazas (lugares) que queremos mostrar y configuraremos el barmodeparámetro para apilar .

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)

Y eso es lo básico de Plotly. Para personalizar nuestros gráficos, establecemos diferentes parámetros para los trazos y el diseño. Ahora sigamos adelante y hablemos de la visualización de Lollapalooza.

Mi experiencia Lollapalooza

Para la edición 2018 de Lollapalooza Brasil, todas las compras se realizaron a través de una pulsera habilitada para RFID. Envían los datos a su dirección de correo electrónico, así que decidí echarle un vistazo. ¿Qué podemos aprender sobre mí y mi experiencia analizando las compras que hice en el festival?

Así es como se ven los datos:

  • Fecha de compra
  • hora de compra
  • producto
  • cantidad
  • etapa
  • lugar donde hice la compra

Basándonos en estos datos, respondamos algunas preguntas.

¿A dónde fui durante el festival?

Los datos solo nos dicen el nombre del local donde realicé la compra, y el festival tuvo lugar en el Autódromo de Interlagos. Tomé el mapa con las etapas de aquí y usé la herramienta georeferenciador de georeference.com para obtener las coordenadas de latitud y longitud de las etapas.

Necesitamos mostrar un mapa y los marcadores para cada compra, por lo que usaremos Mapbox y la scattermapboxtraza. Primero, tracemos solo las etapas para ver cómo funciona esto:

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)

Vamos a aprender un nuevo parámetro de diseño: updatemenus. Usaremos esto para mostrar los marcadores por fecha. Hay cuatro métodos de actualización posibles:

  • "restyle": modificar datos o atributos de datos
  • "relayout": modificar atributos de diseño
  • "update": modificar datos y atributos de diseño
  • "animate": iniciar o pausar una animación)

Para actualizar los marcadores, solo necesitamos modificar los datos, por lo que usaremos el "restyle"método. Al cambiar el estilo, puede establecer los cambios para cada traza o para todas las trazas. Aquí configuramos cada traza para que sea visible solo cuando el usuario cambia la opción del menú desplegable:

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)

¿Cómo gasté mi dinero?

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.

Para saber a cuál de estos espectáculos asistí, calculé la distancia desde el lugar de la compra hasta cada escenario. Los espectáculos a los que asistí deberían ser los que tengan la distancia más corta a las concesiones.

Como queremos mostrar cada punto de datos, la mejor opción para una visualización es una tabla. Construyamos uno:

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)

Faltaban tres conciertos y cuatro eran incorrectos, lo que nos da una precisión del 67% y una recuperación del 72%.

Poniéndolo todo junto: guión

Tenemos todos los gráficos, pero el objetivo es ponerlos todos juntos en una página. Para hacer eso usaremos Dash (por Plotly).

“Dash es un marco de Python para crear aplicaciones web analíticas. No se requiere JavaScript. Dash es ideal para crear aplicaciones de visualización de datos con interfaces de usuario altamente personalizadas en Python puro. Es especialmente adecuado para cualquiera que trabaje con datos en Python ". - Sitio 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! ?