如何实现Dash中line_mapbox与时间序列图的双向交叉筛选
实现步骤
核心通过Dash的回调上下文判断交互触发源,同时绑定数据字段实现双向过滤,完整可运行代码如下:
import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output from dash import callback_context import numpy as np import pandas as pd import plotly.express as px import plotly.graph_objs as go app = dash.Dash() server = app.server np.random.seed(0) # 随机虚拟数据 n = 100 time = np.linspace(0, 1, n) latitude = 50 + 0.001 * np.cumsum(np.random.randn(n)) longitude = 2 + 0.001 * np.cumsum(np.random.randn(n)) altitude = (time - 0.5) ** 2 heartrate = 100 + np.cumsum(np.random.randn(n)) df = pd.DataFrame( { "time": time, "latitude": latitude, "longitude": longitude, "altitude": altitude, "heartrate": heartrate, } ) # 初始地图配置,绑定time为自定义数据,开启选择模式 init_map_fig = px.line_mapbox(df, lat="latitude", lon="longitude", zoom=12, height=800, custom_data=['time']) init_map_fig.update_layout(mapbox_style="stamen-terrain", clickmode='event+select', uirevision='constant') app.layout = html.Div( [ html.Div( [ dcc.Graph(id="mymap", figure=init_map_fig), ] ), html.Div( [ dcc.Graph(id="time-series"), dcc.Dropdown( id="column", options=[ {"label": i, "value": i} for i in ["altitude", "heartrate"] ], value="altitude", ), ] ), ] ) @app.callback( [Output("mymap", "figure"), Output("time-series", "figure")], [Input("column", "value"), Input("time-series", "relayoutData"), Input("mymap", "selectedData")], prevent_initial_call=False ) def update_both_charts(column, relayout_data, selected_data): filtered_df = df.copy() trigger_id = callback_context.triggered[0]['prop_id'] if callback_context.triggered else '' # 处理时间序列图刷选y轴区间的情况 if trigger_id == 'time-series.relayoutData': if relayout_data and 'yaxis.range[0]' in relayout_data and 'yaxis.range[1]' in relayout_data: y_min = relayout_data['yaxis.range[0]'] y_max = relayout_data['yaxis.range[1]'] filtered_df = df[(df[column] >= y_min) & (df[column] <= y_max)] # 处理地图选点的情况 elif trigger_id == 'mymap.selectedData': if selected_data and 'points' in selected_data and len(selected_data['points'])>0: time_list = [p['customdata'][0] for p in selected_data['points']] t_min, t_max = min(time_list), max(time_list) filtered_df = df[(df['time'] >= t_min) & (df['time'] <= t_max)] # 生成更新后的地图 map_fig = px.line_mapbox(filtered_df, lat="latitude", lon="longitude", custom_data=['time']) map_fig.update_layout(mapbox_style="stamen-terrain", clickmode='event+select', uirevision='constant', height=800) # 生成更新后的时间序列图,开启刷选模式 ts_fig = { "data": [go.Scatter(x=filtered_df['time'], y=filtered_df[column], mode='lines')], "layout": go.Layout(dragmode='select') } return map_fig, ts_fig app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"}) if __name__ == "__main__": app.run_server(debug=True)
使用说明
- 时间序列图:按住鼠标左键框选y轴区间,地图会自动展示对应数据点的运动轨迹
- 地图:按住shift+鼠标左键框选轨迹点,时间序列图会自动展示选中点对应时间区间的海拔/心率数据
- 双击任意图表可重置选择状态,恢复展示全量数据
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

