ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

sktime 分数阶差分(Fractional Differentiation)实战指南:基于 Fracdiff 的非平稳时间序列平稳化与记忆保持

2026/9/16 20:49:08 拓冰建站 浏览量
sktime 分数阶差分(Fractional Differentiation)实战指南:基于 Fracdiff 的非平稳时间序列平稳化与记忆保持 sktime 分数阶差分Fractional Differentiation实战指南基于 Fracdiff 的非平稳时间序列平稳化与记忆保持【免费下载链接】sktimeA unified framework for machine learning with time series项目地址: https://gitcode.com/GitHub_Trending/sk/sktime导读本文围绕 sktime 内置的Fracdiff库路径sktime/libs/fracdiff/展开讲解如何对非平稳时间序列执行分数阶差分Fractional Differentiation在使其平稳化的同时最大限度保留原始序列中的记忆memory信息。读者将掌握fdiff函数、scikit-learn 风格转换器Fracdiff的完整参数用法、窗长window与模式mode对结果的影响以及如何用统计检验验证平稳化效果并能直接将 Fracdiff 嵌入机器学习 Pipeline 用于金融等领域的特征工程。Fracdiff 是fracdiff包在 sktime 中的非官方 fork其算法思想源自 Marcos López de Prado 的著作Advances in Financial Machine LearningWiley, 2018。核心价值在于对价格类序列普通的一阶差分d1虽然能消除非平稳性却会过度差分丢掉长期记忆而分数阶差分通过在0 d 1之间选取阶数可在平稳性与信息保留之间取得平衡。分数阶差分为什么一阶差分会丢失记忆传统的整数阶差分如numpy.diff的一阶、二阶差分是分数阶差分在d为整数时的特例。它的缺陷在于对随机游走类型的金融价格序列d1虽然能使其平稳但会抹掉序列中蕴含的长期趋势记忆momentum 等特征这在建模收益预测时会造成信息损失反之d0完全不差分序列保持非平稳。分数阶差分在两者之间连续取值。其数学基础是差分算子的分数次幂对整数d差分算子(1 - B)^dB为后移算子展开后项数有限对分数d展开是无穷级数实际计算时通过截断取前window项来近似截断项系数即为fracdiff 系数。在 sktime/libs/fracdiff/fdiff.py 中系数序列由fdiff_coef生成from sktime.libs.fracdiff import fdiff_coef fdiff_coef(0.5, 4) # array([ 1. , -0.5 , -0.125 , -0.0625]) fdiff_coef(1.0, 4) # array([ 1., -1., 0., -0.]) fdiff_coef(1.5, 4) # array([ 1. , -1.5 , 0.375 , 0.0625])其实现为(-1) ** np.arange(window) * binom(d, np.arange(window))即负二项式系数。观察可知d0.5时系数序列衰减缓慢、永不归零这正是分数阶差分保留记忆的来源而d1.0时系数只有前两项非零等价于一阶差分。测试 sktime/libs/fracdiff/tests/test_fdiff.py 用 Pochhammer 符号s * p / f独立重算系数并与fdiff_coef对照验证了该实现的正确性。功能总览Fracdiff 提供两类接口覆盖 NumPy 函数式调用与 scikit-learn 对象式集成接口说明fdiff(a, n, axis, ...)将numpy.diff推广到分数阶的纯函数输入输出均为 NumPy 数组Fracdiff(d, window, mode, window_policy)scikit-learn Transformer通过fit/transform计算分数阶差分可嵌入 PipelineFracdiffStatFracdiff的扩展自动搜索使序列平稳的最小差分阶数d_注当前 sktime fork 中未包含该类实现原fracdiff包提供fdiff_coef(d, window)返回 fracdiff 算子系数序列sklearn 层的公开导出见 sktime/libs/fracdiff/sklearn/init.py模块顶层统一导出fdiff、fdiff_coef、Fracdiff见 sktime/libs/fracdiff/init.py。快速上手fdiff函数一维序列fdiff是numpy.diff在分数阶上的自然延伸。对整数n两者在n:之后的位置结果一致只是fdiff会把开头n个元素用广义差分算子的值填充这是为了保持输出与输入等长import numpy as np from sktime.libs.fracdiff import fdiff a np.array([1, 2, 4, 7, 0]) fdiff(a, 0.5) # array([ 1. , 1.5 , 2.875 , 4.6875 , -4.1640625]) np.array_equal(fdiff(a, n1), np.diff(a, n1)) # True在 sktime/libs/fracdiff/fdiff.py 的 docstring 中给出了等价的断言np.diff(a) fdiff(a)[1:]、np.diff(a, 2) fdiff(a, 2)[2:]。测试 sktime/libs/fracdiff/tests/test_fdiff.py 进一步在 1~3 维随机数组上、沿所有轴验证了整数n情形下fdiff(a, n, axisaxis)[n:]与np.diff完全一致并在test_axis中确认负轴索引axis-4等价于axis0工作正常。多维数组与轴向金融面板数据通常是(n_samples, n_features)形状axis参数决定沿哪个轴做差分。沿时间轴axis0与沿特征轴axis-1结果完全不同a np.array([[1, 3, 6, 10], [0, 5, 6, 8]]) # 沿时间轴行方向 fdiff(a, 0.5, axis0) # array([[ 1. , 3. , 6. , 10. ], # [-0.5, 3.5, 3. , 3. ]]) # 沿最后一个轴列方向 fdiff(a, 0.5, axis-1) # array([[1. , 2.5 , 4.375 , 6.5625], # [0. , 5. , 3.5 , 4.375 ]])参数速查表参数类型 / 默认值作用aarray_like输入数组会被强制转为 NumPy 数组nfloat默认1.0差分阶数。整数时与numpy.diff对齐非整数时为分数阶差分axisint默认-1沿哪个轴做差分默认最后一个轴prepend/appendarray_like可选在差分前沿axis拼接的值标量会自动广播为长度为 1 的数组。用于边界样本的处理windowint默认10计算输出中每个元素所使用的观测数量fracdiff 算子截断项数mode{same, valid}默认samesame输出与输入沿axis等长序列开头存在边界效应valid只返回所有系数都参与卷积的元素输出长度减为L_in - window 1无边界效应两点实现细节值得注意modefull已被弃用并重命名为same传入full会抛出ValueErrorfdiff.py测试 test_fdiff.py 也对此做了回归校验modevalid与prepend组合使用可以补齐因截断丢失的window-1个元素。例如fdiff(a, 0.5, window3, modevalid, prepend[1, 1]) # array([ 0.375, 1.375, 2.875, 4.75 , -4. ])测试文件中的test_prepend/test_append证明fdiff(a, ..., prependp)与先把p拼到a上再差分的结果逐元素相等。scikit-learn APIFracdiff转换器基本用法把 2 维时间序列X形状(n_samples, n_features)传给Fracdiff转换器即可完成差分from sktime.libs.fracdiff.sklearn import Fracdiff X ... # 2d time-series with shape (n_samples, n_features) f Fracdiff(0.5) X f.fit_transform(X)Fracdiff继承自TransformerMixin, BaseEstimator见 sktime/libs/fracdiff/sklearn/fracdiff.py。其构造参数与底层fdiff一一对应参数类型 / 默认值说明dfloat默认1.0差分阶数windowint 0默认10计算每个输出元素所用观测数mode{same, valid}默认same同fdiff的modewindow_policy{fixed}默认fixed目前仅支持固定窗长法fracdiff 算子后移算子的多项式截断到第window项expanding 扩展窗方法尚未实现fit阶段只做两件事通过_sklearn_check_input做输入校验并记录n_features_in_然后调用fdiff_coef(self.d, self.window)预计算系数存入coef_transform阶段校验特征数与fit一致后调用fdiff(X, nself.d, axis0, windowself.window, modeself.mode)完成实际差分见 fracdiff.py。也就是说Fracdiff固定沿axis0时间轴做差分这与面板数据的常规布局一致。窗长与模式对输出的影响window与mode的组合决定了输出的长度与边界质量。以形状(5, 2)的数组为例import numpy as np X np.arange(10).reshape(5, 2) # modesame输出与输入等长 fracdiff Fracdiff(0.5, window3) fracdiff.fit_transform(X) # array([[0. , 1. ], # [2. , 2.5 ], # [3. , 3.375], # [3.75 , 4.125], # [4.5 , 4.875]]) fracdiff.coef_ # array([ 1. , -0.5 , -0.125]) # modevalid开头 window-12 行被剔除 fracdiff Fracdiff(0.5, window3, modevalid) fracdiff.fit_transform(X) # array([[3. , 3.375], # [3.75 , 4.125], # [4.5 , 4.875]])测试 sktime/libs/fracdiff/tests/sklearn/test_fracdiff.py 验证了Fracdiff(d, window, mode).fit_transform(X)与直接调用fdiff(X, nd, axis0, windowwindow, modemode)逐元素相等sktime/libs/fracdiff/tests/sklearn/test_sklearn.py 则用 sklearn 官方parametrize_with_checks对Fracdiff(1)做全套兼容性检查仅豁免排列不变性检查因为差分天然与样本顺序有关并端到端跑通了包含StandardScaler Fracdiff LinearRegression的 Pipeline。嵌入机器学习 Pipeline由于实现了完整的 sklearn Transformer 协议Fracdiff可以无缝插入sklearn.pipeline.Pipeline先标准化再差分再回归from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline X, y ... # Dataset pipeline Pipeline([ (scaler, StandardScaler()), (fracdiff, Fracdiff(0.5)), (regressor, LinearRegression()), ]) pipeline.fit(X, y)顺序上有讲究StandardScaler放在Fracdiff之前先消除量纲影响再做差分特征工程。这一组合模式正是 examples/transformation/fracdiff/example_howto.ipynb 中演示的标准用法。在保留记忆的同时实现平稳化最小阶数搜索的思路Fracdiff需要人工指定d。对于未知数据最稳妥的做法是在保持平稳的前提下取尽可能小的差分阶数因为阶数越低保留的长期记忆越多。原fracdiff包为此提供了FracdiffStat它在一个区间内搜索使序列通过平稳性检验的最小差分阶数搜索结果保存在属性d_数组每列特征一个阶数中from fracdiff.sklearn import FracdiffStat X ... # 2d time-series with shape (n_samples, n_features) f FracdiffStat() X f.fit_transform(X) f.d_ # array([0.71875 , 0.609375, 0.515625])说明FracdiffStat及其d_属性是上游fracdiff包提供的功能在 sktime 当前的 forksktime/libs/fracdiff/init.py中顶层仅导出fdiff、fdiff_coef与FracdiffFracdiffStat尚未被同步进来。若需自动定阶可安装原版fracdiff使用或按下一节思路自行搜索。用 ADF 检验验证平稳化效果分数阶差分的记忆-平稳权衡可以用 ADF 单位根检验量化。在 examples/transformation/fracdiff/example_prado.ipynb 中作者对 SP 500 序列做了完整实验多阶数扫描对d在 0.1~0.9 之间取 9 个值分别做fdiff(spx, d, modevalid)并绘图直观看到差分后序列随d增大而波动加剧、趋势被逐步剥离平稳性-记忆权衡曲线以 ADF 统计量越负越平稳为左轴、与原始序列的相关系数记忆的代理指标为右轴绘制d ∈ [0, 1]上的变化曲线并画出 5% 临界值虚线。可以看到d越大 ADF 统计量越低越平稳但相关系数越低记忆越少定阶与验证fs FracdiffStat(modevalid) Xdiff fs.fit_transform(X) _, pvalue, _, _, _, _ stattools.adfuller(Xdiff.reshape(-1)) corr np.corrcoef(X[-Xdiff.size:, 0], Xdiff.reshape(-1))[0][1] print(f* Order: {fs.d_[0]:.2f}) print(f* ADF p-value: {100 * pvalue:.2f} %) print(f* Correlation with the original time-series: {corr:.2f})ADF p 值低于显著性水平如 5%说明差分后序列平稳与原始序列的相关性越高说明保留的记忆越多。同样examples/transformation/fracdiff/example_exercise.ipynb书中第 5 章习题的求解示例给出了完整的搜索模式先扫d ∈ {0.2, 0.4, ..., 1.0}看 p 值再用FracdiffStat(window100, modevalid, precision10e-8, lower0.9)在高精度区间内逼近最小平稳阶数最后用Fracdiff(f.d_[0] - precision, modevalid)反向验证阶数再低一档就不平稳。窗长选择基于容差的自动截断window决定 fracdiff 算子截断到第几项直接影响计算精度与速度。手动调窗比较繁琐上游fracdiff包提供了两个基于容差的工具函数sktime fork 中对应实现位于 sktime/libs/fracdiff/sklearn/tol.pywindow_from_tol_coef(n, tol_coef, max_window2**12)取使第window个系数的绝对值首次小于tol_coef的最小整数。示例window_from_tol_coef(0.5, 0.1)返回 4因为fdiff_coef(0.5, 3)[-1] -0.125fdiff_coef(0.5, 4)[-1] -0.0625后者首次小于 0.1window_from_tol_memory(n, tol_memory, max_window2**12)取使从第window1项起系数的绝对值之和小于tol_memory的最小整数即显式控制记忆损失量。示例window_from_tol_memory(0.5, 0.2)返回 9对应尾部系数累积和约-0.19073。from sktime.libs.fracdiff.sklearn.tol import window_from_tol_coef, window_from_tol_memory window_from_tol_coef(0.5, 1e-5) # 允许系数截断误差不超过 1e-5 时的窗长 window_from_tol_memory(0.5, 0.2) # 允许记忆损失不超过 20% 时的窗长注意 tol.py 中的提醒当d很小或容差很小时窗长会变得极大量级约随tol_coef ** (-1 / d)增长。上述示例用法与测试断言test_sklearn.py一一对应可直接照搬。速度优势NumPy 引擎与基准数据README 中给出了与官方实现指按定义逐点累加的朴素实现的对比基准强调两点得益于 NumPy 引擎执行时间随时间步数n_samples增长非常缓慢在特定规模下可快约 4 个数量级。不同n_samples每列单特征的执行时间毫秒Ubuntu 20.04 / Intel Xeon E5-2673 v3 2.40GHzn_samplesfracdiffofficial1000.675 ± 0.08620.008 ± 1.47210005.081 ± 0.426135.613 ± 3.4151000050.644 ± 0.5741310.033 ± 17.708100000519.969 ± 8.16613113.457 ± 105.274不同n_features固定 1000 个时间步的执行时间毫秒n_featuresfracdiffofficial15.081 ± 0.426135.613 ± 3.415106.146 ± 0.2471350.161 ± 15.1951006.903 ± 0.65413675.023 ± 193.960100013.783 ± 0.700136610.030 ± 540.572从实现看速度来自fdiff将差分转化为一次卷积运算modevalid时直接np.convolve(coef, a, modevalid)modesame时做modefull卷积后截取尾部等长部分fdiff.py。卷积是高度向量化的 NumPy 操作因此耗时几乎与窗长和样本数的乘积成正比而非平方增长。上述数据来自原 README 的基准表硬件环境与版本以当时测试为准具体数字会因机器、NumPy 版本与数据规模而异请以实际复测为准。真实数据示例与可视化SP 500 的 0.5 阶差分以下脚本来自 examples/transformation/fracdiff/fig/spx.py演示了从 Yahoo Finance 拉取 SP 500 数据、用Fracdiff(0.5, window100, modevalid)做差分并绘制的完整流程import pandas as pd import pandas_datareader from sktime.libs.fracdiff import Fracdiff s pandas_datareader.data.DataReader( ^GSPC, yahoo, 1999-10-01, 2020-09-30 )[Adj Close] f Fracdiff(0.5, window100, modevalid) d f.fit_transform(s.values.reshape(-1, 1)).reshape(-1) s s[100 - 1:] # valid 模式丢掉前 window-1 个样本索引对齐 d pd.Series(d, indexs.index)图中蓝线为原始 SP 500 收盘价左轴橙线为 0.5 阶差分后的序列右轴原始序列呈现长期上升趋势差分序列围绕零轴剧烈波动趋势被削弱而波动信息被保留且差分序列与价格水平的量纲已解耦。Nikkei 225 的自适应定阶差分examples/transformation/fracdiff/example_howto.ipynb 展示了另一条路径——不手动指定阶数而是让FracdiffStat自动搜索使序列平稳的最小阶数此处对 Nikkei 225 得到约 0.27 阶from fracdiff import FracdiffStat nky fetch_yahoo(^N225) # 日经 225 指数 fs FracdiffStat(window100, modevalid) diff fs.fit_transform(nky.values.reshape(-1, 1))与固定 0.5 阶相比自动定阶得到的阶数更低意味着差分后的序列保留了更多原始记忆同时通过了平稳性检验——这正是保持记忆的平稳化stationarity while preserving memory的实际效果。边界与注意事项轴语义fdiff的modesame保证输出与输入等长但序列开头的元素只用了部分系数边界效应modevalid无边界效应但会缩短序列使用时需自行对齐索引见上面 SP 500 示例中的s s[window-1:]。特征数一致性Fracdiff在transform时会校验输入特征数与fit时一致不一致会抛出ValueErrorfracdiff.py。输入维度fdiff要求至少一维输入0 维标量会抛ValueError传入非法mode同样抛错test_fdiff.py。线性性质分数阶差分是线性算子——测试test_linearity_add与test_linearity_mul验证了fdiff(a0) fdiff(a1) fdiff(a0 a1)与const * fdiff(a0) fdiff(const * a0)这保证了它作为线性特征变换嵌入模型时的可解释性。窗长取舍window过小会引入较大的截断误差阶数越高越明显过大则浪费计算量在要求精度时优先用window_from_tol_coef/window_from_tol_memory按容差自动定窗。参考资源核心实现sktime/libs/fracdiff/fdiff.pyfdiff、fdiff_coefsklearn 转换器sktime/libs/fracdiff/sklearn/fracdiff.pyFracdiff窗长工具sktime/libs/fracdiff/sklearn/tol.pywindow_from_tol_coef、window_from_tol_memory单元测试sktime/libs/fracdiff/tests/test_fdiff.py、sktime/libs/fracdiff/tests/sklearn/test_fracdiff.py、sktime/libs/fracdiff/tests/sklearn/test_sklearn.py可交互示例examples/transformation/fracdiff/example_howto.ipynb、examples/transformation/fracdiff/example_prado.ipynb、examples/transformation/fracdiff/example_exercise.ipynb理论出处Marcos López de Prado,Advances in Financial Machine Learning, Wiley, 2018分数阶差分章节以上所有代码与参数说明均以当前仓库内的实现与测试为准其中引用FracdiffStat的示例来自上游fracdiff包及其 notebook实际以所在环境安装的包为准。【免费下载链接】sktimeA unified framework for machine learning with time series项目地址: https://gitcode.com/GitHub_Trending/sk/sktime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考