如何为Bokeh多列DataFrame折线图实现可滚动图例?
解决Bokeh大量图例的滚动需求
针对大量图例无法适配布局的问题,下面提供两种保留点击隐藏功能的可滚动图例实现方案:
方法一:直接修改图例DOM样式(内置图例场景)
通过CustomJS在图表加载后,给图例容器添加滚动样式,无需调整整体布局:
from bokeh.io import show from bokeh.layouts import gridplot from bokeh.plotting import figure from bokeh.palettes import Category20_20 from bokeh.models import CustomJS, Legend import pandas as pd import numpy as np # 生成测试数据 data1 = {str(key): [np.random.randint(0, 10) for i in range(100)] for key in range(20)} data1 = pd.DataFrame(data1) data2 = {str(key): [np.random.randint(5, 15) for i in range(100)] for key in range(20)} data2 = pd.DataFrame(data2) # 创建图表 graph1 = figure(title='Top graph') graph2 = figure(title='Bottom Graph', x_range=graph1.x_range) # 收集折线渲染器与标签,用于创建独立图例 renderers1 = [] for col, color in zip(data1.columns, Category20_20): r = graph1.line(data1.index, data1[col], color=color) renderers1.append((col, [r])) renderers2 = [] for col, color in zip(data2.columns, Category20_20): r = graph2.line(data2.index, data2[col], color=color) renderers2.append((col, [r])) # 创建图例并绑定点击隐藏功能 legend1 = Legend(items=renderers1, click_policy='hide') legend1.location = 'right' graph1.add_layout(legend1, 'right') legend2 = Legend(items=renderers2, click_policy='hide') legend2.location = 'right' graph2.add_layout(legend2, 'right') # 注入JS代码设置图例滚动样式 js_code = """ const legendEl = document.querySelector('#' + legend.id + '-container'); legendEl.style.maxHeight = '300px'; legendEl.style.overflowY = 'auto'; legendEl.style.paddingRight = '10px'; """ # 给两个图例添加滚动效果 for legend in [legend1, legend2]: callback = CustomJS(args={'legend': legend}, code=js_code) legend.js_on_change('visible', callback) # 布局并展示 overplot = gridplot([[graph1, None], [graph2, None]], width=1000, height=400) show(overplot)
关键说明
- 单独创建
Legend对象,方便后续样式控制 - 通过JS获取图例DOM容器,设置
maxHeight固定高度,超出部分自动显示垂直滚动条 - 保留了原有的
click_policy='hide'点击隐藏功能
方法二:独立滚动容器布局(分离图例与图表)
如果需要更灵活的布局,可将图例与图表分离,放在独立的滚动容器中:
from bokeh.io import show from bokeh.layouts import gridplot, row from bokeh.plotting import figure from bokeh.palettes import Category20_20 from bokeh.models import Legend import pandas as pd import numpy as np # 生成测试数据 data1 = {str(key): [np.random.randint(0, 10) for i in range(100)] for key in range(20)} data1 = pd.DataFrame(data1) data2 = {str(key): [np.random.randint(5, 15) for i in range(100)] for key in range(20)} data2 = pd.DataFrame(data2) # 创建不含内置图例的图表 graph1 = figure(title='Top graph', toolbar_location=None) graph2 = figure(title='Bottom Graph', x_range=graph1.x_range, toolbar_location=None) # 收集折线渲染器 renderers1 = [] for col, color in zip(data1.columns, Category20_20): r = graph1.line(data1.index, data1[col], color=color) renderers1.append((col, [r])) renderers2 = [] for col, color in zip(data2.columns, Category20_20): r = graph2.line(data2.index, data2[col], color=color) renderers2.append((col, [r])) # 创建带滚动样式的图例 legend1 = Legend(items=renderers1, click_policy='hide', width=100) legend1.style = {'max-height': '300px', 'overflow-y': 'auto', 'padding-right': '10px'} legend2 = Legend(items=renderers2, click_policy='hide', width=100) legend2.style = {'max-height': '300px', 'overflow-y': 'auto', 'padding-right': '10px'} # 布局:图表与图例横向排列 row1 = row(graph1, legend1) row2 = row(graph2, legend2) overplot = gridplot([[row1], [row2]], width=1100, height=400) show(overplot)
关键说明
- 将图表与图例通过
row布局分离,避免图例占用图表空间 - 直接给
Legend对象设置style属性,指定滚动规则,无需额外JS - 可根据需求调整图例的
width和max-height参数,适配显示需求
内容的提问来源于stack exchange,提问作者Andre
相关产品推荐
相关产品推荐

