时间序列-数据拆分和模型评估

20

我尝试使用机器学习来基于时间序列数据进行预测。在 stackoverflow 的一个问题中(CARET 包中 createTimeSlices 函数的使用示例),展示了如何使用 createTimeSlices 进行模型训练和参数调整的交叉验证:

    library(caret)
    library(ggplot2)
    library(pls)
    data(economics)
    myTimeControl <- trainControl(method = "timeslice",
                                  initialWindow = 36,
                                  horizon = 12,
                                  fixedWindow = TRUE)

    plsFitTime <- train(unemploy ~ pce + pop + psavert,
                        data = economics,
                        method = "pls",
                        preProc = c("center", "scale"),
                        trControl = myTimeControl)

我的理解是:

  1. 我需要将数据分成训练集和测试集。
  2. 使用训练集进行参数调整。
  3. 使用R2、RMSE等方法在测试集上评估获得的模型。

由于我的数据是时间序列,所以我认为不能使用自助法将数据拆分为训练集和测试集。因此,我的问题是:我是对的吗?如果是这样,如何使用createTimeSlices进行模型评估?


1
如果您定义一下“模型评估”的含义会更有帮助。 - topepo
我已经编辑了我的问题。现在或许更容易理解了? - Jot eN
1
  1. 需要安装“pls”包。
  2. 如果您只关心样本的顺序(而不是特定的月份或年份),您可以为它们分配一个数字ID,仍然可以使用自助法。
  3. 为什么不使用像glm这样简单的东西呢?
- James Tobin
3个回答

43

请注意,您发布的原始问题已经考虑到了时间切片问题,因此您不必手动创建时间切片。

但是,以下是如何使用 createTimeSlices 对数据进行拆分,然后将其用于训练和测试模型的方法。

步骤0:设置数据和 trainControl :(来自您的问题)

library(caret)
library(ggplot2)
library(pls)

data(economics)

步骤1:为数据索引创建时间片段:

timeSlices <- createTimeSlices(1:nrow(economics), 
                   initialWindow = 36, horizon = 12, fixedWindow = TRUE)

这将创建一个训练时间片和测试时间片的列表。

> str(timeSlices,max.level = 1)
## List of 2
## $ train:List of 431
##   .. [list output truncated]
## $ test :List of 431
##   .. [list output truncated]

为了更容易地理解,我将它们保存在不同的变量中:

trainSlices <- timeSlices[[1]]
testSlices <- timeSlices[[2]]

步骤2:在第一个trainSlices上进行训练:

plsFitTime <- train(unemploy ~ pce + pop + psavert,
                    data = economics[trainSlices[[1]],],
                    method = "pls",
                    preProc = c("center", "scale"))

第三步:在第一个 testSlices 上进行测试:

pred <- predict(plsFitTime,economics[testSlices[[1]],])

步骤4:绘图:

true <- economics$unemploy[testSlices[[1]]]

plot(true, col = "red", ylab = "true (red) , pred (blue)", ylim = range(c(pred,true)))
points(pred, col = "blue") 

接下来,您可以对所有的切片执行此操作:

for(i in 1:length(trainSlices)){
  plsFitTime <- train(unemploy ~ pce + pop + psavert,
                      data = economics[trainSlices[[i]],],
                      method = "pls",
                      preProc = c("center", "scale"))
  pred <- predict(plsFitTime,economics[testSlices[[i]],])
  
  
  true <- economics$unemploy[testSlices[[i]]]
  plot(true, col = "red", ylab = "true (red) , pred (blue)", 
            main = i, ylim = range(c(pred,true)))
  points(pred, col = "blue") 
}

如前所述,这种时间切片是由您的原始函数一步完成的:

> myTimeControl <- trainControl(method = "timeslice",
+                               initialWindow = 36,
+                               horizon = 12,
+                               fixedWindow = TRUE)
> 
> plsFitTime <- train(unemploy ~ pce + pop + psavert,
+                     data = economics,
+                     method = "pls",
+                     preProc = c("center", "scale"),
+                     trControl = myTimeControl)
> plsFitTime
Partial Least Squares 

478 samples
  5 predictors

Pre-processing: centered, scaled 
Resampling: Rolling Forecasting Origin Resampling (12 held-out with a fixed window) 

Summary of sample sizes: 36, 36, 36, 36, 36, 36, ... 

