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

Bokeh:隐藏图例系列时自动更新图表缩放或Y轴

解决Bokeh隐藏图例系列时自动更新Y轴范围/框选缩放的问题

嘿,这个需求Bokeh完全能搞定!我来给你详细拆解两种实现思路,分别对应自动更新Y轴范围和框选缩放的配合:

一、自动更新Y轴范围

Bokeh默认点击图例隐藏系列时,只会隐藏对应的图形元素,不会自动调整轴范围。要实现这个功能,我们需要给图例的点击事件添加回调逻辑,根据可见系列的数据重新计算Y轴的上下限。这里分两种场景:

1. 使用Bokeh Server(Python回调,更灵活)

如果你的可视化应用是基于Bokeh Server运行的,用Python写回调会更直观易维护。核心逻辑是监听每个渲染器的visible属性变化,收集所有可见系列的Y数据,计算最小最大值后更新Y轴范围。

示例代码:

from bokeh.models import ColumnDataSource, Legend, LegendItem, Range1d
from bokeh.plotting import figure, curdoc
from bokeh.layouts import layout

# 构造测试数据
source1 = ColumnDataSource(data=dict(x=[1,2,3], y=[10,20,30]))
source2 = ColumnDataSource(data=dict(x=[1,2,3], y=[5,15,25]))
source3 = ColumnDataSource(data=dict(x=[1,2,3], y=[2,8,14]))

# 创建图表,默认Y轴范围,添加框选和重置工具
p = figure(x_range=(1,3), y_range=(0,30), tools="box_zoom,reset")
r1 = p.vbar(x='x', top='y', width=0.2, source=source1, color="red")
r2 = p.vbar(x='x', top='y', width=0.2, source=source2, color="green", x_offset=0.25)
r3 = p.vbar(x='x', top='y', width=0.2, source=source3, color="blue", x_offset=0.5)

# 创建图例
legend = Legend(items=[
    LegendItem(label="系列1", renderers=[r1]),
    LegendItem(label="系列2", renderers=[r2]),
    LegendItem(label="系列3", renderers=[r3]),
])
p.add_layout(legend, 'right')

# 定义Y轴范围更新的回调函数
def update_y_range(attr, old, new):
    # 收集所有可见渲染器的Y数据
    y_values = []
    for item in legend.items:
        renderer = item.renderers[0]
        if renderer.visible:
            y_values.extend(renderer.data_source.data['y'])
    
    if y_values:
        min_y = min(y_values)
        max_y = max(y_values)
        # 给Y轴留10%的边距,避免图形贴到轴上
        margin = 0.1 * (max_y - min_y)
        p.y_range.start = min_y - margin
        p.y_range.end = max_y + margin
    else:
        # 如果所有系列都隐藏,恢复初始Y轴范围
        p.y_range.start = 0
        p.y_range.end = 30

# 给每个渲染器的visible属性绑定回调
for item in legend.items:
    renderer = item.renderers[0]
    renderer.on_change('visible', update_y_range)

curdoc().add_root(layout([p]))

2. 纯前端CustomJS(无需Bokeh Server)

如果只是生成静态HTML页面,用CustomJS写前端回调就能实现同样效果,不需要后端服务:

示例代码:

from bokeh.models import ColumnDataSource, Legend, LegendItem, CustomJS
from bokeh.plotting import figure, show
from bokeh.layouts import layout

source1 = ColumnDataSource(data=dict(x=[1,2,3], y=[10,20,30]))
source2 = ColumnDataSource(data=dict(x=[1,2,3], y=[5,15,25]))
source3 = ColumnDataSource(data=dict(x=[1,2,3], y=[2,8,14]))

p = figure(x_range=(1,3), y_range=(0,30), tools="box_zoom,reset")
r1 = p.vbar(x='x', top='y', width=0.2, source=source1, color="red")
r2 = p.vbar(x='x', top='y', width=0.2, source=source2, color="green", x_offset=0.25)
r3 = p.vbar(x='x', top='y', width=0.2, source=source3, color="blue", x_offset=0.5)

legend = Legend(items=[
    LegendItem(label="系列1", renderers=[r1]),
    LegendItem(label="系列2", renderers=[r2]),
    LegendItem(label="系列3", renderers=[r3]),
])
p.add_layout(legend, 'right')

# 构造前端回调代码
callback = CustomJS(args=dict(renderers=[r1, r2, r3], y_range=p.y_range), code="""
    const renderers = renderers;
    let yValues = [];
    renderers.forEach(renderer => {
        if (renderer.visible) {
            const yData = renderer.data_source.data['y'];
            yValues = yValues.concat(yData);
        }
    });
    if (yValues.length > 0) {
        const minY = Math.min(...yValues);
        const maxY = Math.max(...yValues);
        const margin = 0.1 * (maxY - minY);
        y_range.start = minY - margin;
        y_range.end = maxY + margin;
    } else {
        // 所有系列隐藏时恢复初始范围
        y_range.start = 0;
        y_range.end = 30;
    }
""")

# 给每个渲染器绑定回调
for r in [r1, r2, r3]:
    r.js_on_change('visible', callback)

show(layout([p]))

二、框选缩放的配合

Bokeh默认的BoxZoomTool(已经在上面的代码中通过tools="box_zoom,reset"添加)完全支持框选缩放,而且会自动忽略隐藏的图形元素。需要注意两点:

  • 确保工具已经添加到figure的tools参数中
  • 如果使用了上面的Y轴自动更新逻辑,隐藏/显示系列时会重新计算轴范围,覆盖之前的缩放结果。如果希望保留手动缩放的状态,可以在回调里添加判断逻辑(比如检测轴范围是否被手动修改过),不过大多数场景下,自动适配可见数据的逻辑更符合需求。

内容的提问来源于stack exchange,提问作者davidvarenne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:29:01