勾选复选框时Bokeh图表无法显示的技术问题求助
需求实现:勾选DataTable复选框显示对应ID的Bokeh图表
需求说明
- 基于包含
id、data_point、max、min列的DataFrame,实现勾选ID旁复选框时,展示对应ID的Bokeh折线图 - 若
data_point为空、None或空数组[],则不显示该ID的图表 - 每个唯一ID需独立控制显示/隐藏,单ID图表样式如下:

原始尝试代码
import logging import random import pandas as pd from bokeh.plotting import figure from bokeh.models import HoverTool, ColumnDataSource, DataTable, CustomJS from bokeh.palettes import Turbo256 data = df if len(data) == 0: logging.debug(" No data") else: req_hover = HoverTool( tooltips=[ ('data_point: ', '@data_point'), ('id: ', '@id')]) req_figure = figure(title='Graph', x_axis_label="Index", y_axis_label="id/data_point [ms]", plot_width=800, plot_height=400, output_backend="webgl") req_figure.add_tools(handover_hover) id_values = data['id'].drop_duplicates() column_view_data = data[ ['id', 'tag', 'sw', 'name']].drop_duplicates().dropna() column_view_data_source = ColumnDataSource(column_view_data) data_table = DataTable(selectable='checkbox', source=column_view_data_source, columns=columns, width=1450, height=400, css_classes=['blueTable']) name_dict_req = {'name': [], 'legend': [], 'label': []} logging.info('START OF DRAWINGS') for ind, element in enumerate(elements): d = [] for i, item in data.iterrows(): it_color = Turbo256[random.randint(0, 255)] if element == item['id']: if item['data_point'] is None or str(item['data_point']) == '[]': item['data_point'] = '0' else: item['data_point'] = [float(x) for x in item['data_point'].strip('[').strip(']').split(',')] raw_data_element_count = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] # this is my x axis which will be 1 to 20 d.append({'id': id, 'number of raw data points': data_point_element_count, 'data_point': item['data_point']}) d_new = pd.DataFrame(d) name_glyph_req = req_figure.line(x='number of raw data points', y='data_point', line_width=2, legend_label=str(item['id']), source=d_new, color=it_color) name_dict_req['name'].append(name_glyph_req) name_dict_req['label'].append(str(item['id'])) logging.info('AFTER DRAWINGS LOOP') for label in range(len(data.id.unique())): name_dict_req['legend'].append(req_figure.legend.items[label]) initial_value = [] options = list(data.id.unique()) for i, name in enumerate(options): options[i] = str(name) for i in range(len(options)): if name_dict_req['label'][i] in initial_value: name_dict_req['name'][i].visible = True name_dict_req['legend'][i].visible = True else: name_dict_req['name'][i].visible = False name_dict_req['legend'][i].visible = False ######################### callback_datatable = CustomJS(args=dict(source=column_view_data_source), code=callback_file_content_mq) ################### column_view_data_source.selected.js_on_change('indices', callback_datatable) req_figure.legend.location = "top_left" req_figure.legend.click_policy = "hide" logging.info('END DRAWINGS END SETUP')
修正后的完整实现代码
核心改进
- 预处理过滤无效
data_point的ID - 用字典存储每个ID对应的图表元素,简化控制逻辑
- 完善JS回调,实现复选框与图表显示的联动
- 修复原始代码中未定义变量、变量名错误等问题
import logging import random import pandas as pd from bokeh.plotting import figure, show from bokeh.models import HoverTool, ColumnDataSource, DataTable, TableColumn, CustomJS from bokeh.palettes import Turbo256 from bokeh.layouts import column # 替换为你的实际DataFrame data = pd.DataFrame({ 'id': ['A', 'A', 'B', 'C', 'C'], 'data_point': ['[1,2,3,4,5]', None, '[6,7,8,9,10]', '[11,12,13,14,15]', '[]'], 'tag': ['tag1', 'tag1', 'tag2', 'tag3', 'tag3'], 'sw': ['sw1', 'sw1', 'sw2', 'sw3', 'sw3'], 'name': ['nameA', 'nameA', 'nameB', 'nameC', 'nameC'], 'max': [5,5,10,15,15], 'min': [1,1,6,11,11] }) # 预处理:过滤无效data_point def process_data_point(val): if pd.isna(val) or str(val).strip() in ('[]', ''): return None try: return [float(x.strip()) for x in str(val).strip('[]').split(',')] except: return None data['processed_data'] = data['data_point'].apply(process_data_point) valid_ids = data[data['processed_data'].notna()]['id'].unique() data = data[data['id'].isin(valid_ids)] if len(data) == 0: logging.debug("No data available") else: # 创建Hover工具 req_hover = HoverTool( tooltips=[ ('ID', '@id'), ('数据点', '@data_point') ] ) # 创建基础图表 req_figure = figure( title='ID数据折线图', x_axis_label='数据点索引', y_axis_label='数据值 [ms]', plot_width=800, plot_height=400, output_backend="webgl" ) req_figure.add_tools(req_hover) # 准备DataTable数据 column_view_data = data[['id', 'tag', 'sw', 'name']].drop_duplicates().reset_index(drop=True) column_view_data_source = ColumnDataSource(column_view_data) # 定义表格列 columns = [ TableColumn(field='id', title='ID'), TableColumn(field='tag', title='标签'), TableColumn(field='sw', title='软件版本'), TableColumn(field='name', title='名称') ] data_table = DataTable( selectable='checkbox', source=column_view_data_source, columns=columns, width=1450, height=400, css_classes=['blueTable'] ) # 存储每个ID对应的图表元素 id_glyph_map = {} x_axis = list(range(1, 21)) # 固定x轴1-20 # 绘制每个有效ID的折线 for unique_id in valid_ids: id_data = data[(data['id'] == unique_id) & (data['processed_data'].notna())] if len(id_data) == 0: continue # 构造匹配x轴的数据源 plot_data = [] for _, row in id_data.iterrows(): data_vals = row['processed_data'] matched_vals = data_vals + [None]*(20 - len(data_vals)) if len(data_vals) <20 else data_vals[:20] plot_data.extend([ {'id': unique_id, 'x': x, 'data_point': val} for x, val in zip(x_axis, matched_vals) if val is not None ]) source = ColumnDataSource(data=plot_data) color = Turbo256[random.randint(0, 255)] glyph = req_figure.line( x='x', y='data_point', line_width=2, legend_label=str(unique_id), source=source, color=color, visible=False # 默认隐藏 ) id_glyph_map[unique_id] = glyph # 配置图例 req_figure.legend.location = "top_left" req_figure.legend.click_policy = "hide" # 复选框联动回调 callback_code = """ const selected_ids = source.data.id.filter((_, idx) => source.selected.indices.includes(idx)); Object.keys(id_glyph_map).forEach(id => { const glyph = id_glyph_map[id]; glyph.visible = selected_ids.includes(id); }); """ callback_datatable = CustomJS( args=dict(source=column_view_data_source, id_glyph_map=id_glyph_map), code=callback_code ) column_view_data_source.selected.js_on_change('indices', callback_datatable) # 布局并展示 layout = column(data_table, req_figure) show(layout)
内容的提问来源于stack exchange,提问作者noob
相关产品推荐
相关产品推荐

