如何在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()
关键说明
- 用
frame.layout.shapes定义水平线:xref="paper"表示x轴范围占整个画布(0到1),确保水平线贯穿整个图表宽度。 - 用
frame.layout.annotations添加标注:将标注固定在x轴右侧(x=1),跟随水平线的y值动态变化,保留一位小数让数值更清晰。 - 初始视图需要单独添加水平线和标注,因为动画的初始状态不会自动加载第一个帧的layout配置。
内容的提问来源于stack exchange,提问作者Ewdlam
相关产品推荐
相关产品推荐

