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

如何在Python Plotly动画散点图中添加动态水平线?

问题:为Plotly动画散点图添加动态加权平均水平线及标注

原始动画散点图代码:

import plotly.express as px
df = px.data.gapminder()
fig = px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country",
           size="pop", color="continent", hover_name="country",
           log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])
fig.show()

需求:为每个年份的动画帧添加一条动态水平线,代表该年份的加权平均预期寿命,并在水平线旁标注对应数值。已通过以下代码生成加权平均列表:

def weighted_average(df, values, weights):
    # source : https://datagy.io/pandas-weighted-average/
    return sum(df[weights] * df[values]) / df[weights].sum()
# 保留年份索引,方便后续匹配标注文本
weighted_life_exp = df.groupby('year').apply(weighted_average, 'lifeExp', 'pop')
L_weighted_average_life_exp = weighted_life_exp.to_list()

尝试遍历帧添加水平线时触发错误:'Frame' object has no attribute 'add_hline',原错误代码:

for y,frame in zip(L_weighted_average_life_exp,fig.frames):
   frame.add_hline(y=y, line_width=1, line_dash="dash", line_color="black")

解决方案

Plotly的Frame对象不支持add_hline方法,需要直接操作帧的layout属性,通过添加shape实现水平线,添加annotation实现数值标注。

完整实现代码

import plotly.express as px

def weighted_average(df, values, weights):
    return sum(df[weights] * df[values]) / df[weights].sum()

# 加载数据并计算加权平均预期寿命(保留年份索引)
df = px.data.gapminder()
weighted_life_exp = df.groupby('year').apply(weighted_average, 'lifeExp', 'pop')
year_list = weighted_life_exp.index.to_list()
avg_list = weighted_life_exp.to_list()

# 创建基础动画散点图
fig = px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country",
           size="pop", color="continent", hover_name="country",
           log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])

# 为初始视图添加第一条水平线和标注
initial_avg = avg_list[0]
fig.add_hline(y=initial_avg, line_width=1, line_dash="dash", line_color="black")
fig.add_annotation(
    x=100000,  # 标注位置选在x轴右侧
    y=initial_avg,
    text=f"加权平均: {initial_avg:.1f}",
    showarrow=False,
    yshift=10,
    font={"color": "black"}
)

# 遍历每个帧,更新水平线和标注
for frame, avg in zip(fig.frames, avg_list):
    # 添加水平线shape
    frame.layout.shapes = [dict(
        type="line",
        y0=avg, y1=avg,
        x0=0, x1=1,
        xref="paper", yref="y",
        line_width=1, line_dash="dash", line_color="black"
    )]
    # 添加数值标注
    frame.layout.annotations = [dict(
        x=1, xref="paper",
        y=avg, yref="y",
        text=f"加权平均: {avg:.1f}",
        showarrow=False,
        yshift=10,
        font={"color": "black"}
    )]

fig.show()

关键说明

  1. 用frame.layout.shapes定义水平线:xref="paper"表示x轴范围占整个画布(0到1),确保水平线贯穿整个图表宽度。
  2. 用frame.layout.annotations添加标注:将标注固定在x轴右侧(x=1),跟随水平线的y值动态变化,保留一位小数让数值更清晰。
  3. 初始视图需要单独添加水平线和标注,因为动画的初始状态不会自动加载第一个帧的layout配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:25:25