Crear un mejor panel de control: ¿mito o realidad?
Mi versión beta 2.0 construida usando Dash & Plotly en lugar de Matplotlib
Introducción
En febrero de 2023 escribí mi primera publicación en Medium:
Cómo personalizar infografías en Python: consejos y trucos
Aquí expliqué cómo crear un panel simplificado con varios diagramas, incluido un diagrama de líneas, gráficos circulares y de barras, y un mapa de coropletas. Para trazarlos utilicé el ‘bueno’ Matplotlib [1], porque estaba familiarizado con sus palabras clave y funciones principales. Sigo creyendo que Matplotlib es una gran biblioteca para comenzar su viaje de datos con Python, ya que existe una base de conocimiento colectiva muy grande. Si algo no está claro con Matplotlib, puede buscar en Google sus consultas y lo más probable es que obtenga respuestas.
Sin embargo, Matplotlib puede encontrar algunas dificultades al crear visualizaciones interactivas y basadas en web. Para este último propósito, Plotly [2] puede ser una buena alternativa, permitiéndole crear paneles interactivos inusuales. Matplotlib, por otro lado, es una biblioteca poderosa que ofrece un mejor control sobre la personalización del gráfico, lo cual es bueno para crear visualizaciones listas para publicar.
En esta publicación, intentaré sustituir código que utiliza Matlab (1) con que basado en Plotly (2). La estructura repetirá el post inicial, porque los tipos de parcelas y datos de entrada [3] son lo mismo. Sin embargo, aquí agregaré algunos comentarios sobre el nivel de similitud entre (1) y (2) para cada tipo de gráficos. Mi principal intención al escribir este artículo es recordar mi primera publicación e intentar rehacerla con mi nivel actual de conocimiento.
Nota: Te sorprenderá lo corto que es el código Plotly necesario para construir el mapa de coropletas 🙂
Pero primero lo primero, y comenzaremos creando un gráfico de líneas en Plotly.
#1. Trazado lineal
El gráfico de líneas puede ser una buena elección para mostrar la dinámica del cambio de nuestros datos con el tiempo. En el siguiente caso, combinaremos un diagrama de líneas con un diagrama de dispersión para marcar cada ubicación con su propio color.
A continuación puede encontrar un fragmento de código utilizando Plotly que produce un gráfico de líneas que muestra la asistencia promedio a la Copa Mundial de la FIFA durante el período 1990-2018: cada valor está marcado con una etiqueta y un punto de dispersión de color.
## Line Plot ##
import plotly.graph_objects as go
time = [1990, 1994, 1998, 2002, 2006, 2010, 2014, 2018]
numbers = [48411, 68626, 44676, 42571, 52609, 49499, 53772, 47371]
labels = ['Italy', 'USA', 'France', 'Japan / South Korea', 'Germany',
'South Africa', 'Brazil', 'Russia']
fig = go.Figure()
# Line plot
fig.add_trace(go.Scatter(x=time, y=numbers, mode='lines+markers',
marker=dict(color='black',size=10), line=dict(width=2.5)))
# Scatter plot
for i in range(len(time)):
fig.add_trace(go.Scatter(x=[time[i]], y=[numbers[i]],
mode='markers', name=labels[i]))
# Layout settings
fig.update_layout(title='Average number of attendees per game in 1990-2018',
xaxis=dict(tickvals=time),
yaxis=dict(range=[35000, 70000]),
showlegend=True,
legend=dict(x=0.5, y=-0.2),
plot_bgcolor='white')
fig.show()
Y el resultado es el siguiente:
Cuando pasas el mouse sobre cualquier punto del gráfico en Plotly, aparece una ventana que muestra el número de espectadores y el nombre del país donde se llevó a cabo el torneo.
Nivel de similitud con el gráfico de Matplotlib: 8 de 10.
En general, el código se parece mucho al fragmento inicial en términos de estructura y ubicación de los bloques de código principales en su interior.
Que es diferente: Sin embargo, también se presentan algunas diferencias. Por ejemplo, preste atención a los detalles de cómo se declaran los elementos de la trama (por ejemplo, un modo de trama de líneas + marcadores que permite mostrarlos simultáneamente).
Lo que es importante: Para construir este gráfico utilizo el módulo plotly.graph_objects (que se importa sobre la marcha). Proporciona una jerarquía de clases generada automáticamente denominada “objetos gráficos” que pueden usarse para representar varias figuras.
#2. Gráfico circular (en realidad, un gráfico de anillos)
Los gráficos circulares y de anillos son muy buenos para demostrar las contribuciones de diferentes valores a una cantidad total: están divididos en segmentos, que muestran el valor proporcional de cada parte de nuestros datos.
Aquí hay un fragmento de código en Plotly que se utiliza para crear un gráfico circular que mostraría la proporción entre los países con más títulos de la Copa Mundial.
## Pie Chart ##
import plotly.express as px
# Data
label_list = ['Brazil', 'Germany', 'Italy', 'Argentina', 'Uruguay', 'France', 'England', 'Spain']
freq = [5, 4, 4, 3, 2, 2, 1, 1]
# Customize colors
colors = ['darkorchid', 'royalblue', 'lightsteelblue', 'silver', 'sandybrown', 'lightcoral', 'seagreen', 'salmon']
# Building chart
fig = px.pie(values=freq, names=label_list, title='Countries with the most FIFA World Cup titles',
color_discrete_map=dict(zip(label_list, colors)),
labels={'label': 'Country', 'value': 'Frequency'},
hole=0.3)
fig.update_traces(textposition='inside', textinfo='percent+label')
fig.show()
El elemento visual resultante se muestra a continuación (por cierto, ¡también es un gráfico interactivo!):
Nivel de similitud con el gráfico de Matplotlib: 7 sobre 10. Nuevamente, la lógica del código es casi la misma para las dos versiones.
Que es diferente: Se podría notar que con la ayuda de la palabra clave agujero es posible convertir un gráfico circular en un gráfico de anillos. Y vea lo fácil y sencillo que es mostrar porcentajes para cada segmento del gráfico en Plotly en comparación con Matplotlib.
Lo que es importante: En lugar de usar el módulo plotly.graph_objects, aquí aplico el módulo plotly.express (generalmente importado como px) que contiene funciones que pueden crear figuras enteras a la vez. Esta es la interfaz Plotly fácil de usar, que opera con una variedad de tipos de datos.
#3. Gráfico de barras
Los gráficos de barras, sin importar si son verticales u horizontales, muestran comparaciones entre diferentes categorías. El eje vertical (“Estadio”) del gráfico muestra las categorías específicas que se comparan, y el eje horizontal representa un valor medido, es decir, la “Capacidad”. sí mismo.
## Bar Chart ##
import plotly.graph_objects as go
labels = ['Estádio do Maracanã', 'Camp Nou', 'Estadio Azteca',
'Wembley Stadium', 'Rose Bowl', 'Estadio Santiago Bernabéu',
'Estadio Centenario', 'Lusail Stadium']
capacity = [200, 121, 115, 99, 94, 90, 90, 89]
fig = go.Figure()
# Horizontal bar chart
fig.add_trace(go.Bar(y=labels, x=capacity, orientation='h', marker_color='blue'))
# Layout settings
fig.update_layout(title='Top-8 stadiums on capacity (in thousands)',
yaxis=dict(title='Stadiums'),
xaxis=dict(title='Capacity'),
showlegend=False,
plot_bgcolor='white')
fig.show()
Nivel de similitud con el gráfico de Matplotlib: 6 de 10.
En general, las dos piezas de código tienen más o menos los mismos bloques, pero el código en Plotly es más corto.
Que es diferente: El fragmento de código en Plotly es más corto porque no tenemos que incluir un párrafo para colocar etiquetas para cada columna; Plotly lo hace automáticamente gracias a su interactividad.
Lo que es importante: Para la construcción de este tipo de parcela también se utilizó el módulo plotly.express. Para un carácter de barra horizontal, podemos usar la función px.bar con orientación = ‘h’.
#4. Mapa de coropletas
El mapa de coropletas es una gran herramienta para visualizar cómo varía una variable en un área geográfica. Un mapa de calor es similar, pero utiliza regiones dibujadas según el patrón de una variable en lugar de áreas geográficas como lo hacen los mapas coropletas.
A continuación puedes ver el código de Plotly para dibujar el corograma. Aquí cada país obtiene su color dependiendo de la frecuencia con la que celebra la Copa Mundial de la FIFA. Los países de color rojo oscuro albergaron el torneo 2 veces, los países de color rojo claro, 1 y todos los demás (gris), 0.
## Choropleth Map ##
import polars as pl
import plotly.express as px
df = pl.read_csv('data_football.csv')
df.head(5)
fig = px.choropleth(df, locations='team_code', color='count',
hover_name='team_name', projection='natural earth',
title='Geography of the FIFA World Cups',
color_continuous_scale='Reds')
fig.show()
Nivel de similitud con el gráfico de Matplotlib: 4 de 10.
El código en Plotly es tres veces más pequeño que el código en Matplotlib.
Que es diferente: Mientras usamos Matplotlib para construir un mapa de coropletas, tenemos que hacer mucho personal adicional, por ejemplo:
- descargar una carpeta zip ne_110m_admin_0_countries.zip con archivos de forma para dibujar el mapa en sí y dibujar los límites de los países, líneas de cuadrícula, etc.;
- para importar elementos Basemap, Polygon y PatchCollection de las bibliotecas mpl_toolkits.basemap y matplotlib.patches y matplotlib.collections y usarlos para crear un fondo coloreado según la lógica que sugerimos en el archivo data_football.csv.
Lo que es importante: ¿Y qué hace Plotly? Toma el mismo archivo data_football.csv y, con la ayuda de la función px.choropleth, muestra datos agregados en diferentes regiones o países del mapa. Cada uno de ellos está coloreado de acuerdo con el valor de una información específica proporcionada; en nuestro caso, esta es la variable de conteo en el archivo de entrada.
Como puede ver, todos los códigos de Plotly son más cortos (o iguales en el caso de construir el diagrama de líneas) que los de Matplotlib. Esto se logra porque Plotly hace que sea mucho más fácil crear tramas complejas. Plotly es excelente para crear visualizaciones interactivas con solo unas pocas líneas de código.
Resumen: creación de un panel único con Dash
Dash permite crear un panel interactivo sobre una base de código Python sin necesidad de aprender marcos de JavaScript complejos como React.js.
Aquí puede encontrar el código y comentarios a sus partes importantes:
import polars as pl
import plotly.express as px
import plotly.graph_objects as go
import dash
from dash import dcc
from dash import html
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
header = html.H2(children="FIFA World Cup Analysis")
## Line plot with scatters ##
# Data
time = [1990, 1994, 1998, 2002, 2006, 2010, 2014, 2018]
numbers = [48411, 68626, 44676, 42571, 52609, 49499, 53772, 47371]
labels = ['Italy', 'USA', 'France', 'Japan / South Korea', 'Germany', 'South Africa', 'Brazil', 'Russia']
# Building chart
chart1 = go.Figure()
chart1.add_trace(go.Scatter(x=time, y=numbers, mode='lines+markers',
marker=dict(color='black',size=10), line=dict(width=2.5)))
for i in range(len(time)):
chart1.add_trace(go.Scatter(x=[time[i]], y=[numbers[i]],
mode='markers', name=labels[i]))
# Layout settings
chart1.update_layout(title='Average number of attendees per game in 1990-2018',
xaxis=dict(tickvals=time),
yaxis=dict(range=[35000, 70000]),
showlegend=True,
plot_bgcolor='white')
plot1 = dcc.Graph(
id='plot1',
figure=chart1,
className="six columns"
)
## Pie chart ##
# Data
label_list = ['Brazil', 'Germany', 'Italy', 'Argentina', 'Uruguay', 'France', 'England', 'Spain']
freq = [5, 4, 4, 3, 2, 2, 1, 1]
# Customize colors
colors = ['darkorchid', 'royalblue', 'lightsteelblue', 'silver', 'sandybrown', 'lightcoral', 'seagreen', 'salmon']
# Building chart
chart2 = px.pie(values=freq, names=label_list, title='Countries with the most FIFA World Cup titles',
color_discrete_map=dict(zip(label_list, colors)),
labels={'label': 'Country', 'value': 'Frequency'},
hole=0.3)
chart2.update_traces(textposition='inside', textinfo='percent+label')
plot2 = dcc.Graph(
id='plot2',
figure=chart2,
className="six columns"
)
## Horizontal bar chart ##
labels = ['Estádio do Maracanã', 'Camp Nou', 'Estadio Azteca',
'Wembley Stadium', 'Rose Bowl', 'Estadio Santiago Bernabéu',
'Estadio Centenario', 'Lusail Stadium']
capacity = [200, 121, 115, 99, 94, 90, 90, 89]
# Building chart
chart3 = go.Figure()
chart3.add_trace(go.Bar(y=labels, x=capacity, orientation='h', marker_color='blue'))
# Layout settings
chart3.update_layout(title='Top-8 stadiums on capacity (in thousands)',
yaxis=dict(title='Stadiums'),
xaxis=dict(title='Capacity'),
showlegend=False,
plot_bgcolor='white')
plot3 = dcc.Graph(
id='plot3',
figure=chart3,
className="six columns"
)
## Chropleth map ##
# Data
df = pl.read_csv('data_football.csv')
# Building chart
chart4 = px.choropleth(df, locations='team_code', color='count',
hover_name='team_name', projection='natural earth',
title='Geography of the FIFA World Cups',
color_continuous_scale='Reds')
plot4 = dcc.Graph(
id='plot4',
figure=chart4,
className="six columns"
)
row1 = html.Div(children=[plot1, plot2],)
row2 = html.Div(children=[plot3, plot4])
layout = html.Div(children=[header, row1, row2], style={"text-align": "center"})
app.layout = layout
if __name__ == "__main__":
app.run_server(debug=False)
Comentarios al código:
- Primero, necesitamos importar todas las bibliotecas (incluidos los módulos HTML) e inicializar el panel con la ayuda de la cadena app = dash.Dash(__name__, external_stylesheets=external_stylesheets).
- Luego pegamos cada gráfico en los componentes principales de Dash para integrarlos aún más con otros componentes HTML (dcc.Graph). Aquí className=”seis columnas” necesita usar la mitad de la pantalla para cada fila de gráficos.
- Después de eso, creamos 2 filas de componentes html.Div con 2 gráficos en cada uno. Además, se puede utilizar un CSS simple con el atributo de estilo para mostrar el encabezado de nuestro panel en la cadena de diseño. Este diseño se establece como el diseño de la aplicación inicializada anteriormente.
- Finalmente, el último párrafo permite ejecutar la aplicación localmente (app.run_server(debug=False)). Para ver el panel, simplemente siga el enlace http://127.0.0.1:8050/ y encontrarás algo como la imagen de abajo.
Observaciones finales
Honestamente, la pregunta en el título era retórica y solo usted, querido lector, puede decidir si la versión actual del tablero es mejor que la anterior. Pero al menos hice lo mejor que pude (y en el fondo creo que la versión 2.0 es mejor) 🙂
Quizás pienses que esta publicación no contiene ninguna información nueva, pero no podría estar más en desacuerdo. Al escribir esta publicación, quería enfatizar la importancia de mejorar las habilidades con el tiempo, incluso si la primera versión del código no se ve tan mal.
Espero que esta publicación te anime a mirar tus proyectos terminados e intentar rehacerlos utilizando las nuevas técnicas disponibles. Esa es la razón principal por la que decidí sustituir Matplotlib por Plotly & Dash. (Además, los dos últimos facilitan la creación de resultados de análisis de datos).
La capacidad de mejorar constantemente su trabajo mejorando una versión anterior o utilizando nuevas bibliotecas en lugar de las que usaba antes es una gran habilidad para cualquier programador. Si tomas este consejo como un hábito, verás avances, porque sólo la práctica hace la perfección.
¡Y como siempre, gracias por leer!
Referencias
- La página principal de la biblioteca Matplotlib: https://matplotlib.org/stable/
- La página principal de la biblioteca Plotly: https://plotly.com/python/
- Fjelstul, Joshua C. “La base de datos de la Copa Mundial de Fjelstul v.1.0”. 8 de julio de 2022. https://www.github.com/jfjelstul/worldcup
Crear un mejor panel de control: ¿mito o realidad? fue publicado originalmente en Hacia la ciencia de datos en Medium, donde las personas continúan la conversación resaltando y respondiendo a esta historia.