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

Plotly.express:如何让轰炸点动画保留已生成点不消失?

问题:Plotly实现轰炸点随时间逐步保留的地图动画

我正在可视化越南战争轰炸区域的数据,数据格式如下:

DATE       |   TARGET_LATITUDE    |  TARGET_LONGTITUDE
1/10/1969  |    10.469393         |  106.019629
1/10/1969  |    11.525273         |  107.06769
1/10/1969  |    10.973675         |  106.019629
...
1/10/1970  |    11.469921         |  105.918000 
1/10/1970  |    12.589493         |  103.019346
...
1/10/1972  |    10.464893         |  103.013245
...

需求是:用Plotly实现轰炸点随时间逐步出现,且已出现的点始终保留在地图上,不会消失。

我尝试使用scatter_mapbox的animation_frame参数,但结果未达预期,代码如下:

import pandas as pd
import plotly.express as px
data = pd.read_csv("./data/THOR_Vietnam_Bombing_Operations.csv", 
                   encoding="latin-1", 
                   usecols=["DATE", "TARGET_LATITUDE", "TARGET_LONGTITUDE", "AIRCRAFT_ORIGINAL"])
data = data.sort_values(["MSNDATE"])
# Use first 2000 data: 1966 - 1970
data = data.iloc[:2000]
fig = px.scatter_mapbox(data, lat="TARGET_LATITUDE", 
                        lon="TARGET_LONGTITUDE", 
                        animation_frame="DATE",
                        animation_group="TARGET_LONGTITUDE", 
                        color_continuous_scale=px.colors.sequential.Hot, 
                        color_discrete_sequence=["red"])
fig.update_layout(mapbox_style="carto-positron")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.update_layout(autosize=False, width=600, height=750)
fig.show()

当前效果:
current_result

预期效果示例:
expected_result

当前动画仅显示对应时间帧的轰炸点,之前出现的点会消失,求解决方法实现已生成点持续显示的效果。


解决方案

Plotly Express的animation_frame默认是单帧独立显示数据,要实现累积显示,需先预处理数据,为每个时间帧生成包含当前及之前所有时间点的数据,再用Plotly Graph Objects手动构建动画。

步骤1:数据预处理

  1. 将DATE列转换为日期格式,确保时间排序正确
  2. 按日期分组,生成每个日期对应的累积数据集
  3. 为每个累积数据集标记对应的动画帧标签

代码示例:

import pandas as pd
import plotly.graph_objects as go

# 读取并预处理原始数据
data = pd.read_csv("./data/THOR_Vietnam_Bombing_Operations.csv", 
                   encoding="latin-1", 
                   usecols=["DATE", "TARGET_LATITUDE", "TARGET_LONGTITUDE"])

# 转换日期格式并排序
data["DATE"] = pd.to_datetime(data["DATE"], format="%m/%d/%Y")
data = data.sort_values("DATE").iloc[:2000]  # 取前2000条数据

# 生成累积帧数据
frames_data = []
unique_dates = data["DATE"].unique()

for date in unique_dates:
    # 取当前日期及之前的所有数据
    cumulative_data = data[data["DATE"] <= date]
    # 添加帧标识
    cumulative_data["frame_date"] = date.strftime("%m/%d/%Y")
    frames_data.append(cumulative_data)

# 合并所有帧数据
animation_data = pd.concat(frames_data)

步骤2:构建累积动画

用go.Figure()手动创建地图和动画帧,每个帧对应累积到当前日期的所有轰炸点:

# 创建基础地图(第一帧数据)
fig = go.Figure(
    data=[go.Scattermapbox(
        lat=animation_data[animation_data["frame_date"] == unique_dates[0].strftime("%m/%d/%Y")]["TARGET_LATITUDE"],
        lon=animation_data[animation_data["frame_date"] == unique_dates[0].strftime("%m/%d/%Y")]["TARGET_LONGTITUDE"],
        mode="markers",
        marker=dict(color="red", size=5)
    )],
    layout=go.Layout(
        mapbox_style="carto-positron",
        margin={"r":0,"t":0,"l":0,"b":0},
        autosize=False,
        width=600,
        height=750,
        updatemenus=[dict(
            type="buttons",
            showactive=False,
            buttons=[dict(label="Play",
                          method="animate",
                          args=[None, dict(frame=dict(duration=300, redraw=True), fromcurrent=True)])]
        )]
    ),
    # 生成所有动画帧
    frames=[go.Frame(
        data=[go.Scattermapbox(
            lat=animation_data[animation_data["frame_date"] == date.strftime("%m/%d/%Y")]["TARGET_LATITUDE"],
            lon=animation_data[animation_data["frame_date"] == date.strftime("%m/%d/%Y")]["TARGET_LONGTITUDE"],
            mode="markers",
            marker=dict(color="red", size=5)
        )],
        name=date.strftime("%m/%d/%Y")
    ) for date in unique_dates]
)

# 添加滑块控件
fig.update_layout(
    sliders=[dict(
        active=0,
        currentvalue={"prefix": "日期: "},
        pad={"t": 50},
        steps=[dict(
            method="animate",
            args=[[date.strftime("%m/%d/%Y")], dict(mode="immediate", frame=dict(duration=300, redraw=True))],
            label=date.strftime("%m/%d/%Y")
        ) for date in unique_dates]
    )]
)

fig.show()

关键说明

  • 预处理时生成的animation_data包含每个时间点的累积数据,确保每帧都显示之前所有的轰炸点
  • 使用Plotly Graph Objects而非Express,能更灵活地控制每个动画帧的内容
  • 调整frame的duration参数可以控制动画播放速度

内容的提问来源于stack exchange,提问作者Hoang D Nam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:15:53