如何为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) | -2474 | nan | -1128 | -4424 | 3327 | nan | -3021 | -432 |
| (Timestamp('2023-01-03 00:00:00'), 18.0) | -1056 | nan | 7373 | -1199 | -1948 | nan | -731 | -165 |
| (Timestamp('2023-01-03 00:00:00'), 102.0) | 6613 | nan | 4045 | 2683 | 425 | nan | nan | nan |
| (Timestamp('2023-01-03 00:00:00'), 131.0) | 2890 | nan | 5971 | 3882 | -84 | nan | 361 | nan |
| (Timestamp('2023-01-03 00:00:00'), 1000.0) | 12068 | nan | 18309 | 17182 | 840 | nan | -2437 | 5518 |
| (Timestamp('2023-01-04 00:00:00'), 1.0) | -2388 | nan | -996 | -4463 | 3053 | nan | -2650 | -388 |
| (Timestamp('2023-01-04 00:00:00'), 18.0) | -563 | nan | 6464 | -1385 | -829 | nan | -1762 | 318 |
| (Timestamp('2023-01-04 00:00:00'), 102.0) | 6309 | nan | 4350 | 1707 | 508 | nan | nan | nan |
| (Timestamp('2023-01-04 00:00:00'), 131.0) | 2976 | nan | 5977 | 4001 | -66 | nan | 258 | nan |
现咨询两个问题:
- 如何在循环中的每个
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
相关产品推荐
相关产品推荐

