You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:35:30