如何用Plotly Express绘制带独立时间滑块的并排Choropleth地图
实现并排带独立时间滑块的Plotly Choropleth地图
需求说明
- 两张Choropleth地图并排展示
- 每张地图配备独立的时间滑块(支持动画切换)
- 仅保留一个共享的侧边颜色条
- 已提供示例数据及基础绘图函数,但无法完成目标布局
解决方案
由于Plotly Express的分面功能不支持动画,需通过Plotly原生Figure对象手动组合子图并配置动画:
核心步骤
- 合并数据集并添加分组标识:将2019和2020的数据合并,新增
year字段区分两个子图的数据源 - 创建子图框架:使用
make_subplots生成2列的地理子图布局 - 添加动画图层:为每个年份的数据集创建独立的动画帧,分别添加到对应子图
- 共享颜色条:隐藏其中一个子图的颜色条,调整另一个的位置实现共享
- 调整动画控件:配置两个时间滑块的位置,避免重叠并对应各自子图
- 美化布局:移除边框、海岸线,调整边距和整体尺寸
完整实现代码
import pandas as pd import plotly.express as px from plotly.subplots import make_subplots import plotly.graph_objects as go # 合并数据并添加年份标识 data_2019 = { 'region': ['Germany', 'Denmark', 'Japan'], 'date': ['2019-01-01', '2019-02-01', '2019-02-01'], 'valence': [0.3, 0.21, 0.1], 'year': ['2019']*3 } data_2020 = { 'region': ['Germany', 'Denmark', 'Japan'], 'date': ['2020-01-01', '2020-02-01', '2020-02-01'], 'valence': [0.94, 0.71, 0.81], 'year': ['2020']*3 } df = pd.concat([pd.DataFrame(data_2019), pd.DataFrame(data_2020)], ignore_index=True) # 创建2列子图布局 fig = make_subplots( rows=1, cols=2, specs=[[{"type": "choropleth"}, {"type": "choropleth"}]], subplot_titles=("2019 Data", "2020 Data") ) # 创建动画帧 frames = [] dates_2019 = sorted(df[df['year']=='2019']['date'].unique()) dates_2020 = sorted(df[df['year']=='2020']['date'].unique()) # 为每个日期组合创建动画帧 for i, (date19, date20) in enumerate(zip(dates_2019, dates_2020)): frame = go.Frame( data=[ # 2019子图对应帧 go.Choropleth( locations=df[(df['year']=='2019') & (df['date']==date19)]['region'], locationmode='country names', z=df[(df['year']=='2019') & (df['date']==date19)]['valence'], hovertext=df[(df['year']=='2019') & (df['date']==date19)]['valence'], marker_line_width=0, coloraxis='coloraxis' ), # 2020子图对应帧 go.Choropleth( locations=df[(df['year']=='2020') & (df['date']==date20)]['region'], locationmode='country names', z=df[(df['year']=='2020') & (df['date']==date20)]['valence'], hovertext=df[(df['year']=='2020') & (df['date']==date20)]['valence'], marker_line_width=0, coloraxis='coloraxis' ) ], name=f"frame{i}" ) frames.append(frame) # 更新布局和动画配置 fig.update_layout( updatemenus=[ # 2019的播放/暂停按钮 dict( type="buttons", showactive=False, buttons=[dict(label="Play", method="animate", args=[None, {"frame": {"duration": 1000, "redraw": True}, "fromcurrent": True}]), dict(label="Pause", method="animate", args=[[None], {"frame": {"duration": 0, "redraw": True}, "mode": "immediate"}])], x=0.1, y=-0.15 ), # 2020的播放/暂停按钮 dict( type="buttons", showactive=False, buttons=[dict(label="Play", method="animate", args=[None, {"frame": {"duration": 1000, "redraw": True}, "fromcurrent": True}]), dict(label="Pause", method="animate", args=[[None], {"frame": {"duration": 0, "redraw": True}, "mode": "immediate"}])], x=0.9, y=-0.15 ) ], sliders=[ # 2019的时间滑块 dict( active=0, steps=[dict(method="animate", args=[[f"frame{i}"], {"frame": {"duration": 0, "redraw": True}, "mode": "immediate"}], label=date) for i, date in enumerate(dates_2019)], x=0.05, y=-0.2, len=0.4 ), # 2020的时间滑块 dict( active=0, steps=[dict(method="animate", args=[[f"frame{i}"], {"frame": {"duration": 0, "redraw": True}, "mode": "immediate"}], label=date) for i, date in enumerate(dates_2020)], x=0.55, y=-0.2, len=0.4 ) ], coloraxis=dict( colorscale=px.colors.sequential.Aggrnyl, range=(0,1), colorbar=dict( x=1.05, y=0.5, orientation='v' ) ), showlegend=False, margin=dict(l=0, r=50, b=100, t=30), width=1200, height=600 ) # 配置子图地理样式 fig.update_geos( showframe=False, showcoastlines=False, projection_type='natural earth', row=1, col=1 ) fig.update_geos( showframe=False, showcoastlines=False, projection_type='natural earth', row=1, col=2 ) # 绑定动画帧 fig.frames = frames fig.show()
关键说明
- 两个子图共享同一颜色轴,确保颜色刻度统一
- 每张地图拥有独立的播放/暂停按钮和时间滑块,可单独控制动画进度
- 调整控件位置避免布局重叠,优化交互体验
- 移除冗余边框和海岸线,简化视觉呈现
内容的提问来源于stack exchange,提问作者phoebe_albeduddel
相关产品推荐
相关产品推荐

