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

如何为df.plot_bokeh生成的图表添加MultiChoice组件及替代实现咨询

问题描述

我当前使用以下代码生成折线图列表:

fig_list = []
grouped = df.unstack().groupby(level=1, axis=1)
for key in grouped.groups.keys():
    tmp = grouped.get_group(key).droplevel([0, 1], axis=1)
    fig = tmp.plot_bokeh(kind="line", title=key,
                   rangetool=True,
                   show_figure=False, **kwargs)
    fig_list.append(fig)

对应的df是带有多层行列索引的DataFrame,结构如下:

索引('IC', 'IC00')('IC', 'IC01')('IC', 'IC02')('IC', 'IC03')('IF', 'IF00')('IF', 'IF01')('IF', 'IF02')('IF', 'IF03')
(Timestamp('2023-01-03 00:00:00'), 1.0)-2474nan-1128-44243327nan-3021-432
(Timestamp('2023-01-03 00:00:00'), 18.0)-1056nan7373-1199-1948nan-731-165
(Timestamp('2023-01-03 00:00:00'), 102.0)6613nan40452683425nannannan
(Timestamp('2023-01-03 00:00:00'), 131.0)2890nan59713882-84nan361nan
(Timestamp('2023-01-03 00:00:00'), 1000.0)12068nan1830917182840nan-24375518
(Timestamp('2023-01-04 00:00:00'), 1.0)-2388nan-996-44633053nan-2650-388
(Timestamp('2023-01-04 00:00:00'), 18.0)-563nan6464-1385-829nan-1762318
(Timestamp('2023-01-04 00:00:00'), 102.0)6309nan43501707508nannannan
(Timestamp('2023-01-04 00:00:00'), 131.0)2976nan59774001-66nan258nan

现咨询两个问题:

  • 如何在循环中的每个fig添加MultiChoice组件,实现交互式选择id的功能?
  • 由于不熟悉Bokeh,若要实现与plot_bokeh相同的效果,是否需要先执行df.stack().stack().reset_index()操作?

解答

1. 给每个折线图添加MultiChoice交互式组件

要实现这个功能,需要借助Bokeh的MultiChoice组件和回调函数,具体步骤如下:

步骤1:导入必要模块

from bokeh.models import MultiChoice, CustomJS
from bokeh.layouts import column

步骤2:修改循环代码,添加组件与交互逻辑

在生成每个fig的循环中,获取图表的线条对象,创建多选组件并编写JS回调控制线条显示/隐藏:

fig_list = []
grouped = df.unstack().groupby(level=1, axis=1)
for key in grouped.groups.keys():
    tmp = grouped.get_group(key).droplevel([0, 1], axis=1)
    # 生成折线图
    fig = tmp.plot_bokeh(kind="line", title=key,
                   rangetool=True,
                   show_figure=False, **kwargs)
    
    # 提取图表中所有折线的名称(对应要选择的id)
    renderers = [r for r in fig.renderers if hasattr(r, 'name')]
    line_names = [r.name for r in renderers]
    
    # 创建MultiChoice组件,默认选中所有id
    multi_choice = MultiChoice(options=line_names, value=line_names, title="选择要显示的ID:")
    
    # 编写JS回调,控制折线的显示状态
    callback = CustomJS(args=dict(renderers=renderers, multi_choice=multi_choice), code="""
        renderers.forEach(renderer => {
            renderer.visible = multi_choice.value.includes(renderer.name);
        });
    """)
    
    # 绑定组件的数值变化事件到回调
    multi_choice.js_on_change('value', callback)
    
    # 将组件添加到图表布局顶部
    fig.layout = column(multi_choice, fig.layout)
    
    fig_list.append(fig)

修改后,每个图表都会附带一个多选框,选择对应的id后,图表会实时切换对应折线的显示状态。

2. 是否需要执行df.stack().stack().reset_index()操作?

不需要额外执行这个操作。

你使用的plot_bokeh是pandas_bokeh库的封装方法,它已经能自动处理多层索引的DataFrame。原代码中通过groupby(level=1, axis=1)分组后得到的tmp,结构已经符合plot_bokeh的要求,直接调用方法就能生成正确的折线图。

如果是手动使用Bokeh原生API绘制图表,可能需要将多层索引展开为普通列(比如用reset_index)来映射坐标轴,但pandas_bokeh已经封装了这些数据处理逻辑,无需手动调整数据结构。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:39:53