如何使用ipywidgets+Plotly实现无全局变量可嵌入HBox的交互式散点图
解决方案
调整要点
- 补全了
FloatRangeSlider的事件监听,滑块值变更时同步更新色条显示范围 - 切换颜色字段的回调中新增滑块范围同步逻辑,同时更新色条的全局取值范围和悬停提示标签
- 移除全局变量依赖,所有数据和组件逻辑在同一上下文内运行
- 所有数据变更场景(筛选ID、切换字段、调整色条范围)都同步更新对应图表属性,保证交互一致性
完整可运行代码
import pandas as pd import plotly.express as px import plotly.graph_objects as go import ipywidgets as widgets # 初始化测试数据 big_grid=pd.DataFrame(data=dict( id_col=[1,2,3,4,5], col1=[0.1,0.2,0.3,0.4,0.5], col2=[10,20,30,40,50], coord1=[6,7,8,9,10], coord2=[6,7,8,9,10] )) list_elem=["col1","col2"] list_id=big_grid.id_col.values # 初始化交互组件 dropm_elem=widgets.Dropdown(options=list(list_elem)) dropm_id=widgets.SelectMultiple(options=list_id,description="Active",disabled=False, value=list_id) # 默认全选ID rangewidg=widgets.FloatRangeSlider( value=[big_grid[dropm_elem.value].min(),big_grid[dropm_elem.value].max()], min=big_grid[dropm_elem.value].min(), max=big_grid[dropm_elem.value].max(), step=0.001, readout_format='.3f', description="Color Range", continuous_update=False ) # 初始化FigureWidget fig = go.FigureWidget( data=px.scatter( big_grid, x="coord1", y="coord2", color=dropm_elem.value, # 直接传字段名,自动关联hover属性 color_continuous_scale="Turbo", ) ) # ID筛选变更回调 def handle_id_change(change): filtered = big_grid[big_grid['id_col'].isin(dropm_id.value)] with fig.batch_update(): fig.data[0]['x'] = filtered["coord1"] fig.data[0]['y'] = filtered["coord2"] fig.data[0]['marker']['color'] = filtered[dropm_elem.value] fig.data[0]['marker']['cmin'] = rangewidg.value[0] fig.data[0]['marker']['cmax'] = rangewidg.value[1] # 同步更新悬停提示 fig.data[0]['customdata'] = filtered[[dropm_elem.value, 'id_col']] fig.data[0]['hovertemplate'] = f'coord1: %{{x}}<br>coord2: %{{y}}<br>id_col: %{{customdata[1]}}<br>{dropm_elem.value}: %{{customdata[0]}}<extra></extra>' # 颜色字段切换回调 def handle_elem_change(change): selected_col = dropm_elem.value # 同步更新滑块范围 with rangewidg.hold_trait_notifications(): rangewidg.min = big_grid[selected_col].min() rangewidg.max = big_grid[selected_col].max() rangewidg.value = [big_grid[selected_col].min(), big_grid[selected_col].max()] filtered = big_grid[big_grid['id_col'].isin(dropm_id.value)] with fig.batch_update(): fig.data[0]['marker']['color'] = filtered[selected_col] fig.data[0]['marker']['cmin'] = rangewidg.value[0] fig.data[0]['marker']['cmax'] = rangewidg.value[1] fig.layout.coloraxis.colorbar.title = selected_col # 同步更新悬停提示 fig.data[0]['customdata'] = filtered[[selected_col, 'id_col']] fig.data[0]['hovertemplate'] = f'coord1: %{{x}}<br>coord2: %{{y}}<br>id_col: %{{customdata[1]}}<br>{selected_col}: %{{customdata[0]}}<extra></extra>' # 色条范围滑块变更回调 def handle_range_change(change): cmin, cmax = rangewidg.value with fig.batch_update(): fig.data[0]['marker']['cmin'] = cmin fig.data[0]['marker']['cmax'] = cmax # 绑定事件监听 dropm_elem.observe(handle_elem_change, names='value') dropm_id.observe(handle_id_change, names='value') rangewidg.observe(handle_range_change, names='value') # 组装布局,输出的box为标准Widget组件可直接嵌套到其他布局 right_box1 = widgets.HBox([fig]) right_box2 = widgets.VBox([dropm_elem, dropm_id, rangewidg]) box = widgets.HBox([right_box1, right_box2]) box
内容的提问来源于stack exchange,提问作者user3458909
相关产品推荐
相关产品推荐

