Python 使用验证窗口进行时间序列数据交叉验证
我希望对我的时间序列数据执行前向验证。关于如何执行滚动窗口,存在大量文档: 或展开窗口 但这种验证与我的生产系统中的情况并不相符:我想每天重新训练一个模型,在未来14天内做出预测。因此,我只会在我之前的培训周期中添加一天的数据(其他方法在接下来的培训周期中添加了一整套长度Python 使用验证窗口进行时间序列数据交叉验证,python,validation,scikit-learn,time-series,Python,Validation,Scikit Learn,Time Series,我希望对我的时间序列数据执行前向验证。关于如何执行滚动窗口,存在大量文档: 或展开窗口 但这种验证与我的生产系统中的情况并不相符:我想每天重新训练一个模型,在未来14天内做出预测。因此,我只会在我之前的培训周期中添加一天的数据(其他方法在接下来的培训周期中添加了一整套长度测试大小;在我的情况下为14天的数据)。因此,我想用滑动窗口验证我的模型: 我的问题是,我找不到一个能完成这项工作的Python库。从sklearn那里,我们别无选择。 基本上我想提供: test\u size,n\u f
测试大小
;在我的情况下为14天的数据)。因此,我想用滑动窗口验证我的模型:
我的问题是,我找不到一个能完成这项工作的Python库。从sklearn那里,我们别无选择。
基本上我想提供:test\u size
,n\u fold
,min\u train\u size
和
如果
n\u fold>(n\u samples-min\u train\u size)%test\u size
,则下一步training\u set
从上一个foldtest\u set中提取数据看起来您的要求是使测试大小大于1倍。要进行更改,您需要调整线条
我已经做了这些更改,并添加了一个名为n\u test\u folds
的新参数,以便可以对其进行自定义
来自sklearn.model\u选择的。\u分割导入时间序列分割
从sklearn.utils.validation导入\u不推荐\u位置参数
从sklearn.utils导入可索引
从sklearn.utils.validation导入\u num\u示例
类WindowedTestTimeSeriesSplit(TimeSeriesSplit):
"""
参数
----------
n_测试折叠:int
每次迭代时用作测试的折叠数。
默认情况下,1。
"""
@_弃用位置参数
定义初始(自,n个拆分=5,*,最大列大小=无,n个测试折叠=1):
super().\uuuu init\uuuu(n\u拆分,
最大列车尺寸=最大列车尺寸)
self.n\u test\u folds=n\u test\u folds
def拆分(自身、X、y=无,组=无):
“”“生成索引以将数据拆分为训练集和测试集。”。
参数
----------
X:形状的阵列状(n个样本,n个特征)
训练数据,其中n_samples是样本数
n_features是特征的数量。
y:形状的数组状(n_个样本,)
始终忽略,因为兼容性而存在。
组:形状的阵列状(n_个样本)
始终忽略,因为兼容性而存在。
产量
------
火车:Ndaray
该分割的训练集索引。
测试:Ndaray
测试设置了该拆分的索引。
"""
十、 y,组=可索引(X,y,组)
n_样本=_num_样本(X)
n_splits=self.n_splits
n_折叠=n_拆分+自测试n_折叠
如果n_折叠>n_样本:
升值误差(
(“折叠次数不能大于{0}”
“比样本数:{1}.”。格式(n_倍,
n(样本)
指数=np.arange(n_样本)
折叠大小=(n个样本//n个折叠)
测试大小=折叠大小*self.n测试折叠测试窗口
测试开始=范围(折叠大小+n个样本%n个折叠,
n_samples-test_size+1,fold_size)#基于fold_size而不是test_size进行拆分
对于测试启动中的测试启动:
如果self.max\u系列尺寸和self.max\u系列尺寸<测试开始:
产量(指标[测试启动-自身最大列车大小:测试启动],
索引[测试开始:测试开始+测试大小])
其他:
收益率(指数[:测试开始],
索引[测试开始:测试开始+测试大小])
例如:
将numpy导入为np
X=np.数组([[1,2],[3,4],[1,2],[3,4],[1,2],[3,4])
y=np.数组([1,2,3,4,5,6])
tscv=WindowedTestTimesPeriesSplit(n_分割=4,n_测试折叠=2)
印刷品(tscv)
对于列车索引,在tscv.split(X)中测试列车索引:
打印(“列车:,列车索引,测试:,测试索引)
X_列,X_测试=X[列索引],X[测试索引]
y_列,y_测试=y[列指数],y[测试指数]
#WindowedTestTimesPeriesSplit(最大列大小=无,n个拆分=4,n个测试折叠=2)
#列车:[0]测试:[1 2]
#列车:[0 1]测试:[2 3]
#列车:[0 1 2]测试:[3 4]
#列车:[0 1 2 3]测试:[4 5]
注:未生成序列:[0 1 2 3 4]测试:[5],因为它不满足测试折叠次数的要求
使用函数,我们可以可视化CV的不同分割
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
np.random.seed(1338)
cmap_data = plt.cm.Paired
cmap_cv = plt.cm.coolwarm
n_splits = 4
# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)
percentiles_classes = [.1, .3, .6]
y = np.hstack([[ii] * int(100 * perc)
for ii, perc in enumerate(percentiles_classes)])
# Evenly spaced groups repeated once
groups = np.hstack([[ii] * 10 for ii in range(10)])
fig, ax = plt.subplots()
cv = WindowedTestTimeSeriesSplit(n_splits=n_splits, n_test_folds=2)
plot_cv_indices(cv, X, y, groups, ax, n_splits)
plt.show()
以下是我的解决方案,允许用户指定测试范围和用于培训的最小数据样本:
from sklearn.model_selection import TimeSeriesSplit
from sklearn.utils import indexable
from sklearn.utils.validation import _num_samples
class TimeSeriesSplitCustom(TimeSeriesSplit):
def __init__(self, n_splits=5, max_train_size=None,
test_size=1,
min_train_size=1):
super().__init__(n_splits=n_splits, max_train_size=max_train_size)
self.test_size = test_size
self.min_train_size = min_train_size
def overlapping_split(self, X, y=None, groups=None):
min_train_size = self.min_train_size
test_size = self.test_size
n_splits = self.n_splits
n_samples = _num_samples(X)
if (n_samples - min_train_size) / test_size >= n_splits:
print('(n_samples - min_train_size) / test_size >= n_splits')
print('default TimeSeriesSplit.split() used')
yield from super().split(X)
else:
shift = int(np.floor(
(n_samples - test_size - min_train_size) / (n_splits - 1)))
start_test = n_samples - (n_splits * shift + test_size - shift)
test_starts = range(start_test, n_samples - test_size + 1, shift)
if start_test < min_train_size:
raise ValueError(
("The start of the testing : {0} is smaller"
" than the minimum training samples: {1}.").format(start_test,
min_train_size))
indices = np.arange(n_samples)
for test_start in test_starts:
if self.max_train_size and self.max_train_size < test_start:
yield (indices[test_start - self.max_train_size:test_start],
indices[test_start:test_start + test_size])
else:
yield (indices[:test_start],
indices[test_start:test_start + test_size])
(要获得相同的结果,请确保更改
对于枚举(**cv.overlapping_split**(X=X,y=y,groups=group))中的ii(tr,tt):
在plot\u cv\u index
函数中
干杯!我的回答解决了你的问题吗?谢谢你的回答!它确实很有帮助;特别是可视化。我想出了一个稍微不同的解决方案,满足了我的要求,特别是关于min\u training\u size
和test\u size
,我想控制它,这很有帮助。我瘦了k、 测试大小n\u测试折叠已捕获的内容。滑动窗口视觉中未捕获您的最小训练大小。因此,我没有添加它。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
from ModelEvaluation import TimeSeriesSplitCustom
np.random.seed(1338)
cmap_data = plt.cm.Paired
cmap_cv = plt.cm.coolwarm
n_splits = 13
# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)
percentiles_classes = [.1, .3, .6]
y = np.hstack([[ii] * int(100 * perc)
for ii, perc in enumerate(percentiles_classes)])
# Evenly spaced groups repeated once
groups = np.hstack([[ii] * 10 for ii in range(10)])
fig, ax = plt.subplots()
cv = TimeSeriesSplitCustom(n_splits=n_splits, test_size=20, min_train_size=12)
plot_cv_indices(cv, X, y, groups, ax, n_splits)
plt.show()