如何在Dash中通过回调实现联动更新的双列并排子图
问题根因
- 直接在回调返回的data数组中新增trace时,所有轨迹默认绑定到全局的第一组x/y坐标轴,不会自动拆分到独立子图,因此会出现全部轨迹堆叠在同一坐标系的效果
- 即便引入
make_subplots,如果没有通过显式的行列参数将trace绑定到对应子图,或者手动设置trace的坐标轴指向(如xaxis='x2'、yaxis='y2'),依然无法实现分栏渲染的效果
实现方案
- 用
make_subplots初始化1行2列的独立子图画布 - 遍历选中ID的分组数据时,对每一组数据分别生成两类轨迹:
x_val与y_val的散点图绑定到第一列子图,x_val与rand的散点图绑定到第二列子图 - 统一更新布局参数,匹配坐标轴标题、画布尺寸要求
补充:如果不使用
make_subplots工具,手动实现双坐标系分栏的话,需要在trace中显式指定坐标轴绑定关系(如xaxis='x2'、yaxis='y2'),同时在layout中定义第二组坐标轴的位置、标题参数,这种方式手动维护成本较高,容易出现绑定错误,优先推荐用make_subplots实现子图布局。
可直接运行的完整代码
import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output import pandas as pd from plotly.subplots import make_subplots import plotly.graph_objects as go # 初始化数据集 df = pd.DataFrame( [['A', 1, 7, 10], ['A', 2, 6, 15], ['A', 3, 14, 20], ['B', 1, 3, 2], ['B', 2, 9, 6], ['B', 3, 2, 8], ['C', 1, 1, 90], ['C', 2, 5, 40], ['C', 3, 11, 23]], columns=['ID', 'x_val', 'y_val', 'rand'] ) app = dash.Dash() app.layout = html.Div([ dcc.Dropdown( id='my-dropdown', options=[{'label': i, 'value': i} for i in df.ID.unique()], multi=True, placeholder='Select id', value=[df.ID.min()] ), dcc.Graph(id='my-graph') ], style={'width': '800px'}) # 调整容器宽度匹配子图画布尺寸 @app.callback(Output('my-graph', 'figure'), [Input('my-dropdown', 'value')]) def update_graph(selected_dropdown_value): # 按选中ID筛选并分组数据 filtered_df = df[df['ID'].isin(selected_dropdown_value)] grouped_df = filtered_df.groupby(by=['ID']) # 初始化1行2列的子图画布,附带子图标题 fig = make_subplots( rows=1, cols=2, subplot_titles=("y_val 随x_val变化", "rand 随x_val变化") ) for ID, group in grouped_df: # 添加第一列子图的轨迹:x_val vs y_val fig.add_trace( go.Scatter( x=group['x_val'], y=group['y_val'], name=f"{ID} - y_val" ), row=1, col=1 ) # 添加第二列子图的轨迹:x_val vs rand fig.add_trace( go.Scatter( x=group['x_val'], y=group['rand'], name=f"{ID} - rand" ), row=1, col=2 ) # 统一更新布局参数 fig.update_layout( height=600, width=800, title_text="Side By Side Subplots" ) # 设置坐标轴标题 fig.update_xaxes(title_text="X-title", row=1, col=1) fig.update_yaxes(title_text="Y-title", row=1, col=1) fig.update_yaxes(title_text="Rand值", row=1, col=2) fig.update_xaxes(title_text="X-title", row=1, col=2) return fig if __name__ == '__main__': app.run_server()
效果验证
- 下拉框选择任意多个ID后,回调会自动将对应数据拆分渲染到两个并排子图中:左侧子图展示
y_val随x_val的变化趋势,右侧子图展示rand值随x_val的变化趋势 - 每个ID对应的两个子图轨迹会通过图例标识,不会出现全部轨迹堆叠在同一坐标系的问题
内容的提问来源于stack exchange,提问作者user1294510
相关产品推荐
相关产品推荐

