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()
关键修改点
- 直接更新轨迹数据:不再重新创建
FigureWidget,而是修改fig.data中已有轨迹的属性,确保原样式保留且实例被正确更新 - 简化归一化逻辑:通过
iloc[0]直接获取基准值,避免不必要的melt和pivot操作,提升代码效率 - 统一日期类型:将所有日期转换为
datetime,避免比较时的类型不匹配问题
内容的提问来源于stack exchange,提问作者Stuart
相关产品推荐
相关产品推荐

