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

如何将Plotly的px.scatter动画散点图改写为go.Scatter()实现

问题说明

我现有一段使用NFL追踪数据(下方提供样例数据)创建动画图表的代码(已附上对应效果图),想要咨询如何将代码中的px.scatter()部分改写为使用graph object类型的go.Scatter()来实现相同效果?

原实现代码如下:

fig = px.scatter(data,  x = "x", y = "y", hover_name="displayName",
          color="team", animation_frame = "frameId", animation_group = "displayName",
          range_y = [-5, 60], range_x = [-5, 125], 
          color_discrete_map = dict((["home", home], ["away", away], ["football", football])))

对应效果图:
NFL追踪动画散点效果图

样例数据结构

xyteamframeIddisplayName
61.2146.77home1Mahomes
6032away1Beckham

实现方案

px.scatter生成动画的本质是自动按animation_frame拆分数据帧、按分组生成轨迹,用go.Scatter手动实现只要对应拆解这两个步骤,就能得到完全一致的效果:

  1. 初始化画布,提前设置固定的坐标轴范围
  2. 按队伍分组生成第一帧(初始状态)的散点轨迹,匹配原有颜色映射规则
  3. 遍历所有frameId生成对应动画帧,每个帧下保持轨迹顺序一致,保证点和选手/队伍的绑定关系不错位
  4. 配置动画播放控件、滑块、悬浮提示规则,对齐px默认交互效果

完整可运行代码:

import plotly.graph_objects as go
import pandas as pd

# 沿用原代码定义的颜色映射规则
color_map = {
    "home": home,
    "away": away,
    "football": football
}
# 提前获取所有帧id、队伍分类
frame_ids = sorted(data["frameId"].unique())
team_list = list(color_map.keys())

# 初始化画布
fig = go.Figure()

# 添加初始帧(第一个frameId)的所有散点轨迹
first_frame = data[data["frameId"] == frame_ids[0]]
for team in team_list:
    team_subset = first_frame[first_frame["team"] == team]
    fig.add_trace(
        go.Scatter(
            x=team_subset["x"],
            y=team_subset["y"],
            mode="markers",
            marker=dict(color=color_map[team], size=12),
            name=team,
            # 对齐原代码hover_name效果,悬浮显示选手名
            text=team_subset["displayName"],
            hovertemplate="%{text}<extra></extra>"
        )
    )

# 生成全部动画帧
animation_frames = []
for fid in frame_ids:
    frame_subset = data[data["frameId"] == fid]
    frame_traces = []
    for team in team_list:
        team_data = frame_subset[frame_subset["team"] == team]
        frame_traces.append(
            go.Scatter(
                x=team_data["x"],
                y=team_data["y"],
                text=team_data["displayName"],
                hovertemplate="%{text}<extra></extra>"
            )
        )
    animation_frames.append(go.Frame(data=frame_traces, name=str(fid)))
fig.frames = animation_frames

# 配置布局、坐标轴、动画控件
fig.update_layout(
    xaxis=dict(range=[-5, 125], title="x"),
    yaxis=dict(range=[-5, 60], title="y"),
    # 添加和px默认一致的进度滑块
    sliders=[dict(
        active=0,
        steps=[
            dict(
                method="animate",
                args=[[str(fid)], dict(mode="immediate", frame=dict(duration=300, redraw=True))],
                label=str(fid)
            ) for fid in frame_ids
        ]
    )],
    # 添加播放按钮
    updatemenus=[dict(
        type="buttons",
        buttons=[
            dict(
                label="播放",
                method="animate",
                args=[None, dict(frame=dict(duration=300, redraw=True), fromcurrent=True)]
            )
        ]
    )]
)

fig.show()

关键逻辑对应说明

  • 原代码color="team"+color_discrete_map的分色效果:通过循环每个队伍单独添加go.Scatter轨迹、给每个轨迹指定映射表内对应颜色实现
  • 原代码animation_group="displayName"的点-选手绑定逻辑:逐帧按固定的队伍顺序更新轨迹数据,不会出现动画过程中点错位、跳变的问题
  • 原代码hover_name="displayName"的悬浮提示效果:通过给散点配置text字段+自定义hovertemplate实现,悬浮时优先展示选手名称
  • 原代码range_x/range_y的坐标范围限制:直接在布局配置中给x、y轴设置range参数即可,效果完全一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:12:30