如何让numpy.polyfit拟合季节性模型时避免周度拟合值跳变?
解决周度时间序列季节性拟合的首尾跳变问题
针对你用多项式拟合周度平均收益时出现的第52周与第1周跳变问题,以下是几种可行的解决方案:
方案1:带首尾相等约束的多项式拟合
numpy.polyfit是无约束最小二乘,要强制拟合曲线在x=1和x=52处值相等,可通过自定义约束的优化实现:
import numpy as np from scipy.optimize import minimize # 示例数据(替换为你的真实数据) weeks = np.arange(1, 53) avg_returns = np.random.randn(52) # 定义多项式拟合的损失函数:拟合误差平方和 def poly_loss(coeffs, x, y): y_pred = np.polyval(coeffs, x) return np.sum((y_pred - y)**2) # 定义约束条件:f(1) = f(52) def continuity_constraint(coeffs): return np.polyval(coeffs, 1) - np.polyval(coeffs, 52) # 用无约束拟合结果作为初始值 init_coeffs = np.polyfit(weeks, avg_returns, deg=3) # 带约束的最小化求解 cons = {'type': 'eq', 'fun': continuity_constraint} result = minimize(poly_loss, init_coeffs, args=(weeks, avg_returns), constraints=cons) constrained_coeffs = result.x # 生成平滑拟合曲线 fit_x = np.linspace(1, 52, 100) fit_y = np.polyval(constrained_coeffs, fit_x)
方案2:使用周期性基函数(傅里叶级数)
多项式本身不具备周期性,改用正弦/余弦基函数的傅里叶拟合,天然满足首尾连续:
import numpy as np weeks = np.arange(1, 53) avg_returns = np.random.randn(52) # 构造傅里叶基函数(可通过调整谐波数量控制平滑度) def build_fourier_basis(x, n_harmonics): basis = np.zeros((len(x), 2*n_harmonics + 1)) basis[:, 0] = 1 # 常数项 for k in range(1, n_harmonics+1): basis[:, 2*k-1] = np.sin(2 * np.pi * k * x / 52) basis[:, 2*k] = np.cos(2 * np.pi * k * x / 52) return basis # 选择2个谐波(可根据需求增减) n_harmonics = 2 X = build_fourier_basis(weeks, n_harmonics) # 最小二乘拟合 coeffs = np.linalg.lstsq(X, avg_returns, rcond=None)[0] # 生成拟合曲线 fit_x = np.linspace(1, 52, 100) fit_X = build_fourier_basis(fit_x, n_harmonics) fit_y = fit_X @ coeffs
方案3:优化伪周扩展法
针对你之前的伪周扩展效果不佳的问题,可通过加权拟合提升原数据的权重:
import numpy as np weeks = np.arange(1, 53) avg_returns = np.random.randn(52) # 扩展数据:前后各追加10周(原数据43-52周前置,1-10周后置) extend_weeks = np.concatenate([np.arange(1-10, 1), weeks, np.arange(52+1, 52+11)]) extend_returns = np.concatenate([avg_returns[-10:], avg_returns, avg_returns[:10]]) # 加权拟合:原数据权重为1,扩展数据权重为0.5,降低扩展数据的影响 weights = np.concatenate([np.ones(10)*0.5, np.ones(52)*1, np.ones(10)*0.5]) coeffs = np.polyfit(extend_weeks, extend_returns, deg=3, w=weights) # 生成1-52周的拟合曲线 fit_x = np.linspace(1, 52, 100) fit_y = np.polyval(coeffs, fit_x)
方案4:周期性样条插值
使用三次周期性样条,直接强制首尾值和导数连续,彻底解决跳变:
import numpy as np from scipy.interpolate import CubicSpline weeks = np.arange(1, 53) avg_returns = np.random.randn(52) # 创建周期性三次样条 cs = CubicSpline(weeks, avg_returns, bc_type='periodic') # 生成平滑曲线 fit_x = np.linspace(1, 52, 100) fit_y = cs(fit_x)
内容的提问来源于stack exchange,提问作者younggotti
相关产品推荐
相关产品推荐