Resampling results across tuning parameters:

  ncomp  RMSE  Rsquared  RMSE SD  Rsquared SD
  1      1080  0.443     796      0.297      
  2      1090  0.43      845      0.295      

RMSE was used to select the optimal model using  the smallest value.
The final value used for the model was ncomp = 1. 

希望这可以帮到您!


1
那么,如果我使用caret train control,就不需要在之前将数据分成训练集和测试集了吗? - Jot eN
3
没问题。该脚本的作者还表示他们将在下一版本中改进其文档。 - Shambho

5
Shambho的答案提供了如何使用caret包和TimeSlices的不错示例,但从建模技术角度来看可能会误导读者。因此,为了不误导未来想要在时间序列上使用caret包进行预测建模的读者(我并不是指自回归模型),我想强调一些事情。
时间序列数据的问题在于如果不小心就会出现前瞻性偏差。在这种情况下,经济数据集将其数据对齐到其经济报告日期而非发布日期,这在实际应用中从未发生(经济数据点具有不同的时间戳)。失业数据可能比其他指标的发布日期晚两个月,这将在Shambho的示例中引入模型偏差。
其次,这个示例仅涉及描述统计而非预测(预测),因为我们想要预测的数据(失业率)没有被正确地滞后。它仅仅训练一个模型来最好地解释失业率的变化(在这种情况下也是一个平稳时间序列,在建模过程中产生各种问题),基于相同的经济报告日期的预测变量。
最后,这个示例中的12个月预测期并不是Hyndman在他的示例中所做的真正的多期预测。

Hyndman关于时间序列交叉验证的文章


2

实际上,你可以这样做!

首先,让我给你提供一篇关于这个主题的学术文章

在R中:

使用caret包,可以使用createResample创建简单的自助样本,使用createFolds从一组数据生成平衡的交叉验证分组。因此,您可能需要使用createResample。以下是其用法示例:

data(oil)
createDataPartition(oilType, 2)

x <- rgamma(50, 3, .5)
inA <- createDataPartition(x, list = FALSE)

plot(density(x[inA]))
rug(x[inA])

points(density(x[-inA]), type = "l", col = 4)
rug(x[-inA], col = 4)

createResample(oilType, 2)

createFolds(oilType, 10)
createFolds(oilType, 5, FALSE)

createFolds(rnorm(21))

createTimeSlices(1:9, 5, 1, fixedWindow = FALSE)
createTimeSlices(1:9, 5, 1, fixedWindow = TRUE)
createTimeSlices(1:9, 5, 3, fixedWindow = TRUE)
createTimeSlices(1:9, 5, 3, fixedWindow = FALSE)

createResample函数中,您可以看到的值是数据和要创建的分区数,在这种情况下为2。您还可以指定结果是否应存储为列表,使用list = TRUElist = FALSE
此外,caret包含一个名为createTimeSlices的函数,可以创建此类型拆分的索引。
此类型拆分的三个参数是:
  • initialWindow:每个训练集样本中连续值的初始数量
  • horizon:测试集样本中连续值的数量
  • fixedWindow:逻辑值:如果为FALSE,则训练集始终从第一个样本开始,并且训练集大小将随数据拆分而变化。
用法:
createDataPartition(y, 
                    times = 1,
                    p = 0.5,
                    list = TRUE,
                    groups = min(5, length(y)))
createResample(y, times = 10, list = TRUE)
createFolds(y, k = 10, list = TRUE, returnTrain = FALSE)
createMultiFolds(y, k = 10, times = 5)
createTimeSlices(y, initialWindow, horizon = 1, fixedWindow = TRUE)

Sources:

http://caret.r-forge.r-project.org/splitting.html

http://eranraviv.com/blog/bootstrapping-time-series-r-code/

http://rgm3.lab.nig.ac.jp/RGM/R_rdfile?f=caret/man/createDataPartition.Rd&d=R_CC

CARET. 数据拆分与trainControl之间的关系


你能给一个R语言的例子吗? - Jot eN
@JoteN 当然。这两个链接中有示例,但我会直接将它们添加到解决方案中。 - Hack-R
@JoteN 实际上,是的,我想他们提供了其他函数的代码示例,然后讨论了createResample并展示了结果。我为您找到了一个示例和一般用法示例。 - Hack-R
那个能满足你的需求吗? - Hack-R
是的,除了最重要的一点 - 如何使用 createTimeSlices 来拆分适合模型训练和模型评估的数据? - Jot eN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接