Desarrollando Paneles Interactivos e Informativos con Spark y Plotly Dash.

Developing Interactive and Informative Panels with Spark and Plotly Dash.

Visualización de datos interactiva a gran escala para aplicaciones web en Python

Foto del autor

1. Introducción

El lago de datos en la nube es ampliamente adoptado por las organizaciones empresariales como un repositorio escalable y de bajo costo de todo tipo de datos (estructurados y no estructurados). Existen muchos desafíos en el análisis de un conjunto de datos a gran escala del lago de datos de manera eficiente para obtener ideas significativas para la toma de decisiones impulsada por los datos. Uno de los desafíos es que el tamaño del conjunto de datos tiende a ser demasiado grande para caber en una sola máquina. Por lo general, se requiere un clúster de servidores para manejar un gran conjunto de datos. Otro desafío es cómo compartir los resultados del análisis de datos en servidores con clientes o accionistas relacionados de manera fácil y rentable.

Este documento utiliza el mismo conjunto de datos de código abierto utilizado en [1] para presentar un marco de aplicación web de código abierto para el desarrollo de paneles interactivos y perspicaces utilizando Spark [2][3] y Plotly Dash[4]. Este marco nos permite analizar y visualizar conjuntos de datos a gran escala en servidores y compartir los resultados del análisis de datos y la visualización como paneles en cualquier lugar.

Como se muestra en la Figura 1, el nuevo marco de aplicación web consta de tres componentes principales:

  • Servicio de Spark SQL (por ejemplo, DataFrame) para el procesamiento de datos distribuidos (consulte la Sección 2)
  • Servicio de gráficos de Plotly para crear gráficos de visualización de datos como paneles (consulte la Sección 3)
  • Servicio web Dash para la interacción entre el servicio de gráficos de Plotly en el lado del servidor y los clientes del panel (consulte la Sección 4)
Figura 1: Arquitectura del marco de aplicación de alto nivel.

2. Servicio de Spark SQL para el procesamiento de datos distribuidos

Como se describe en [2], PySpark (API de Python para Spark) se puede usar fácilmente para leer archivos csv del lago de datos en la nube, como AWS S3. Para simplificar, se supone en este documento que un archivo csv de conjunto de datos train_data.csv [1] está disponible en la máquina local sin pérdida de generalidad.

El siguiente código se usa para cargar el archivo csv en la memoria como un DataFrame de Spark SQL:

import pysparkfrom pyspark.sql import SparkSessionspark = SparkSession.builder.appName('hospital-stay').getOrCreate()spk_df = spark.read.csv('./data/train_data.csv',                         header = True, inferSchema = True)

Después de cargar los datos, se puede crear una vista temporal global como se muestra a continuación para la conveniencia de la consulta dinámica de datos.

spk_df.createOrReplaceTempView("dataset_view")

Una vez que se crea la vista del conjunto de datos, podemos usar Spark SQL para consultar datos como una consulta de datos común desde una base de datos. Como ejemplo, el siguiente código consulta todas las filas de dataset_view donde la edad de las personas cae dentro del rango de [21, 30].

age = "21-30"sdf = spark.sql(f"SELECT * FROM dataset_view WHERE Age=='{age}'")

Para utilizar Plotly para crear gráficos de visualización de datos a partir de un DataFrame de Spark sdf, debemos convertirlo en un DataFrame de Pandas pdf porque Plotly no admite directamente DataFrame de Spark.

pdf = sdf.toPandas()

3. Plotly para crear gráficos de visualización de datos

Plotly admite la generación de muchos tipos diferentes de gráficos. Algunos son adecuados para crear gráficos a partir de características numéricas continuas, mientras que otros son adecuados para crear gráficos a partir de características categóricas discretas.

Este documento utiliza la biblioteca Plotly Express para crear los siguientes diagramas comunes con fines de demostración.

  • Gráficos para características numéricas: gráfico de dispersión, gráfico de histograma y gráfico de línea
  • Gráficos para características categóricas: gráfico de barras, gráfico de histograma, gráfico de línea y gráfico circular

