如何在Dash中复用现有数据切换图表类型?求优雅实现方案
问题描述
在Plotly中,可通过如下代码借助updatemenus切换图表类型并复用现有数据:
fig.update_layout( updatemenus=[ dict( buttons=list([ dict( args=["type", "surface"], label="3D Surface", method="restyle" ), dict( args=["type", "heatmap"], label="Heatmap", method="restyle" ) ]), direction="down", pad={"r": 10, "t": 10}, showactive=True, x=0.1, xanchor="left", y=1.1, yanchor="top" ), ] )
但在Dash的文档及示例中未找到类似功能的实现方法。目前通过重复从PostgreSQL查询已有数据的方式实现需求,仅切换图表类型,其余属性均一致,代码如下:
@app.callback( dash.dependencies.Output('main-plot', 'figure'), dash.dependencies.Input('x_dropdown', 'value'), dash.dependencies.Input('y_dropdown', 'value'), dash.dependencies.Input('z_dropdown', 'value'), dash.dependencies.Input('dimension', 'value'), prevent_initial_call=True ) def update_figure(x, y, z, dimension): if dimension == "2D": return update_2d_figure(x, y, z) else: return update_3d_figure(x, y, z) def update_2d_figure(x, y, z): fig = px.scatter( data_frame=get_df(select_xyz([x, y, z])), x='x', y='y', color='z', hover_data=[ 'name', 'name', 'nation', 'nation', ] ) fig.update_layout( width=900, height=600, autosize=False, margin=dict(t=0, b=0, l=0, r=0), template="plotly_white", ) fig.update_traces(marker_size=5) fig.update_scenes( aspectratio=dict(x=0.9, y=0.9, z=0.7), aspectmode="manual" ) return fig def update_3d_figure(x, y, z): fig = px.scatter_3d( data_frame=get_df(select_xyz([x, y, z])), x='x', y='y', z='z', color='z', hover_data=[ 'name', 'name', 'nation', 'nation', ] ) fig.update_layout( width=900, height=600, autosize=False, margin=dict(t=0, b=0, l=0, r=0), template="plotly_white", ) fig.update_traces(marker_size=5) fig.update_scenes( aspectratio=dict(x=0.9, y=0.9, z=0.7), aspectmode="manual" ) return fig
请问是否存在更优雅的实现方式?
解决方案
方法一:使用Plotly原生updatemenus在前端切换
直接在Dash生成的Figure中添加切换按钮,数据仅查询一次,无需触发Dash回调,前端完成交互:
@app.callback( dash.dependencies.Output('main-plot', 'figure'), dash.dependencies.Input('x_dropdown', 'value'), dash.dependencies.Input('y_dropdown', 'value'), dash.dependencies.Input('z_dropdown', 'value'), prevent_initial_call=True ) def update_figure(x, y, z): # 仅查询一次数据 df = get_df(select_xyz([x, y, z])) # 创建初始3D散点图 fig = px.scatter_3d( data_frame=df, x='x', y='y', z='z', color='z', hover_data=['name', 'nation'] ) # 添加2D散点图轨迹 fig.add_trace(px.scatter( data_frame=df, x='x', y='y', color='z', hover_data=['name', 'nation'] ).data[0]) # 默认隐藏2D轨迹 fig.data[1].visible = False # 添加切换按钮 fig.update_layout( updatemenus=[ dict( buttons=[ dict( args=[{"visible": [True, False]}], label="3D 散点图", method="restyle" ), dict( args=[{"visible": [False, True]}], label="2D 散点图", method="restyle" ) ], direction="down", pad={"r": 10, "t": 10}, showactive=True, x=0.1, xanchor="left", y=1.1, yanchor="top" ) ], width=900, height=600, autosize=False, margin=dict(t=0, b=0, l=0, r=0), template="plotly_white", ) fig.update_traces(marker_size=5) fig.update_scenes( aspectratio=dict(x=0.9, y=0.9, z=0.7), aspectmode="manual" ) return fig
方法二:优化回调逻辑,复用查询数据
若偏好通过Dash输入组件(如dimension下拉框)切换,可统一查询数据后再生成对应图表,避免重复数据库请求:
@app.callback( dash.dependencies.Output('main-plot', 'figure'), dash.dependencies.Input('x_dropdown', 'value'), dash.dependencies.Input('y_dropdown', 'value'), dash.dependencies.Input('z_dropdown', 'value'), dash.dependencies.Input('dimension', 'value'), prevent_initial_call=True ) def update_figure(x, y, z, dimension): # 仅查询一次数据 df = get_df(select_xyz([x, y, z])) # 抽离通用配置,避免代码冗余 common_layout = { "width": 900, "height": 600, "autosize": False, "margin": dict(t=0, b=0, l=0, r=0), "template": "plotly_white" } common_trace_config = {"marker_size": 5} common_scene_config = { "aspectratio": dict(x=0.9, y=0.9, z=0.7), "aspectmode": "manual" } # 根据维度选择生成对应图表 if dimension == "2D": fig = px.scatter( data_frame=df, x='x', y='y', color='z', hover_data=['name', 'nation'] ) else: fig = px.scatter_3d( data_frame=df, x='x', y='y', z='z', color='z', hover_data=['name', 'nation'] ) # 应用通用配置 fig.update_layout(**common_layout) fig.update_traces(**common_trace_config) fig.update_scenes(**common_scene_config) return fig
内容的提问来源于stack exchange,提问作者am1234
相关产品推荐
相关产品推荐

