如何用Bokeh创建适配可变Excel输入输出的动态堆叠柱状图
动态适配数据结构的Bokeh堆叠柱状图解决方案
我正在开展一项数据可视化项目,需使用Bokeh创建堆叠柱状图。数据来自定期更新的Excel文件,包含数量可变的输入与输出字段,数据结构会发生变化。目标是让Bokeh图表无需手动修改代码,即可自动适配这些变化,能展示固定部分输入、其余输入可变时对应的所有输出堆叠情况。目前已实现固定2个输入、3个输出场景的静态堆叠图,但无法找到适配输入输出数量动态变化的解决方案,希望获得可自动读取Excel、识别数据结构并更新图表的方案。
示例数据结构
| Input_1 | Input_2 | Output_1 | Output_2 | Output_3 |
|---|---|---|---|---|
| 1 | 1 | 100 | 200 | 200 |
| 2 | 1 | 150 | 150 | 200 |
| 3 | 1 | 200 | 100 | 200 |
| 1 | 2 | 200 | 200 | 100 |
| 2 | 2 | 150 | 200 | 150 |
| 3 | 2 | 100 | 200 | 200 |
| 1 | 3 | 200 | 100 | 200 |
| 2 | 3 | 200 | 150 | 150 |
| 3 | 3 | 200 | 200 | 100 |
静态场景实现代码
import pandas as pd from bokeh.plotting import figure, output_file, show from bokeh.layouts import gridplot, row from bokeh.models import ColumnDataSource data_frame = pd.read_excel("example.xlsx") data_frame['Input_1'] = data_frame['Input_1'].astype(str) data_frame['Input_2'] = data_frame['Input_2'].astype(str) output_file("stacked_bar_charts.html") unique_input_1 = data_frame['Input_1'].unique() unique_input_2 = data_frame['Input_2'].unique() plots_for_input_1 = [] plots_for_input_2 = [] for value in unique_input_1: filtered_data = data_frame[data_frame["Input_1"] == value] filtered_data = filtered_data.sort_values(by='Input_2') source = ColumnDataSource(filtered_data) plot = figure(title=f"Input_1 = {value} fixed", x_range=filtered_data["Input_2"].unique(), height=300, width=500 ) plot.xaxis.axis_label = "Input_2" plot.vbar_stack(stackers=["Output_1", "Output_2", "Output_3"], x="Input_2", width=0.9, color=["orange", "gray", "brown"], source=source, legend_label=["Output 1", "Output 2", "Output 3"] ) plots_for_input_1.append(plot) for value in unique_input_2: filtered_data = data_frame[data_frame['Input_2'] == value] filtered_data = filtered_data.sort_values(by='Input_1') source = ColumnDataSource(filtered_data) plot = figure(title=f"Input_2 = {value} fixed", x_range=filtered_data["Input_1"].unique(), height=300, width=500 ) plot.xaxis.axis_label = "Input_1" plot.vbar_stack(stackers=["Output_1", "Output_2", "Output_3"], x="Input_1", width=0.9, color=["orange", "gray", "brown"], source=source, legend_label=["Output 1", "Output 2", "Output 3"] ) plots_for_input_2.append(plot) grid_for_input_1 = gridplot(plots_for_input_1, ncols=1) grid_for_input_2 = gridplot(plots_for_input_2, ncols=1) final_layout = row(grid_for_input_1, grid_for_input_2) show(final_layout)
目标可视化效果示例
- Bokeh示例图1:

- Bokeh示例图2:

动态适配解决方案
核心思路
- 自动识别输入输出字段:通过列名前缀区分输入(
Input_开头)和输出(Output_开头)字段 - 动态生成颜色映射:为每个输出字段分配唯一颜色,避免硬编码
- 通用化绘图逻辑:循环遍历所有输入字段,对每个输入字段固定时,生成对应可变输入的堆叠柱状图
完整代码实现
import pandas as pd from bokeh.plotting import figure, output_file, show from bokeh.layouts import gridplot, row from bokeh.models import ColumnDataSource from bokeh.palettes import Category20 # 用于生成动态颜色 # 读取Excel数据 data_frame = pd.read_excel("example.xlsx") # 自动识别输入和输出字段 input_cols = [col for col in data_frame.columns if col.startswith('Input_')] output_cols = [col for col in data_frame.columns if col.startswith('Output_')] # 将所有输入字段转为字符串类型(确保x轴为分类值) for col in input_cols: data_frame[col] = data_frame[col].astype(str) # 生成输出字段对应的颜色和图例标签 output_colors = Category20[len(output_cols)] if len(output_cols) <=20 else Category20[20] + [f'#{i:06x}' for i in range(len(output_cols)-20)] legend_labels = [col.replace('Output_', 'Output ') for col in output_cols] output_file("dynamic_stacked_bar_charts.html") all_plots = [] # 遍历每个输入字段,作为固定维度生成图表 for fixed_col in input_cols: # 获取当前固定字段的唯一值 unique_fixed_values = data_frame[fixed_col].unique() plot_list = [] # 为每个固定值生成堆叠图 for fixed_val in unique_fixed_values: # 过滤数据:固定当前输入字段的值 filtered_data = data_frame[data_frame[fixed_col] == fixed_val] # 确定可变的输入字段(除了当前固定的字段) variable_col = [col for col in input_cols if col != fixed_col][0] # 按可变字段排序 filtered_data = filtered_data.sort_values(by=variable_col) source = ColumnDataSource(filtered_data) plot = figure(title=f"{fixed_col} = {fixed_val} fixed", x_range=filtered_data[variable_col].unique(), height=300, width=500) plot.xaxis.axis_label = variable_col # 动态生成堆叠柱状图 plot.vbar_stack(stackers=output_cols, x=variable_col, width=0.9, color=output_colors, source=source, legend_label=legend_labels) plot.legend.location = "top_left" plot.legend.click_policy = "hide" # 可选:添加图例隐藏交互 plot_list.append(plot) # 将当前固定字段对应的所有图组成网格 plot_grid = gridplot(plot_list, ncols=1) all_plots.append(plot_grid) # 组合所有网格布局 final_layout = row(*all_plots) show(final_layout)
代码说明
- 字段自动识别:通过列表推导式筛选
Input_和Output_开头的列,无需硬编码字段名 - 动态颜色分配:使用Bokeh内置的
Category20调色板,输出字段超过20个时自动生成额外颜色 - 通用绘图循环:遍历每个输入字段作为固定维度,自动找到剩余的可变输入字段,生成对应堆叠图
- 交互增强:添加了图例点击隐藏功能,提升用户体验
内容的提问来源于stack exchange,提问作者Silberspecht
相关产品推荐
相关产品推荐

