滚动窗口 REVISITED - 添加窗口滚动数量作为参数 - 向前分析
Posted
技术标签:
【中文标题】滚动窗口 REVISITED - 添加窗口滚动数量作为参数 - 向前分析【英文标题】:Rolling window REVISITED - Adding window rolling quantity as a parameter- Walk Forward Analysis 【发布时间】:2019-05-16 17:54:46 【问题描述】:我一直在网上搜索可以创建 滚动窗口 的方法,以便我可以以一种通用的方式对时间序列执行一种称为前向分析的交叉验证技术。
但是,我还没有找到任何在 1) 窗口大小方面包含灵活性的解决方案(几乎所有方法都有这个;例如,pandas
rolling 或有点不同的np.roll)和 2 ) 窗口滚动量,理解为我们要滚动窗口多少个索引(即没有找到任何包含这个的索引)。
在this answer 中的@coldspeed 的帮助下,我一直在尝试优化和制作简洁的代码(我无法在那里发表评论,因为我没有达到所需的声誉;希望尽快到达那里!),但我无法合并窗户滚动数量。
我的想法:
我已经尝试使用np.roll
以及下面的示例,但没有成功。
我还尝试修改下面的代码乘以 ith
值,但我无法将其放入我想维护的列表理解中。
3.下面的示例适用于任何窗口大小,但是,它只会提前一步“滚动”窗口,我希望它可以推广到任何步骤。
那么,¿有没有办法让这两个参数在列表理解方法中可用?或者,¿是否有任何其他我没有找到的资源可以使这更容易?非常感谢所有帮助。我的示例代码如下:
In [1]: import numpy as np
In [2]: arr = np.random.random((10,3))
In [3]: arr
Out[3]: array([[0.38020065, 0.22656515, 0.25926935],
[0.13446667, 0.04386083, 0.47210474],
[0.4374763 , 0.20024762, 0.50494097],
[0.49770835, 0.16381492, 0.6410294 ],
[0.9711233 , 0.2004874 , 0.71186102],
[0.61729025, 0.72601898, 0.18970222],
[0.99308981, 0.80017134, 0.64955358],
[0.46632326, 0.37341677, 0.49950571],
[0.45753235, 0.55642914, 0.31972887],
[0.4371343 , 0.08905587, 0.74511753]])
In [4]: inSamplePercentage = 0.4
In [5]: outSamplePercentage = 0.3 * inSamplePercentage
In [6]: windowSizeTrain = round(inSamplePercentage * arr.shape[0])
In [7]: windowSizeTest = round(outSamplePercentage * arr.shape[0])
In [8]: windowTrPlusTs = windowSizeTrain + windowSizeTest
In [9]: sliceListX = [arr[i: i + windowTrPlusTs] for i in range(len(arr) - (windowTrPlusTs-1))]
给定 5 的窗口长度和 2 的窗口滚动数量,我可以指定如下内容:
Out [15]:
[array([[0.38020065, 0.22656515, 0.25926935],
[0.13446667, 0.04386083, 0.47210474],
[0.4374763 , 0.20024762, 0.50494097],
[0.49770835, 0.16381492, 0.6410294 ],
[0.9711233 , 0.2004874 , 0.71186102]]),
array([[0.4374763 , 0.20024762, 0.50494097],
[0.49770835, 0.16381492, 0.6410294 ],
[0.9711233 , 0.2004874 , 0.71186102],
[0.61729025, 0.72601898, 0.18970222],
[0.99308981, 0.80017134, 0.64955358]]),
array([[0.9711233 , 0.2004874 , 0.71186102],
[0.61729025, 0.72601898, 0.18970222],
[0.99308981, 0.80017134, 0.64955358],
[0.46632326, 0.37341677, 0.49950571],
[0.45753235, 0.55642914, 0.31972887]]),
array([[0.99308981, 0.80017134, 0.64955358],
[0.46632326, 0.37341677, 0.49950571],
[0.45753235, 0.55642914, 0.31972887],
[0.4371343 , 0.08905587, 0.74511753]])]
(这包含了最后一个数组,尽管它的长度小于 5)。
或:
Out [16]:
[array([[0.38020065, 0.22656515, 0.25926935],
[0.13446667, 0.04386083, 0.47210474],
[0.4374763 , 0.20024762, 0.50494097],
[0.49770835, 0.16381492, 0.6410294 ],
[0.9711233 , 0.2004874 , 0.71186102]]),
array([[0.4374763 , 0.20024762, 0.50494097],
[0.49770835, 0.16381492, 0.6410294 ],
[0.9711233 , 0.2004874 , 0.71186102],
[0.61729025, 0.72601898, 0.18970222],
[0.99308981, 0.80017134, 0.64955358]]),
array([[0.9711233 , 0.2004874 , 0.71186102],
[0.61729025, 0.72601898, 0.18970222],
[0.99308981, 0.80017134, 0.64955358],
[0.46632326, 0.37341677, 0.49950571],
[0.45753235, 0.55642914, 0.31972887]])]
(只有长度 == 5 的数组 -> 但是,这可以从上面的一个简单掩码派生而来)。
编辑:忘了提this also——如果熊猫滚动对象支持iter方法,可以做一些事情。
【问题讨论】:
【参考方案1】:所以,给我两分钱(在@Ben.T 的帮助下),这里是创建前向分析基本工具的代码,以了解您的模型/模型以更通用的方式执行。
非锚定 WFA
def walkForwardAnal(myArr, windowSize, rollQty):
from numpy.lib.stride_tricks import as_strided
ArrRows, ArrCols = myArr.shape
ArrItems = myArr.itemsize
sliceQtyAndShape = (int((ArrRows - windowSize) / rollQty + 1), windowSize, ArrCols)
print('The final view shape is '.format(sliceQtyAndShape))
ArrStrides = (rollQty * ArrCols * ArrItems, ArrCols * ArrItems, ArrItems)
print('The final strides are '.format(ArrStrides))
sliceList = list(as_strided(myArr, shape=sliceQtyAndShape, strides=ArrStrides, writeable=False))
return sliceList
wSizeTr = 400
wSizeTe = 100
wSizeTot = wSizeTr + wSizeTe
rQty = 200
sliceListX = wf.walkForwardAnal(X, wSizeTot, rQty)
sliceListY = wf.walkForwardAnal(y, wSizeTot, rQty)
for sliceArrX, sliceArrY in zip(sliceListX, sliceListY):
## Consider having to make a .copy() of each array, so that we don't modify the original one.
# XArr = sliceArrX.copy() and hence, changing Xtrain, Xtest = XArr[...]
# YArr = sliceArrY.copy() and hence, changing Ytrain, Ytest = XArr[...]
Xtrain = sliceArrX[:-wSizeTe,:]
Xtest = sliceArrX[-wSizeTe:,:]
Ytrain = sliceArrY[:-wSizeTe,:]
Ytest = sliceArrY[-wSizeTe:,:]
锚定 WFA
timeSeriesCrossVal = TimeSeriesSplit(n_splits=5)
for trainIndex, testIndex in timeSeriesCrossVal.split(X):
## Check if the training and testing quantities make sense. If not, increase or decrease the n_splits parameter.
Xtrain = X[trainIndex]
Xtest = X[testIndex]
Ytrain = y[trainIndex]
Ytest = y[testIndex]
然后,您可以创建以下内容(使用两种方法中的任何一种)并继续建模:
# Fit on training set only - The targets (y) are already encoded in dummy variables, so no need to standarize them.
scaler = StandardScaler()
scaler.fit(Xtrain)
# Apply transform to both the training set and the test set.
trainX = scaler.transform(Xtrain)
testX = scaler.transform(Xtest)
## PCA - Principal Component Analysis #### APPLY PCA TO THE STANDARIZED TRAINING SET! :::: Fit on training set only.
pca = PCA(.95)
pca.fit(trainX)
# Apply transform to both the training set and the test set.
trainX = pca.transform(trainX)
testX = pca.transform(testX)
## Predict and append predictions...
具有广义窗口滚动量的非锚定情况的单衬:
sliceListX = [arr[i: i + wSizeTot] for i in range(0, arr.shape[0] - wSizeTot+1, rQty)]
【讨论】:
【参考方案2】:IIUC你想要什么,你可以使用np.lib.stride_tricks.as_strided
创建窗口大小和滚动数量的视图例如:
#redefine arr to see better what is happening than with random numbers
arr = np.arange(30).reshape((10,3))
#get arr properties
arr_0, arr_1 = arr.shape
arr_is = arr.itemsize #the size of element in arr
#parameter window and rolling
win_size = 5
roll_qty = 2
# use as_stribed by defining the right parameters:
from numpy.lib.stride_tricks import as_strided
print (as_strided( arr,
shape=(int((arr_0 - win_size)/roll_qty+1), win_size,arr_1),
strides=(roll_qty*arr_1*arr_is, arr_1*arr_is, arr_is)))
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14]],
[[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17],
[18, 19, 20]],
[[12, 13, 14],
[15, 16, 17],
[18, 19, 20],
[21, 22, 23],
[24, 25, 26]]])
对于另一个窗口大小和滚动数量:
win_size = 4
roll_qty = 3
print( as_strided( arr,
shape=(int((arr_0 - win_size)/roll_qty+1), win_size,arr_1),
strides=(roll_qty*arr_1*arr_is, arr_1*arr_is, arr_is)))
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17],
[18, 19, 20]],
[[18, 19, 20],
[21, 22, 23],
[24, 25, 26],
[27, 28, 29]]])
【讨论】:
@Ezarate11 这就是我对它的理解。创建数组时,它存储在连续内存中,数组的每个元素都有一个大小(这里是 8 个字节),strides
是在内存中移动的字节数,以获取每个方向的下一个项目.对于同一行,要获得该行中的下一项,您需要移动 8 个字节,在下一行,每行的元素数为 8 个字节(此处为 3*8=24)如果由于上述原因,您执行arr.strides
您会得到(24,8)
。请注意,移位一词可能不是确切的术语,但它是我能想到的最好的术语。
@Ezarate11 请参阅有关 strides
的文档。现在解决问题,首先要注意它们只是视图而不是副本。例如,如果在您的代码中的某个时刻,您将一个值赋予as_stribed
的一个元素,比如as_stribed( ...)[0,0,0] = 55
,那么您的原始数组也已被修改。另一件事是,如果您不使用原始的strides
(实际上我们使用它们,因为它们分别对应于arr_1*arr_is
和arr_is
),那么您的输出将毫无意义。
@Ezarate11 尝试将as_strides
的最后一个步幅更改为arr_is/3
,例如,您将获得不在原始数组中的值,因为内存中的移位不合适。如果您在第一个维度中执行多个 int((arr_0 - win_size)/roll_qty+1)
元素(例如添加 2),那将是同样的事情。在某些时候,我想您可以访问您的代码不应该访问的内存,如果您修改它,那么它会为您的另一部分内存搞砸很多事情。但这正是我的猜测:)
@Ezarate11 总结一下,这里在strides
,第一个元素是roll_qty*arr_1*arr_is
,因为你想在内存中跳转你的原始数组的足够多的元素来获取行的第一个元素如果有意义的话,就在roll_qty
行!其他两个值是arr.stribes
,以在之后的行和列中保持相同的值。
@Ezarate11 good :),顺便说一句,如果你想创建sliceListX
,那么你可以通过在range
中定义一个步骤来实现,例如:[arr[i: i + win_size] for i in range(0, arr_0 - win_size+1, roll_qty)]
以上是关于滚动窗口 REVISITED - 添加窗口滚动数量作为参数 - 向前分析的主要内容,如果未能解决你的问题,请参考以下文章
随着时间的推移,使用熊猫计算滚动窗口中唯一 id 的特定条件事件的数量
11.Flink四大基石Window窗口的分类Flink提供了很多各种场景用的WindowAssigner基于时间的滚动和滑动基于时间的滚动和滑动窗口基于数量的滚动和滑动
11.Flink四大基石Window窗口的分类Flink提供了很多各种场景用的WindowAssigner基于时间的滚动和滑动基于时间的滚动和滑动窗口基于数量的滚动和滑动