Holoviews中实现双标记点图与双流关联DynamicMap时序联动
Holoviews实现不同标记散点图+双流联动时序图
核心问题解决思路
- 标记与图例问题:
hv.dim动态设置marker无法生成图例,原因是动态映射不会触发图例生成逻辑。解决方案是将两类数据拆分为独立的Points对象,分别设置标记和标签后叠加,自动生成图例。 - 双流联动问题:为两个
Points分别创建Selection1D流,在DynamicMap的回调函数中同时监听两个流的选中索引,判断哪类数据被点击后返回对应时序图。
完整代码实现
import holoviews as hv import pandas as pd hv.extension('bokeh') # 模拟散点数据(替换为你的真实数据) scatter_data = pd.DataFrame({ 'x': [1,2,3,4,5], 'y': [2,4,1,5,3], 'value': [10,20,15,25,18], 'type': ['d','h','d','h','d'], 'id': ['A','B','C','D','E'] }) # 模拟时序数据(替换为你的真实数据,key对应散点的id) timeseries_data = { 'A': pd.DataFrame({'time': pd.date_range('2023-01-01', periods=10), 'val': range(10)}), 'B': pd.DataFrame({'time': pd.date_range('2023-01-01', periods=10), 'val': range(10,20)}), 'C': pd.DataFrame({'time': pd.date_range('2023-01-01', periods=10), 'val': range(5,15)}), 'D': pd.DataFrame({'time': pd.date_range('2023-01-01', periods=10), 'val': range(15,25)}), 'E': pd.DataFrame({'time': pd.date_range('2023-01-01', periods=10), 'val': range(20,30)}) } # 1. 拆分数据并创建带标记和图例的散点图 data_d = scatter_data[scatter_data['type'] == 'd'] data_h = scatter_data[scatter_data['type'] == 'h'] points_d = hv.Points(data_d, kdims=['x', 'y'], vdims=['value', 'id', 'type']).opts( marker='circle', color='value', cmap='viridis', label='Type D', colorbar=True, size=10, tools=['tap'] ) points_h = hv.Points(data_h, kdims=['x', 'y'], vdims=['value', 'id', 'type']).opts( marker='square', color='value', cmap='viridis', label='Type H', size=10, tools=['tap'] ) scatter_plot = points_d * points_h # 2. 创建双流并关联DynamicMap sel_d = hv.streams.Selection1D(source=points_d) sel_h = hv.streams.Selection1D(source=points_h) def timeseries_callback(index_d, index_h): # 判断哪类数据被选中 if index_d: selected_id = data_d.iloc[index_d[0]]['id'] elif index_h: selected_id = data_h.iloc[index_h[0]]['id'] else: return hv.Curve([]).opts(title='Select a point') ts_df = timeseries_data[selected_id] return hv.Curve(ts_df, kdims='time', vdims='val').opts( title=f'Timeseries for ID: {selected_id}', xlabel='Time', ylabel='Value', width=600 ) dynamic_ts = hv.DynamicMap(timeseries_callback, streams=[sel_d, sel_h]) # 3. 组合布局并优化样式 layout = (scatter_plot + dynamic_ts).cols(1) layout.opts( hv.opts.Points(legend_position='top_right'), hv.opts.Curve(toolbar='above') )
关键细节说明
- 图例生成:每个
Points对象通过label参数设置图例名称,叠加后Holoviews自动合并图例,支持点击图例切换显示/隐藏对应类别。 - 双流监听:回调函数的参数顺序必须和
streams列表的顺序一致,分别对应两个流的选中索引数组。当无选中点时返回空图,避免报错。 - 交互工具:必须为每个
Points添加tap工具,否则点击不会触发Selection1D流的更新。
内容的提问来源于stack exchange,提问作者hm8
相关产品推荐
相关产品推荐

