¿Cómo crear un panel de visualización totalmente interactivo y en tiempo real utilizando Bokeh y JavaScript personalizado?

En este tutorial, creamos un panel de visualización de datos visualmente atractivo y totalmente interactivo utilizando Bokeh. Comenzamos convirtiendo datos sin procesar en gráficos reveladores y luego los mejoramos con funciones como cepillado vinculado, degradados de color y filtros en tiempo real impulsados ​​por menús desplegables y controles deslizantes. A medida que avanzamos, le damos vida a nuestro panel con interactividad de JavaScript personalizado (CustomJS), lo que permite respuestas instantáneas del lado del navegador sin una sola devolución de llamada de Python. Al combinar lo mejor de la fortaleza analítica de Python con la capacidad de respuesta de JavaScript, creamos una experiencia de panel dinámica y fluida que redefine cómo visualizamos e interactuamos con los datos. Consulta los CÓDIGOS COMPLETOS aquí.

!pip instalar bokeh pandas numpy scipy -q importar numpy como np importar pandas como pd desde bokeh.io importar salida_notebook, mostrar, exportar_png, archivo_salida desde bokeh.plotting importar figura desde bokeh.layouts importar fila, columna, diagrama de cuadrícula desde bokeh.models importar (ColumnDataSource, HoverTool, LassoSelectTool, BoxSelectTool, TapTool, ColorBar, LinearColorMapper, BasicTicker, PrintfTickFormatter, Slider, Select, CheckboxGroup, CustomJS, CDSView, BooleanFilter, Div, Button) de bokeh.palettes importar Viridis256 de bokeh.models.widgets importar DataTable, TableColumn output_notebook() np.random.seed(42) N = 300 datos = pd.DataFrame({ “temp_c”: 20 + 5 * np.random.randn(N), “presión_kpa”: 101 + 3 * np.random.randn(N), “humidity_pct”: 40 + 15 * np.random.randn(N), “sensor_id”: np.elección.aleatoria([“A1″,”A2″,”B7″,”C3”]tamaño=N), “timestep”: np.arange(N) }) source_main = ColumnDataSource(data) p_scatter = figure(title=”Temperatura vs Presión”, ancho=400, alto=300, x_axis_label=”Temperatura (°C)”, y_axis_label=”Presión (kPa)”, herramientas=”pan,wheel_zoom,reset”) scat = p_scatter.circle(x=”temp_c”, y=”presión_kpa”, tamaño=8, fill_alpha=0.6, fill_color=”orange”, line_color=”black”, source=source_main, legend_label=”Lecturas del sensor”) hover = HoverTool(tooltips=[
(“Temp (°C)”, “@temp_c{0.0}”), (“Pressure”, “@pressure_kpa{0.0} kPa”),
(“Humidity”, “@humidity_pct{0.0}%”), (“Sensor”, “@sensor_id”),
(“Timestep”, “@timestep”)]renderizadores =[scat]) p_scatter.add_tools(hover) p_scatter.legend.location = “arriba_izquierda” show(p_scatter)

Comenzamos configurando nuestro entorno e importando todas las bibliotecas necesarias. Luego creamos un conjunto de datos sintéticos y visualizamos la temperatura frente a la presión utilizando un diagrama de dispersión simple con función de desplazamiento. Esto nos ayuda a establecer una base para nuestro panel interactivo. Consulta los CÓDIGOS COMPLETOS aquí.

p_humidity = figure(title=”Humedad vs temperatura (selección vinculada)”, ancho=400, alto=300, x_axis_label=”Temperatura (°C)”, y_axis_label=”Humedad (%)”, herramientas=”pan,wheel_zoom,reset,box_select,lasso_select,tap”) r2 = p_humidity.square(x=”temp_c”, y=”humidity_pct”, size=8, fill_alpha=0.6, fill_color=”navy”, line_color=”white”, source=source_main) p_humidity.add_tools(HoverTool(tooltips=[
(“Temp (°C)”, “@temp_c{0.0}”), (“Humidity”, “@humidity_pct{0.0}%”),
(“Sensor”, “@sensor_id”)]renderizadores =[r2])) diseño_vinculado = fila(p_scatter, p_humidity) show(diseño_vinculado)