3.1 Gráficos para características numéricas

Como se describió anteriormente, tres de los gráficos comunes para características numéricas son:

  • gráfico de dispersión
  • histograma
  • gráfico de línea

Dado un par de características numéricas, el gráfico de dispersión utiliza cada par de valores de características como coordenadas para dibujar un punto en un plano 2D. Como ejemplo, la siguiente figura muestra un gráfico de dispersión de dos características numéricas, ID de paciente y Depósito de admisión, para personas de 21 a 30 años. La característica Tipo de admisión se utiliza para codificación de color.

Figura 2: Gráfico de dispersión de muestra para un par de características numéricas.

Suponiendo que un usuario del panel ha seleccionado el rango de edad [21, 30], un par de características numéricas x = ID de paciente e y = Depósito de admisión, y la característica de codificación de color = Tipo de admisión, la siguiente declaración crea el gráfico de dispersión anterior.

fig = px.scatter(dff, x = x, y = y, color = color_feature)

De manera similar, la siguiente declaración se utiliza para crear un histograma de los mismos datos:

fig = px.histogram(dff, x = x, y = y, color = color_feature)
Figura 3: Histograma de muestra para un par de características numéricas.

Para ser completos, la siguiente declaración se utiliza para crear un gráfico de línea.

fig = px.line(dff, x = x, y = y, color = color_feature)
Figura 4: Gráfico de línea de muestra para un par de características numéricas.

Aunque podemos crear fácilmente un gráfico de línea, un gráfico de línea como el anterior no revela información útil. Un buen uso del gráfico de línea es aplicarlo a un conjunto de datos que esté ordenado de manera significativa, como una secuencia de datos ordenados por tiempo o una lista de valores de características ordenados por recuento, como se muestra en la Sección 3.2.

3.2 Gráficos para características categóricas

Esta subsección muestra cuatro de los gráficos comunes para características categóricas:

  • gráfico de barras
  • histograma
  • gráfico de línea
  • gráfico circular

Suponiendo que un usuario del panel ha seleccionado edad = [21-30], característica categórica = Estancia, color = púrpura, estilo de gráfico = barra, el siguiente código se puede utilizar para generar el gráfico de barras a continuación.

dff = spark.sql(f"SELECT * FROM dataset_view WHERE Age=='{age}'").toPandas()vc = dff[feature].value_counts()fig = px.bar(vc, x = vc.index, y = vc.values)

Observé que un histograma tiene el mismo resultado que un gráfico de barras para los recuentos de valores de características categóricas.

Figura 5: Gráfico de barras de muestra para los recuentos de valores de una característica categórica.

Podemos crear un gráfico de línea de la siguiente manera seleccionando el estilo de gráfico = línea:

fig = px.line(vc, x = vc.index, y = vc.values)
Figura 6: Gráfico de línea de muestra para los recuentos de valores de una característica categórica.

Como se mencionó anteriormente, el gráfico de línea es adecuado para visualizar recuentos de valores de una característica categórica.

El siguiente código es para crear un gráfico circular para los recuentos de valores de la misma característica categórica de Estancia.

fig = px.pie(vc, x = vc.index, y = vc.values)

Este gráfico circular utiliza una codificación automática de colores en lugar del color seleccionado púrpura.

Figura 7: Ejemplo de gráfico circular para recuentos de valores de una característica categórica.

4. Dash para la visualización de datos interactiva

La sección anterior describe cómo crear paneles de control con diferentes tipos de gráficos utilizando la biblioteca Plotly Express en un grupo de servidores Spark. Esta sección muestra cómo utilizar Dash para compartir paneles de control con clientes de aplicaciones web y permitir a los clientes utilizar los paneles de control para visualizar datos de diversas maneras de manera interactiva.

