index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html
![]()
本教程指导如何使用Bokeh库创建一个完全交互式、视觉吸引力强的数据可视化仪表盘。我们将原始数据转化为有洞察力的图表,并添加链接刷选、颜色渐变和实时过滤器等功能。通过Custom JavaScript,实现无需Python回调的即时浏览器端响应。最终,我们将Python的分析能力与JavaScript的响应性相结合,构建一个无缝、动态的仪表盘体验,重新定义数据可视化和交互方式。
📊 **数据可视化基础**: 教程从使用Bokeh库将原始数据转化为散点图、折线图等基础图表开始,并集成HoverTool以显示详细信息,为构建交互式仪表盘奠定基础。
🔗 **联动选择与多维分析**: 通过在不同图表之间实现联动刷选(Linked Brushing),用户可以在一个图表中进行选择,并在另一个图表中自动高亮显示相应数据,从而更有效地分析变量间的关系。
🌈 **增强视觉信息传达**: 引入颜色映射(Color Mapping)和颜色条(Color Bar),使图表能够通过颜色梯度直观地表示数据的第三个维度(如湿度),增强了信息的可读性和直观性。
⚙️ **交互式控件与数据过滤**: 集成了下拉菜单、滑块和复选框等控件,允许用户动态地过滤数据并更新表格显示,实现了实时的数据探索和属性筛选。
🚀 **客户端JavaScript交互**: 利用Bokeh的CustomJS功能,可以直接在浏览器端实现交互,例如通过按钮点击改变图表元素的大小,无需服务器端(Python)的往返通信,提升了响应速度和用户体验。
In this tutorial, we create a fully interactive, visually compelling data visualization dashboard using Bokeh. We start by turning raw data into insightful plots, then enhance them with features such as linked brushing, color gradients, and real-time filters powered by dropdowns and sliders. As we progress, we bring our dashboard to life with Custom JavaScript (CustomJS) interactivity, enabling instant browser-side responses without a single Python callback. By blending the best of Python’s analytical strength with JavaScript’s responsiveness, we build a seamless, dynamic dashboard experience that redefines how we visualize and interact with data. Check out the FULL CODES here.
!pip install bokeh pandas numpy scipy -qimport numpy as npimport pandas as pdfrom bokeh.io import output_notebook, show, export_png, output_filefrom bokeh.plotting import figurefrom bokeh.layouts import row, column, gridplotfrom bokeh.models import ( ColumnDataSource, HoverTool, LassoSelectTool, BoxSelectTool, TapTool, ColorBar, LinearColorMapper, BasicTicker, PrintfTickFormatter, Slider, Select, CheckboxGroup, CustomJS, CDSView, BooleanFilter, Div, Button)from bokeh.palettes import Viridis256from bokeh.models.widgets import DataTable, TableColumnoutput_notebook()np.random.seed(42)N = 300data = pd.DataFrame({ "temp_c": 20 + 5 * np.random.randn(N), "pressure_kpa": 101 + 3 * np.random.randn(N), "humidity_pct": 40 + 15 * np.random.randn(N), "sensor_id": np.random.choice(["A1","A2","B7","C3"], size=N), "timestep": np.arange(N)})source_main = ColumnDataSource(data)p_scatter = figure(title="Temperature vs Pressure", width=400, height=300, x_axis_label="Temperature (°C)", y_axis_label="Pressure (kPa)", tools="pan,wheel_zoom,reset")scat = p_scatter.circle(x="temp_c", y="pressure_kpa", size=8, fill_alpha=0.6, fill_color="orange", line_color="black", source=source_main, legend_label="Sensor Readings")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")], renderers=[scat])p_scatter.add_tools(hover)p_scatter.legend.location = "top_left"show(p_scatter)
We begin by setting up our environment and importing all the necessary libraries. We then create a synthetic dataset and visualize temperature against pressure using a simple scatter plot with hover functionality. This helps us establish a foundation for our interactive dashboard. Check out the FULL CODES here.
p_humidity = figure(title="Humidity vs Temperature (Linked Selection)", width=400, height=300, x_axis_label="Temperature (°C)", y_axis_label="Humidity (%)", tools="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")], renderers=[r2]))layout_linked = row(p_scatter, p_humidity)show(layout_linked)
We extend our visualization by adding another plot that links humidity and temperature through shared data. We use linked brushing so that selections in one plot automatically reflect in the other, helping us analyze relationships across multiple variables simultaneously. Check out the FULL CODES here.
color_mapper = LinearColorMapper(palette=Viridis256, low=data["humidity_pct"].min(), high=data["humidity_pct"].max())p_color = figure(title="Pressure vs Humidity (Colored by Humidity)", width=500, height=350, x_axis_label="Pressure (kPa)", y_axis_label="Humidity (%)", tools="pan,wheel_zoom,reset,box_select,lasso_select")r3 = p_color.circle(x="pressure_kpa", y="humidity_pct", size=8, fill_alpha=0.8, line_color=None, 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=None, location=(0,0), title="Humidity %")p_color.add_layout(color_bar, "right")show(p_color)
We enhance our visualization by introducing a continuous color mapping feature to represent humidity levels. By adding a color bar and gradient, we make our chart more informative and intuitive, allowing us to interpret variations visually. Check out the FULL CODES here.
sensor_options = sorted(data["sensor_id"].unique().tolist())sensor_select = Select(title="Sensor ID Filter", value=sensor_options[0], options=sensor_options)temp_slider = Slider(title="Max Temperature (°C)", start=float(data["temp_c"].min()), end=float(data["temp_c"].max()), step=0.5, value=float(data["temp_c"].max()))columns_available = ["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): return [(s == sensor_val) and (t <= max_temp) for s, t in zip(data["sensor_id"], data["temp_c"])]bool_filter = BooleanFilter(filter_mask(sensor_select.value, temp_slider.value))view = CDSView(filter=bool_filter)p_filtered = figure(title="Filtered: Temp vs Pressure", width=400, height=300, x_axis_label="Temp (°C)", y_axis_label="Pressure (kPa)", tools="pan,wheel_zoom,reset,box_select,lasso_select")r_filtered = p_filtered.circle(x="temp_c", y="pressure_kpa", size=8, fill_alpha=0.7, fill_color="firebrick", line_color="white", source=source_main, view=view)p_filtered.add_tools(HoverTool(tooltips=[ ("Temp", "@temp_c{0.0}"), ("Pressure", "@pressure_kpa{0.0}"), ("Humidity", "@humidity_pct{0.0}%"), ("Sensor", "@sensor_id")], renderers=[r_filtered]))def make_table_src(cols): return ColumnDataSource(data[cols])table_src = make_table_src(columns_available)table_columns = [TableColumn(field=c, title=c) for c in columns_available]table_widget = DataTable(source=table_src, columns=table_columns, width=500, height=200)def update_filters(attr, old, new): bool_filter.booleans = filter_mask(sensor_select.value, temp_slider.value)def update_table(attr, old, new): active_cols = [columns_available[i] for i in 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="<b>Interactive Filters</b>"), sensor_select, temp_slider, Div(text="<b>Columns in Table</b>"), checkbox_group)dashboard_layout = row(column(p_filtered, table_widget), dashboard_controls)show(dashboard_layout)
We introduce interactivity through widgets such as dropdowns, sliders, and checkboxes. We dynamically filter data and update tables in real time, enabling us to easily explore different subsets and attributes of the dataset. Check out the FULL CODES here.
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="Sine Wave (CustomJS: Enlarge points)", width=400, height=250, tools="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.glyph.size.value + 2; r.glyph.size = new_size;")grow_button = Button(label="Enlarge points (CustomJS)", button_type="success")grow_button.js_on_click(js_callback)show(column(p_wave, grow_button))
We implement a JavaScript-based interaction using Bokeh’s CustomJS. We create a sine wave visualization and allow users to enlarge the plot markers with a button click, demonstrating client-side control without any Python callbacks. Check out the FULL CODES here.
stream_source = ColumnDataSource({"t": [], "val": []})p_stream = figure(title="Streaming Sensor Value", width=500, height=250, x_axis_label="timestep", y_axis_label="value", tools="pan,wheel_zoom,reset")p_stream.line(x="t", y="val", source=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)for t in range(10): new_point = {"t": [t], "val": [np.sin(t/2) + 0.2*np.random.randn()]} stream_source.stream(new_point, rollover=200)show(p_stream)
We simulate a live data stream by continuously adding new data points to our plot. We watch the visualization update dynamically, showcasing how Bokeh can handle real-time data and provide instant visual feedback.
In conclusion, we create a fully functional, real-time, and browser-interactive dashboard that showcases the full potential of Bokeh. We learn how to visualize multiple dimensions of data, dynamically filter and update visuals, and even harness JavaScript integration to make instant, client-side updates directly within the browser. This hands-on experience shows us how Bokeh effortlessly merges Python and JavaScript, empowering us to design dashboards that are not just interactive but intelligent, responsive, and production-ready.
Check out the FULL CODES here. Feel free to check out our GitHub Page for Tutorials, Codes and Notebooks. Also, feel free to follow us on Twitter and don’t forget to join our 100k+ ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.
The post How to Build a Fully Interactive, Real-Time Visualization Dashboard Using Bokeh and Custom JavaScript? appeared first on MarkTechPost.