Ampliamos nuestra visualización agregando otro gráfico que vincula la humedad y la temperatura a través de datos compartidos. Usamos cepillado vinculado para que las selecciones en un gráfico se reflejen automáticamente en el otro, lo que nos ayuda a analizar relaciones entre múltiples variables simultáneamente. Consulta los CÓDIGOS COMPLETOS aquí.

color_mapper = LinearColorMapper(paleta=Viridis256, bajo=datos[“humidity_pct”].min(), alto=datos[“humidity_pct”].max()) p_color = figure(title=”Presión vs Humedad (Coloreado por Humedad)”, ancho=500, alto=350, x_axis_label=”Presión (kPa)”, y_axis_label=”Humedad (%)”, herramientas=”pan,wheel_zoom,reset,box_select,lasso_select”) r3 = p_color.circle(x=”presión_kpa”, y=”humidity_pct”, tamaño=8, fill_alpha=0.8, line_color=Ninguno, color={“field”: “humidity_pct”, “transform”: color_mapper}, source=source_main) color_bar = ColorBar(color_mapper=color_mapper, ticker=BasicTicker(desired_num_ticks=5), formatter=PrintfTickFormatter(format=”%4.1f%%”), label_standoff=8, border_line_color=Ninguno, ubicación=(0,0), title=”Humedad %”) p_color.add_layout(color_bar, “right”) show(p_color)

Mejoramos nuestra visualización introduciendo una función de mapeo de color continuo para representar los niveles de humedad. Al agregar una barra de color y un degradado, hacemos que nuestro gráfico sea más informativo e intuitivo, lo que nos permite interpretar las variaciones visualmente. Consulta los CÓDIGOS COMPLETOS aquí.

opciones_sensor = ordenados (datos[“sensor_id”].unique().tolist()) sensor_select = Seleccionar(título=”Filtro de ID del sensor”, valor=sensor_options[0]opciones=opciones_sensor) temp_slider = Slider(title=”Temperatura máxima (°C)”, start=float(datos[“temp_c”].min()), final=flotante(datos[“temp_c”].max()), paso=0.5, valor=flotante(datos[“temp_c”].max())) columnas_disponibles = [“temp_c”, “pressure_kpa”, “humidity_pct”, “sensor_id”, “timestep”]
checkbox_group = CheckboxGroup(labels=columns_available, active=list(range(len(columns_available)))) def filter_mask(sensor_val, max_temp): retorno [(s == sensor_val) and (t <= max_temp)
for s, t in zip(data[“sensor_id”]datos[“temp_c”])]bool_filter = BooleanFilter(filter_mask(sensor_select.value, temp_slider.value)) view = CDSView(filter=bool_filter) p_filtered = figure(title=”Filtrado: Temp vs Presión”, ancho=400, alto=300, x_axis_label=”Temp (°C)”, y_axis_label=”Presión (kPa)”, herramientas=”pan,wheel_zoom,reset,box_select,lasso_select”) r_filtered = p_filtered.circle(x=”temp_c”, y=”presión_kpa”, tamaño=8, fill_alpha=0.7, fill_color=”ladrillo refractario”, line_color=”blanco”, fuente=fuente_principal, vista=vista) p_filtered.add_tools(HoverTool(tooltips=[
(“Temp”, “@temp_c{0.0}”), (“Pressure”, “@pressure_kpa{0.0}”),
(“Humidity”, “@humidity_pct{0.0}%”), (“Sensor”, “@sensor_id”)]renderizadores =[r_filtered])) def make_table_src(cols): devuelve ColumnDataSource(datos[cols]) table_src = make_table_src(columnas_disponibles) table_columns = [TableColumn(field=c, title=c) for c in columns_available]
table_widget = DataTable(fuente=table_src, columnas=table_columns, ancho=500, alto=200) def update_filters(attr, antiguo, nuevo): bool_filter.booleans = filter_mask(sensor_select.value, temp_slider.value) def update_table(attr, antiguo, nuevo): active_cols = [columns_available[i] para i en checkbox_group.active]new_src = make_table_src(active_cols) table_widget.source.data = new_src.data table_widget.columns = [TableColumn(field=c, title=c) for c in active_cols]

sensor_select.on_change(“value”, update_filters) temp_slider.on_change(“value”, update_filters) checkbox_group.on_change(“active”, update_table) Dashboard_controls = column(Div(text=”Filtros interactivos”), sensor_select, temp_slider, Div(text=”Columnas en la tabla”), checkbox_group) Dashboard_layout = fila (columna (p_filtered, table_widget), tablero_controles) mostrar (dashboard_layout)

