You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Plotly如何实现带维度切换的Scattermapbox年份动画散点图

问题根因

你现有代码只添加了维度切换按钮和空的播放按钮,既没有按Anno年份字段拆分数据集生成对应动画帧,也没有给播放按钮绑定帧数据和播放规则,自然无法实现按年份递进的动画效果。

实现方案

核心逻辑是先按年份切分数据,给每个年份生成独立的动画帧,每一帧同步包含I、M、F三个维度当年的点位和大小配置,保证维度切换和时间动画互不冲突。

完整可运行代码

import plotly.graph_objects as go
import pandas as pd

# 先按升序提取所有年份作为时间轴
years = sorted(df_long['Anno'].unique())

# 保留原有的缩放逻辑,用全量数据最大值计算系数,保证不同年份点大小尺度统一
scale_I = 2.*max(df_long["I"])/(8.**2)
scale_M = 2.*max(df_long["M"])/(8.**2)
scale_F = 2.*max(df_long["F"])/(8.**2)

# 初始化画布,默认加载第一年数据,初始展示I维度
first_year_data = df_long[df_long['Anno'] == years[0]]
fig = go.Figure()
fig.add_trace(go.Scattermapbox(
    lat=first_year_data["Y"], 
    lon=first_year_data["X"], 
    mode="markers", 
    marker_size=first_year_data["I"]/scale_I,
    name='I'
))
fig.add_trace(go.Scattermapbox(
    lat=first_year_data["Y"], 
    lon=first_year_data["X"], 
    mode="markers", 
    marker_size=first_year_data["M"]/scale_M, 
    visible=False,
    name='M'
))
fig.add_trace(go.Scattermapbox(
    lat=first_year_data["Y"], 
    lon=first_year_data["X"], 
    mode="markers", 
    marker_size=first_year_data["F"]/scale_F, 
    visible=False,
    name='F'
))

# 生成所有年份对应的动画帧
frames = []
for year in years:
    year_data = df_long[df_long['Anno'] == year]
    frames.append(go.Frame(
        data=[
            go.Scattermapbox(lat=year_data["Y"], lon=year_data["X"], marker_size=year_data["I"]/scale_I),
            go.Scattermapbox(lat=year_data["Y"], lon=year_data["X"], marker_size=year_data["M"]/scale_M),
            go.Scattermapbox(lat=year_data["Y"], lon=year_data["X"], marker_size=year_data["F"]/scale_F)
        ],
        name=str(year)
    ))
fig.frames = frames

# 更新布局,保留维度切换,补全播放逻辑和年份滑块
fig.update_layout(
    title="Analysis",
    font_size=18,
    mapbox_style="open-street-map", 
    mapbox_center_lon=0, 
    mapbox_center_lat=0, 
    mapbox_zoom=2,
    updatemenus=[
        # I/M/F维度切换下拉菜单
        dict(
            direction = "down",
            buttons=list([
                dict(args=[{"visible":[True, False, False]}], label="I", method="update"),
                dict(args=[{"visible":[False, True, False]}], label="M", method="update"),
                dict(args=[{"visible":[False, False, True]}], label="F", method="update")
            ]),
            pad={"r": 0, "t": -10},
            showactive=True,
            x=0.1,
            xanchor="left",
            y=1.1,
            yanchor="top"
        ),
        # 播放/暂停控制按钮
        dict(
            type = "buttons",
            buttons=list([
                dict(
                    label="Play",
                    method="animate",
                    args=[None, {"frame": {"duration": 500, "redraw": True}, "fromcurrent": True, "transition": {"duration": 300}}]
                ),
                dict(
                    label="Pause",
                    method="animate",
                    args=[[None], {"frame": {"duration": 0, "redraw": False}, "mode": "immediate", "transition": {"duration": 0}}]
                )
            ]),
            pad={"r": 0, "t": -50},
            showactive=True,
            x=0.1,
            xanchor="left",
            y=0.05,
            yanchor="bottom"
        ),
    ],
    # 年份选择滑动条,支持手动跳转指定年份
    sliders=[dict(
        active=0,
        steps=[
            dict(
                method="animate",
                args=[[str(year)], {"frame": {"duration": 300, "redraw": True}, "mode": "immediate"}],
                label=str(year)
            ) for year in years
        ],
        x=0.1,
        y=0,
        pad={"b": 10, "t": 50},
        xanchor="left",
        yanchor="top"
    )]
)

fig.show()

参数调整说明

  • 动画播放速度可以修改frame里的duration参数,单位为毫秒,数值越小播放越快
  • 点的大小缩放比例可以调整你原有scale_I/scale_M/scale_F的计算系数
  • 不需要暂停按钮可以直接删掉对应按钮配置,不影响播放功能
  • 如果不需要手动选年份的滑动条,删掉sliders配置块即可

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:21:19