Bokeh仪表板Slider控件实现DataTable响应式过滤失效问题求助
Bokeh滑块过滤DataTable失效问题修复方案
核心错误点
- 变量名不匹配:CustomJS的
args参数中传入的表格变量名为table,但回调代码中使用的是data_table,JS环境无法识别目标表格对象 - 缺少原始数据传入:回调代码中使用的
groupeddf、sensor_2都属于Python端变量,没有传入JS执行环境,无法直接读取 - 语法不兼容:CustomJS运行在浏览器端的JavaScript环境,不支持Pandas DataFrame的过滤语法,需要按JS逻辑实现数据过滤
- 无全量数据副本:直接修改表格绑定的ColumnDataSource会丢失原始全量数据,后续调整滑块阈值时无法恢复被过滤的数据,需要单独存储全量原始数据用于每次过滤
修正后完整代码
from operator import index from bokeh.models.widgets.markups import Div import numpy as np from numpy.lib import source import pandas as pd from bokeh.io import curdoc,show from bokeh.layouts import column, row, gridplot,widgetbox from bokeh.models import ColumnDataSource, CustomJS,Select, Slider, BoxSelectTool, LassoSelectTool, Tabs, Panel, LinearColorMapper, ColorBar, BasicTicker, PrintfTickFormatter, MultiSelect, DataTable, TableColumn from bokeh.plotting import figure, curdoc from bokeh.palettes import viridis, gray, cividis, Category20, Category20c from bokeh.transform import factor_cmap,cumsum from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import classification_report, confusion_matrix, mean_squared_error, r2_score, recall_score, f1_score from sklearn.preprocessing import StandardScaler, PolynomialFeatures from sklearn.cluster import KMeans from sklearn.svm import SVC from sklearn.decomposition import PCA from math import pi from bokeh.transform import cumsum np.random.seed(42) #define the categorical variable category_a = ['A','B','C'] category_b = ['X','Y','Z'] df_random = pd.DataFrame({ 'id': np.arange(0, 100), 'date': pd.date_range(start='1/1/2021', periods=100, freq='D').astype(str), # 日期转字符串避免JS序列化问题 'month':np.random.randint(1, 12, 100), 'sensor_1': np.random.uniform(0, 1,100), 'sensor_2': np.random.uniform(10, 150, 100), 'sensor_3': np.random.randint(0, 90, 100), 'sensor_4': np.random.randint(0, 450, 100), 'sensor_5': np.random.randint(0, 352, 100), 'categorya': np.random.choice(category_a, 100, p=[0.2, 0.4, 0.4]), 'categoryb': np.random.choice(category_b, 100, p=[0.6, 0.2, 0.2]), }) source = ColumnDataSource(data=df_random) columns = [ TableColumn(field='id', title='ID'), TableColumn(field='date', title='Date'), TableColumn(field='month', title='Month'), TableColumn(field='sensor_1', title='Sensor 1'), TableColumn(field='sensor_2', title='Sensor 2'), TableColumn(field='sensor_3', title='Sensor 3'), TableColumn(field='sensor_4', title='Sensor 4'), TableColumn(field='sensor_5', title='Sensor 5'), TableColumn(field='categorya', title='Category A'), TableColumn(field='categoryb', title='Category B'), ] data_table = DataTable(source=source, columns=columns, width=1100, height=280) slider = Slider(start = 1, end = 150, value = 10, step = 1, title = "sensor_2最小阈值", width = 300) # 阈值范围调整为和sensor_2匹配 callback_code = """ const threshold = slider.value; const original = original_data; const new_data = {}; // 初始化所有列的空数组 Object.keys(original).forEach(col => { new_data[col] = []; }); // 逐行过滤符合条件的数据 for (let i = 0; i < original.sensor_2.length; i++) { if (original.sensor_2[i] >= threshold) { Object.keys(original).forEach(col => { new_data[col].push(original[col][i]); }); } } table.source.data = new_data; """ # 传入全量原始数据、滑块、表格三个对象 callback = CustomJS(args = dict(slider = slider, table = data_table, original_data=df_random.to_dict('list')), code = callback_code) slider.js_on_change('value', callback) layouttable=column(slider, data_table) # widgetbox已弃用,替换为column兼容性更好 curdoc().title = "table" curdoc().add_root(layouttable)
多滑块扩展说明
后续新增滑块实现多维度过滤只需两步:
- 定义新滑块实例后,将其加入CustomJS的
args字典中 - 在回调代码的过滤判断逻辑中,新增对应列的阈值判断条件即可,所有过滤都基于全量原始数据副本,不会出现数据丢失问题
内容的提问来源于stack exchange,提问作者metaltoaster
相关产品推荐
相关产品推荐