Introducimos interactividad a través de widgets como menús desplegables, controles deslizantes y casillas de verificación. Filtramos datos dinámicamente y actualizamos tablas en tiempo real, lo que nos permite explorar fácilmente diferentes subconjuntos y atributos del conjunto de datos. Consulta los CÓDIGOS COMPLETOS aquí.

mini_source = ColumnDataSource({ “x”: np.linspace(0, 2*np.pi, 80), “y”: np.sin(np.linspace(0, 2*np.pi, 80)) }) p_wave = figure(title=”Onda sinusoidal (CustomJS: ampliar puntos)”, ancho=400, alto=250, herramientas=”pan,wheel_zoom,reset”) wave_render = p_wave.circle(x=”x”, y=”y”, size=6, fill_alpha=0.8, fill_color=”green”, line_color=”black”, source=mini_source) js_callback = CustomJS(args=dict(r=wave_render), code=”const new_size = r.glifo.tamaño.valor + 2; r.glifo.size = new_size;”) grow_button = Button(label=”Agrandar puntos (CustomJS)”, button_type=”éxito”) grow_button.js_on_click(js_callback) show(column(p_wave, grow_button))

Implementamos una interacción basada en JavaScript utilizando CustomJS de Bokeh. Creamos una visualización de onda sinusoidal y permitimos a los usuarios ampliar los marcadores de la trama con solo hacer clic en un botón, lo que demuestra el control del lado del cliente sin ninguna devolución de llamada de Python. Consulta los CÓDIGOS COMPLETOS aquí.

stream_source = ColumnDataSource({“t”: []”val”: []}) p_stream = figure(title=”Valor del sensor de transmisión”, ancho=500, alto=250, x_axis_label=”timestep”, y_axis_label=”valor”, herramientas=”pan,wheel_zoom,reset”) p_stream.line(x=”t”, y=”val”, fuente=stream_source, line_width=3, line_alpha=0.8) p_stream.circle(x=”t”, y=”val”, source=stream_source, size=6, fill_color=”red”) show(p_stream) para t en el rango(10): new_point = {“t”: [t]”val”: [np.sin(t/2) + 0.2*np.random.randn()]} stream_source.stream(nuevo_punto, rollover=200) show(p_stream)

Simulamos un flujo de datos en vivo agregando continuamente nuevos puntos de datos a nuestra gráfica. Observamos la actualización de la visualización de forma dinámica, mostrando cómo Bokeh puede manejar datos en tiempo real y proporcionar retroalimentación visual instantánea.

En conclusión, creamos un panel completamente funcional, en tiempo real e interactivo con el navegador que muestra todo el potencial de Bokeh. Aprendemos a visualizar múltiples dimensiones de datos, filtrar y actualizar imágenes dinámicamente e incluso aprovechar la integración de JavaScript para realizar actualizaciones instantáneas del lado del cliente directamente dentro del navegador. Esta experiencia práctica nos muestra cómo Bokeh fusiona sin esfuerzo Python y JavaScript, permitiéndonos diseñar paneles que no solo son interactivos sino también inteligentes, responsivos y listos para producción.

Consulta los CÓDIGOS COMPLETOS aquí. No dude en consultar nuestra página de GitHub para tutoriales, códigos y cuadernos. Además, no dude en seguirnos en Twitter y no olvide unirse a nuestro SubReddit de más de 100.000 ML y suscribirse a nuestro boletín. ¡Esperar! estas en telegrama? Ahora también puedes unirte a nosotros en Telegram.

Asif Razzaq es el director ejecutivo de Marktechpost Media Inc.. Como empresario e ingeniero visionario, Asif está comprometido a aprovechar el potencial de la inteligencia artificial para el bien social. Su esfuerzo más reciente es el lanzamiento de una plataforma de medios de inteligencia artificial, Marktechpost, que se destaca por su cobertura en profundidad del aprendizaje automático y las noticias sobre aprendizaje profundo que es técnicamente sólida y fácilmente comprensible para una amplia audiencia. La plataforma cuenta con más de 2 millones de visitas mensuales, lo que ilustra su popularidad entre el público.

🙌 Siga MARKTECHPOST: agréguenos como fuente preferida en Google.