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

如何在Bokeh的同一图表中叠加多条折线?

解决方案:在Bokeh中叠加分组折线并通过CustomJS控制可见性

问题原因

你遇到的E-1024错误是因为Bokeh的Line、Patch这类连接拓扑型Glyph无法与CDSView过滤配合使用——过滤会打乱数据源的行顺序,导致折线无法正确连接分组内的点。

替代实现方案

以下提供两种符合你需求的方案,均无需使用multi_line:


方案1:为每个分组创建独立数据源,用CustomJS控制折线可见性

这种方法更直观,通过控制每个Line渲染器的visible属性来切换显示,同时保留散点图的全量数据源。

完整代码:

import pandas as pd
import numpy as np
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.models import CheckboxGroup, CustomJS, factor_mark, factor_cmap, Category10
from bokeh.layouts import column

# 构造数据
df = pd.DataFrame(
    {
        "seq": list(range(5)) + list(range(5)),
        "a": ["foo"] * 5 + ["bar"] * 5,
        "b": np.concatenate(
            (np.linspace(1, 5, 5, dtype=int), np.linspace(1, 5, 5, dtype=int) + 3)
        ),
    }
)

# 主数据源(用于散点图)
cds_scatter = ColumnDataSource(df)
# 分组数据源(用于折线图)
cds_foo = ColumnDataSource(df[df["a"] == "foo"])
cds_bar = ColumnDataSource(df[df["a"] == "bar"])

# 创建图表
myfig = figure(y_range=(-1, 10), height=400, width=400)
# 绘制散点图
myfig.scatter(
    "seq",
    "b",
    source=cds_scatter,
    marker=factor_mark("a", ["circle", "diamond"], ["foo", "bar"]),
    color=factor_cmap("a", Category10[3], ["foo", "bar"]),
    size=10,
)
# 绘制分组折线,分别保存渲染器对象
line_foo = myfig.line("seq", "b", source=cds_foo, color=Category10[3][0], line_width=2)
line_bar = myfig.line("seq", "b", source=cds_bar, color=Category10[3][1], line_width=2)

# 创建复选框控件,用于切换折线可见性
checkbox = CheckboxGroup(labels=["foo", "bar"], active=[0, 1])

# 编写CustomJS回调,控制折线的visible属性
callback = CustomJS(args=dict(line_foo=line_foo, line_bar=line_bar, checkbox=checkbox), code="""
    line_foo.visible = checkbox.active.includes(0);
    line_bar.visible = checkbox.active.includes(1);
""")
checkbox.js_on_change("active", callback)

# 布局并显示
show(column(checkbox, myfig))

方案2:通过CustomJS重置主数据源实现折线切换

如果你坚持要通过重置cds1.data来切换折线,可以将所有分组数据整合到主数据源的不同列中,通过CustomJS修改折线的y轴字段来实现切换(或替换整个数据源)。

完整代码:

import pandas as pd
import numpy as np
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.models import Select, CustomJS, factor_mark, factor_cmap, Category10
from bokeh.layouts import column

# 构造数据并整理分组列
df = pd.DataFrame(
    {
        "seq": list(range(5)) + list(range(5)),
        "a": ["foo"] * 5 + ["bar"] * 5,
        "b": np.concatenate(
            (np.linspace(1, 5, 5, dtype=int), np.linspace(1, 5, 5, dtype=int) + 3)
        ),
    }
)
# 添加分组专属列:foo_b和bar_b,空值用NaN填充(折线会自动跳过NaN)
df["foo_b"] = df.apply(lambda x: x["b"] if x["a"] == "foo" else np.nan, axis=1)
df["bar_b"] = df.apply(lambda x: x["b"] if x["a"] == "bar" else np.nan, axis=1)

cds1 = ColumnDataSource(df)

# 创建图表
myfig = figure(y_range=(-1, 10), height=400, width=400)
# 绘制散点图
myfig.scatter(
    "seq",
    "b",
    source=cds1,
    marker=factor_mark("a", ["circle", "diamond"], ["foo", "bar"]),
    color=factor_cmap("a", Category10[3], ["foo", "bar"]),
    size=10,
)
# 绘制折线,初始显示foo分组
line = myfig.line("seq", "foo_b", source=cds1, color=Category10[3][0], line_width=2)

# 创建下拉选择控件
select = Select(title="显示折线分组", options=["foo", "bar", "全部"], value="foo")

# CustomJS回调:修改折线的y字段,或重置数据源实现多线显示
callback = CustomJS(args=dict(line=line, cds=cds1, select=select), code="""
    const value = select.value;
    if (value === "foo") {
        line.glyph.y = {field: "foo_b"};
        line.glyph.line_color = "#1f77b4";
    } else if (value === "bar") {
        line.glyph.y = {field: "bar_b"};
        line.glyph.line_color = "#ff7f0e";
    } else {
        // 全部显示:临时修改数据源,添加合并后的折线数据
        const data = cds.data;
        // 构造包含两组连续点的数据(保证seq顺序)
        const new_seq = [...data.seq.slice(0,5), ...data.seq.slice(5,10)];
        const new_b = [...data.b.slice(0,5), ...data.b.slice(5,10)];
        cds.data = {seq: new_seq, b: new_b};
        line.glyph.y = {field: "b"};
        line.glyph.line_color = "#1f77b4";
    }
""")
select.js_on_change("value", callback)

# 布局并显示
show(column(select, myfig))

注意事项

  • 方案1的多渲染器方式更稳定,适合需要独立控制每条折线的场景;
  • 方案2的数据源重置方式需要注意数据顺序,保证折线连接的正确性;
  • 如果需要同时显示多条折线,方案1的复选框控制会更灵活。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:40:09