Se puede seguir el siguiente procedimiento para desarrollar un panel de control de una página de una aplicación web:

  • Paso 1: importar módulos de la biblioteca Dash
  • Paso 2: crear un objeto de aplicación Dash
  • Paso 3: definir una disposición de panel de control de una página de HTML
  • Paso 4: definir funciones de devolución de llamada (puntos finales del servicio web)
  • Paso 5: iniciar el servidor

4.1 Importar módulos de la biblioteca Dash

Como primer paso, se importan los módulos de la biblioteca Plotly Dash de la siguiente manera con el propósito de demostración en este documento.

import plotly.express as pxfrom dash import Dash, dcc, html, callback, Input, Output

4.2 Crear objeto de aplicación Dash

Después de importar los módulos de la biblioteca, el siguiente paso es crear un objeto de aplicación Dash:

app = Dash(__name__)

4.3 Definir disposición de panel de control

Una vez creado un objeto de aplicación Dash, necesitamos definir una disposición de panel de control como una página HTML.

La página HTML del panel de control se divide en dos partes en este documento:

  • Parte 1: visualización de características numéricas
  • Parte 2: visualización de características categóricas

La parte 1 de la disposición del panel de control se define de la siguiente manera:

app.layout = html.Div([ # disposición del panel de control    html.Div([ # Parte 1        html.Div([            html.Label(['Edad:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['0-10', '11-20', '21-30', '31-40', '41-50',                 '51-60', '61-70', '71-80', '81-90', '91-100',                'Más de 100 días'],                value='21-30',                id='numerical_age'            ),        ],        style={'width': '20%', 'display': 'inline-block'}),        html.Div([            html.Label(['Característica numérica x:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['patientid',                  'Hospital_code',                 'City_Code_Hospital'],                value='patientid',                id='axis_x',            )        ], style={'width': '20%', 'display': 'inline-block'}),        html.Div([            html.Label(['Característica numérica y:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['Hospital_code',                 'Admission_Deposit',                 'Bed Grade',                 'Available Extra Rooms in Hospital',                 'Visitors with Patient',                 'Bed Grade',                 'City_Code_Patient'],                value='Admission_Deposit',                id='axis_y'            ),        ], style={'width': '20%', 'display': 'inline-block'}),        html.Div([            html.Label(['Característica de color:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['Severity of Illness',                 'Stay',                 'Department',                 'Ward_Type',                 'Ward_Facility_Code',                 'Type of Admission',                 'Hospital_region_code'],                value='Type of Admission',                id='color_feature'            ),        ], style={'width': '20%', 'display': 'inline-block'}),        html.Div([            html.Label(['Estilo de gráfico:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['scatter',                 'histogram',                 'line'],                value='histogram',                id='numerical_graph_style'            ),        ], style={'width': '20%', 'float': 'right', 'display': 'inline-block'})    ],     style={        'padding': '10px 5px'    }),    html.Div([        dcc.Graph(id='numerical-graph-content')    ]),......

Las figuras 2, 3 y 4 se crean utilizando la Parte 1 del diseño del panel de control.

La Parte 2 del diseño del panel de control se define de la siguiente manera:

......html.Div([ # Parte 2        html.Div([            html.Label(['Edad:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['0-10', '11-20', '21-30', '31-40', '41-50',                 '51-60', '61-70', '71-80', '81-90', '91-100',                'Más de 100 días'],                value='21-30',                id='categorical_age'            ),        ],        style={'width': '25%', 'display': 'inline-block'}),        html.Div([            html.Label(['Característica Categórica:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['Gravedad de la Enfermedad',                 'Estancia',                 'Departamento',                 'Tipo de Habitación',                 'Código de Instalación de la Habitación',                 'Tipo de Admisión',                 'Código de Región del Hospital'],                value='Estancia',                id='categorical_feature'            ),        ],         style={'width': '25%', 'display': 'inline-block'}),        html.Div([            html.Label(['Color:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown(                ['rojo',                 'verde',                 'azul',                 'naranja',                 'morado',                 'negro',                 'amarillo'],                value='azul',                id='categorical_color'            ),        ],         style={'width': '25%', 'display': 'inline-block'}),        html.Div([            html.Label(['Estilo del Gráfico:'], style={'font-weight': 'bold', "text-align": "center"}),            dcc.Dropdown([                 'histograma',                 'barra',                 'línea',                 'pastel'],                value='barra',                id='categorical_graph_style'            ),        ],         style={'width': '25%', 'float': 'right', 'display': 'inline-block'})    ],     style={        'padding': '10px 5px'    }),    html.Div([        dcc.Graph(id='categorical-graph-content')    ])]) # fin del diseño del panel de control

Las figuras 5, 6 y 7 se crean utilizando la Parte 2 del diseño del panel de control.

4.4 Definir Funciones de Callback

El diseño del panel de control solo crea una página HTML estática de un panel de control. Las funciones de callback (es decir, puntos finales de servicio web) deben definirse para que la acción del usuario del panel de control se pueda enviar a una función de callback en el lado del servidor como una solicitud de servicio web. En otras palabras, las funciones de callback permiten la interacción entre los usuarios del panel de control y los servicios web del panel de control en el lado del servidor, como crear un nuevo gráfico a petición del usuario (por ejemplo, seleccionar una opción de lista desplegable).

En este artículo se definen dos funciones de callback para las dos partes del diseño del panel de control.

La función de callback para la Parte 1 del diseño del panel de control se define de la siguiente manera:

@callback(    Output('numerical-graph-content', 'figure'),    Input('axis_x', 'value'),    Input('axis_y', 'value'),    Input('numerical_age', 'value'),    Input('numerical_graph_style', 'value'),    Input('color_feature', 'value'))def update_numerical_graph(x, y, age, graph_style, color_feature):    dff = spark.sql(f"SELECT * FROM dataset_view WHERE Age=='{age}'").toPandas()    if graph_style == 'line':        fig = px.line(dff,                  x = x,                  y = y,                  color = color_feature                )    elif graph_style == 'histograma':        fig = px.histogram(dff,                  x = x,                  y = y,                  color = color_feature                )    else:        fig = px.scatter(dff,                  x = x,                  y = y,                  color = color_feature                )            fig.update_layout(        title=f"Relación entre {x} vs. {y}",    )    return fig

La función de callback para la Parte 2 del diseño del panel de control se define de la siguiente manera:

@callback(    Output('categorical-graph-content', 'figure'),    Input('categorical_feature', 'value'),    Input('categorical_age', 'value'),    Input('categorical_graph_style', 'value'),    Input('categorical_color', 'value'))def update_categorical_graph(feature, age, graph_style, color):    dff = spark.sql(f"SELECT * FROM dataset_view WHERE Age=='{age}'").toPandas()    vc = dff[feature].value_counts()    if graph_style == 'barra':        fig = px.bar(vc,                  x = vc.index,                  y = vc.values                )    elif graph_style == 'histograma':        fig = px.histogram(vc,                  x = vc.index,                  y = vc.values                )    elif graph_style == 'línea':        fig = px.line(vc,                  x = vc.index,                  y = vc.values                )    else:        fig = px.pie(vc,                  names = vc.index,                  values = vc.values                )            if graph_style == 'línea':        fig.update_traces(line_color=color)    elif graph_style != 'pastel':        fig.update_traces(marker_color=color)    fig.update_layout(        title=f"Conteo de Valores de la Característica {feature}",        xaxis_title=feature,        yaxis_title="Conteo"    )    return fig

Cada función de devolución de llamada está asociada con una anotación @callback. La anotación asociada con una función de devolución de llamada controla qué componentes HTML (por ejemplo, un menú desplegable) proporcionan entradas a la función de devolución de llamada a petición de los usuarios, y qué componente HTML (por ejemplo, un gráfico dentro de una etiqueta div) recibe la salida de la función de devolución de llamada.

4.5 Iniciar el Servidor

El último paso de una aplicación web Dash es iniciar un servidor de servicio web como se muestra a continuación:

if __name__ == "__main__":    app.run_server()

El siguiente diagrama muestra un escenario del panel cuando un usuario ha seleccionado las siguientes opciones en el panel:

  • edad de 21 a 30 años
  • par de características numéricas del paciente y Depósito de Admisión
  • característica categórica Tipo de Admisión para codificación de color de la visualización de características numéricas
  • gráfico de dispersión para la visualización de características numéricas
  • Característica Categórica Estancia para el cálculo de los recuentos de valores de características
  • Color azul para gráficos de barras, histogramas y líneas
  • gráfico circular con codificación automática de color para la visualización de recuentos de valores de características categóricas
Figura 8: Una vista general del panel.

Como ejemplo de obtener posibles conocimientos útiles, el escenario de panel anterior revela los siguientes conocimientos:

  • la mayoría de los pacientes entre 21-30 años tenían un depósito entre $3,000 y $6,000 sin importar cuánto tiempo permanecieron en el hospital
  • la mayoría de los pacientes entre 21-30 años permanecieron en el hospital durante 11-30 días (27.6%) o 21-30 días (27.9%)

La siguiente figura muestra otro escenario del panel cuando un usuario ha seleccionado las siguientes opciones en el panel:

  • edad de 21 a 30 años
  • par de características numéricas del paciente y Depósito de Admisión
  • característica categórica Tipo de Admisión para codificación de color de la visualización de características numéricas
  • histograma para la visualización de características numéricas
  • Característica categórica Tipo de Admisión
  • Color verde para gráficos de barras, histogramas y líneas
  • gráfico de barras para la visualización de recuentos de valores de características categóricas
Figura 9: Otra vista general del panel.

Como otro ejemplo de obtener posibles conocimientos útiles, el escenario de panel anterior revela los siguientes conocimientos:

  • los pacientes en cuidados urgentes tenían un depósito total más alto que los pacientes en otros tipos de admisión
  • la mayoría de los pacientes fueron admitidos como traumatismos

En resumen, el panel permite al usuario visualizar datos de manera flexible para obtener varios conocimientos útiles de manera interactiva, incluyendo:

  • visualizar características numéricas y categóricas en un rango dado de edad como 0-10, 11-20, …, etc.
  • visualizar cualquier par de características numéricas en gráficos de dispersión, histograma y/o gráfico de líneas
  • usar cualquier valor de características categóricas para la codificación de color para la visualización de características numéricas
  • visualizar recuentos de valores de cualquier característica categórica como gráfico de barras/histograma, gráfico de líneas y/o gráfico circular con diferentes codificaciones de color

5. Conclusión

Este artículo presentó un marco de aplicación web de código abierto en Python para el desarrollo de paneles interactivos e informativos utilizando Spark [3] y Plotly Dash[4]. Este marco nos permite analizar conjuntos de datos de gran escala de un lago de datos en la nube, crear paneles interactivos en servidores Spark y permitir que los usuarios interactúen con los paneles en cualquier lugar para visualizar datos de manera flexible y obtener varios conocimientos útiles.

Referencias

[1] Yu Huang, Predicting Hospitalized Time of Covid-19 Patients

[2] PySpark AWS S3 Read Write Operations

[3] Apache Spark examples

[4] Dash Python User Guide

We will continue to update Zepes; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Inteligencia Artificial

Visualizando Modelos en Profundidad Una Guía para Grad-CAM en Aprendizaje Profundo

Introducción El mapeo de activación de clase ponderado por gradiente es una técnica utilizada en el aprendizaje profu...

Inteligencia Artificial

La Tierra no es plana, y tus diagramas de Voronoi tampoco deberían serlo

Explora la precisión geoespacial utilizando Python, entendiendo la diferencia entre los diagramas de Voronoi esférico...

Inteligencia Artificial

Artista Co-creatividad y colaboración entre computadoras y humanos en las artes

Lejos de sentirse amenazados por la inteligencia artificial, muchos profesionales creativos ya la están adoptando, co...