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

Plotly图表缩放时基于X轴起始日期重新归一化数据

解决Plotly缩放时基于X轴起始日期自动归一化数据的问题

原始数据与初始绘图

先定义示例数据并绘制基础图表:

import pandas as pd
import datetime as dt
import plotly.graph_objects as go

# 示例数据
returns = pd.DataFrame({'time': [dt.date(2020,1,1), dt.date(2020,1,2), dt.date(2020,1,3), dt.date(2020,1,4), dt.date(2020,1,5), dt.date(2020,1,6)],
                        'longs': [0, 1,2,3,4,3],
                        'shorts': [0, -1,-2,-3,-4,-4]})

初始绘图代码:

fig = go.Figure()
fig.add_trace(
    go.Scatter(x=list(returns.time),
               y=list(returns.longs),
               name="Longs",
               line=dict(color="#0c56cc")))
fig.add_trace(
    go.Scatter(x=list(returns.time),
               y=list(returns.shorts),
               name="Shorts",
               line=dict(color="#850411", dash="dash")))
fig.show()

初始图表效果:
初始图表

需求说明

希望缩放图表的特定区间时,以X轴区间的首个日期为基准对数据重新归一化。例如缩放至1月5日之后的区间时,应得到如下效果:
期望效果

问题分析

你尝试用fig.layout.on_change实现功能但图表无变化,核心问题在于:

  • 在zoom函数中重新创建了FigureWidget实例,覆盖了原对象,而非更新已有实例的轨迹数据
  • 未保留原始图表的样式配置(如线条颜色、虚线样式)

解决方案

修改zoom函数直接更新已有FigureWidget的轨迹数据,同时优化归一化逻辑提升效率:

import pandas as pd
import datetime as dt
import plotly.graph_objects as go

# 示例数据
returns = pd.DataFrame({'time': [dt.date(2020,1,1), dt.date(2020,1,2), dt.date(2020,1,3), dt.date(2020,1,4), dt.date(2020,1,5), dt.date(2020,1,6)],
                        'longs': [0, 1,2,3,4,3],
                        'shorts': [0, -1,-2,-3,-4,-4]})

# 优化后的归一化函数
def renormalise_returns(returns, renorm_date):
    # 统一日期格式为datetime,避免类型不匹配
    renorm_date = pd.to_datetime(renorm_date)
    returns['time'] = pd.to_datetime(returns['time'])
    
    # 获取缩放起始日期对应的基准值
    base_vals = returns[returns['time'] >= renorm_date][['longs', 'shorts']].iloc[0]
    # 计算归一化后的数据
    norm_df = returns.copy()
    norm_df[['longs', 'shorts']] = norm_df[['longs', 'shorts']] - base_vals
    
    return norm_df

# 创建带样式的FigureWidget实例
fig = go.FigureWidget([
    go.Scatter(x=returns['time'], y=returns['longs'], name='Longs', line=dict(color="#0c56cc")),
    go.Scatter(x=returns['time'], y=returns['shorts'], name="Shorts", line=dict(color="#850411", dash="dash"))
])

def zoom(layout, xrange):
    # 获取缩放区间的起始日期
    xrange_min = pd.to_datetime(xrange[0])
    # 生成归一化后的数据
    norm_df = renormalise_returns(returns, xrange_min)
    
    # 直接更新已有轨迹的x、y数据,保留原样式
    fig.data[0].x = norm_df['time']
    fig.data[0].y = norm_df['longs']
    fig.data[1].x = norm_df['time']
    fig.data[1].y = norm_df['shorts']

# 绑定x轴范围变化事件
fig.layout.on_change(zoom, 'xaxis.range')
fig.show()

关键修改点

  1. 直接更新轨迹数据:不再重新创建FigureWidget,而是修改fig.data中已有轨迹的属性,确保原样式保留且实例被正确更新
  2. 简化归一化逻辑:通过iloc[0]直接获取基准值,避免不必要的melt和pivot操作,提升代码效率
  3. 统一日期类型:将所有日期转换为datetime,避免比较时的类型不匹配问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:37:29