如何在Jupyter Notebook中实现3D数据的交互式2D直方图(悬停触发1D直方图)
解决方案
要实现Jupyter Notebook中悬停2D分箱时联动显示对应par3的1D直方图,核心是利用matplotlib的交互式事件处理,同时注意必须使用交互式后端。以下是完整实现代码:
%matplotlib widget import numpy as np import matplotlib.pyplot as plt # 模拟3D参数数据 all_datas = np.random.rand(5, 10, 20) * 100 par1_bins = 5 par2_bins = 10 par3_bins = 20 # 创建双栏画布 fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(10, 4)) # 绘制par1-par2的2D汇总热力图 sum_over_par3 = np.sum(all_datas, axis=2).transpose() ax0.pcolor(sum_over_par3) ax0.set_xlabel('par1') ax0.set_ylabel('par2') ax0.set_title('par1 vs par2 (sum over par3)') # 初始化右侧1D直方图(空占位) ax1.hist([], bins=par3_bins) ax1.set_xlabel('par3') ax1.set_ylabel('Count') ax1.set_title('par3 distribution') # 定义鼠标悬停事件处理函数 def hover_update(event): # 仅响应ax0内的鼠标移动事件 if event.inaxes != ax0 or not event.xdata or not event.ydata: return # 将鼠标坐标转换为分箱索引 i_par = int(np.floor(event.xdata)) j_par = int(np.floor(event.ydata)) # 索引合法性校验,避免越界 if 0 <= i_par < par1_bins and 0 <= j_par < par2_bins: # 获取对应分箱的par3数据 target_data = all_datas[i_par, j_par, :] # 更新右侧直方图 ax1.clear() ax1.hist(target_data, bins=par3_bins) ax1.set_xlabel('par3') ax1.set_ylabel('Count') ax1.set_title(f'par1={i_par}, par2={j_par}') # 刷新画布生效 fig.canvas.draw_idle() # 绑定事件到画布 fig.canvas.mpl_connect('motion_notify_event', hover_update) plt.tight_layout() plt.show()
关键细节说明
- 交互式后端:开头的
%matplotlib widget是必须的——静态后端(如%matplotlib inline)无法响应鼠标事件,只有交互式后端才能触发回调函数。 - 坐标转索引:
pcolor的坐标轴范围默认对应分箱数量(比如par1有5个分箱,x轴范围是0~5),用np.floor()直接将鼠标坐标转为分箱索引即可。 - 事件过滤:回调函数先判断鼠标是否在ax0区域内,避免无效触发;同时做索引边界检查,防止数组越界错误。
- 画布刷新:更新直方图后调用
draw_idle(),确保画布实时刷新显示新内容。
内容的提问来源于stack exchange,提问作者Stef1611
相关产品推荐
相关产品推荐

