将滚动起点预测重采样和组V折叠交叉验证相结合在rsample中

15

我希望使用R软件包rsample生成数据的重新抽样。

该软件包提供了函数rolling_origin,以生成保留数据时间序列结构的重新抽样。这意味着训练数据(在软件包中称为analysis)始终在测试数据(assessment)之前。

另一方面,我希望对数据执行块采样。这意味着在抽样过程中将行的组保持在一起。可以使用函数group_vfold_cv来完成此操作。其中一些可能与月份相关。例如,我们要进行时间序列交叉验证,始终将月份保持在一起。

是否有办法在rsample中结合这两种方法?

我分别给出每个过程的示例:

## generate some data
library(tidyverse)
library(lubridate)
library(rsample)
my_dates = seq(as.Date("2018/1/1"), as.Date("2018/8/20"), "days")
some_data = data_frame(dates = my_dates) 
some_data$values = runif(length(my_dates))
some_data = some_data %>% mutate(month = as.factor(month(dates))) 

这提供了以下形式的数据

 A tibble: 232 x 3
   dates      values month 
   <date>      <dbl> <fctr>
 1 2018-01-01 0.235  1     
 2 2018-01-02 0.363  1     
 3 2018-01-03 0.146  1     
 4 2018-01-04 0.668  1     
 5 2018-01-05 0.0995 1     
 6 2018-01-06 0.163  1     
 7 2018-01-07 0.0265 1     
 8 2018-01-08 0.273  1     
 9 2018-01-09 0.886  1     
10 2018-01-10 0.239  1  

然后,我们可以生成需要20周数据的样本,并在未来5周进行测试(参数skip跳过一些额外的行):

rolling_origin_resamples <- rolling_origin(
  some_data,
  initial    = 7*20,
  assess     = 7*5,
  cumulative = TRUE,
  skip       = 7
)
我们可以使用以下代码来检查数据并发现没有重叠:
rolling_origin_resamples$splits[[1]] %>% analysis %>% tail
# A tibble: 6 x 3
  dates       values month 
  <date>       <dbl> <fctr>
1 2018-05-15 0.678   5     
2 2018-05-16 0.00112 5     
3 2018-05-17 0.339   5     
4 2018-05-18 0.0864  5     
5 2018-05-19 0.918   5     
6 2018-05-20 0.317   5 

### test data of first split:
rolling_origin_resamples$splits[[1]] %>% assessment
# A tibble: 6 x 3
  dates      values month 
  <date>      <dbl> <fctr>
1 2018-05-21  0.912 5     
2 2018-05-22  0.403 5     
3 2018-05-23  0.366 5     
4 2018-05-24  0.159 5     
5 2018-05-25  0.223 5     
6 2018-05-26  0.375 5   

或者我们可以按月份拆分:

## sampling by month:
gcv_resamples = group_vfold_cv(some_data, group = "month", v = 5)
gcv_resamples$splits[[1]]  %>% analysis %>% select(month) %>% summary
gcv_resamples$splits[[1]] %>% assessment %>% select(month) %>% summary
3个回答

11

根据@missuse的解决方案评论所讨论的,实现此目标的方法已在github问题文档中记录:https://github.com/tidymodels/rsample/issues/42

基本上,首先将“blocks”进行嵌套,然后使用rolling_origin()可以让您在这些块之间滚动,保持完整块不变。

library(dplyr)
library(lubridate)
library(rsample)
library(tidyr)
library(tibble)

# same data generation as before
my_dates = seq(as.Date("2018/1/1"), as.Date("2018/8/20"), "days")
some_data = data_frame(dates = my_dates)
some_data$values = runif(length(my_dates))
some_data = some_data %>% mutate(month = as.factor(month(dates)))

# nest by month, then resample
rset <- some_data %>%
  group_by(month) %>%
  nest() %>%
  rolling_origin(initial = 1)

# doesn't show which month is which :(
rset
#> # Rolling origin forecast resampling 
#> # A tibble: 7 x 2
#>   splits       id    
#>   <list>       <chr> 
#> 1 <S3: rsplit> Slice1
#> 2 <S3: rsplit> Slice2
#> 3 <S3: rsplit> Slice3
#> 4 <S3: rsplit> Slice4
#> 5 <S3: rsplit> Slice5
#> 6 <S3: rsplit> Slice6
#> 7 <S3: rsplit> Slice7


# only January (31 days)
analysis(rset$splits[[1]])$data
#> [[1]]
#> # A tibble: 31 x 2
#>    dates      values
#>    <date>      <dbl>
#>  1 2018-01-01 0.373 
#>  2 2018-01-02 0.0389
#>  3 2018-01-03 0.260 
#>  4 2018-01-04 0.803 
#>  5 2018-01-05 0.595 
#>  6 2018-01-06 0.875 
#>  7 2018-01-07 0.273 
#>  8 2018-01-08 0.180 
#>  9 2018-01-09 0.662 
#> 10 2018-01-10 0.849 
#> # ... with 21 more rows


# only February (28 days)
assessment(rset$splits[[1]])$data
#> [[1]]
#> # A tibble: 28 x 2
#>    dates      values
#>    <date>      <dbl>
#>  1 2018-02-01 0.402 
#>  2 2018-02-02 0.556 
#>  3 2018-02-03 0.764 
#>  4 2018-02-04 0.134 
#>  5 2018-02-05 0.0333
#>  6 2018-02-06 0.907 
#>  7 2018-02-07 0.814 
#>  8 2018-02-08 0.0973
#>  9 2018-02-09 0.353 
#> 10 2018-02-10 0.407 
#> # ... with 18 more rows

这段文字是由 reprex 包 (v0.2.0) 在 2018 年 08 月 28 日创建的。


5

使用 tidyroll 也可以实现此功能,它是一个小型 R 包,提供了一组方便的函数用于处理时间序列数据和不规则时间片。

rolling_origin_nestedrolling_origin 的封装,具有许多良好的功能,包括允许用户选择滚动的单位(分钟、天、周、月等),起始日期/时间和结束日期/时间,以及是否暂时扩展数据,以便在 startend 之间预测所有观测值进行 assess 次数的评估。

# devtools::install_github("gacolitti/tidyroll")

library(tidyverse)
library(lubridate)
library(rsample)
library(tidyroll)

my_dates = seq(as.Date("2018/1/1"), as.Date("2018/8/20"), "days")
some_data = data.frame(dates = my_dates) 
some_data$values = runif(length(my_dates))

roll <- rolling_origin_nested(some_data,
                              time_var = "dates",
                              unit = "month",
                              start = "2018-01-01")
roll
#> # Rolling origin forecast resampling 
#> # A tibble: 7 x 2
#>   splits        id    
#>   <list>        <chr> 
#> 1 <split [1/1]> Slice1
#> 2 <split [2/1]> Slice2
#> 3 <split [3/1]> Slice3
#> 4 <split [4/1]> Slice4
#> 5 <split [5/1]> Slice5
#> 6 <split [6/1]> Slice6
#> 7 <split [7/1]> Slice7

analysis(roll$splits[[1]])$data[[1]] %>% tail
#> # A tibble: 6 x 2
#>   dates               values
#>   <dttm>               <dbl>
#> 1 2018-01-26 00:00:00 0.0929
#> 2 2018-01-27 00:00:00 0.536 
#> 3 2018-01-28 00:00:00 0.194 
#> 4 2018-01-29 00:00:00 0.600 
#> 5 2018-01-30 00:00:00 0.449 
#> 6 2018-01-31 00:00:00 0.754

assessment(roll$splits[[1]])$data[[1]] %>% head
#> # A tibble: 6 x 2
#>   dates               values
#>   <dttm>               <dbl>
#> 1 2018-02-01 00:00:00  0.945
#> 2 2018-02-02 00:00:00  0.733
#> 3 2018-02-03 00:00:00  0.626
#> 4 2018-02-04 00:00:00  0.585
#> 5 2018-02-05 00:00:00  0.303
#> 6 2018-02-06 00:00:00  0.767

还有一些其他的便捷函数,例如fit_rsample_nestedpredict_rsample_nested,可以方便地处理使用rolling_origin_nested创建的对象以及使用recipes进行数据预处理。

predict_rsample_nested的一个非常酷的功能是能够传递额外的recipe步骤来插值预测器的值,这取决于预测日期是否可用。


3
如果我理解正确,您想创建重新采样的数据,其中您会在所有数据中训练到特定月份,并在每个月对该月进行评估。我不是rsample用户,但这可以通过基本的R很容易实现。以下是一种方法:
将数据按月份拆分为列表
df <- split(some_data, some_data$month)

使用lapply沿列表元素定义训练和测试集

df <- lapply(seq_along(df)[-length(df)], function(x){
  train <- do.call(rbind, df[1:x])
  test <- df[x+1]
  return(list(train = train,
              test = test))

})

结果df是一个包含7个元素的列表,每个元素都包含一个训练数据框和一个测试数据框。


谢谢您再次回复我。实际上,我想使用rsample,因为它具有余下代码已经建立的对象设置。我也在RStudio问过,并且这个包的作者Max Kuhn在那里工作。也许他也会帮忙。谢谢! - Richi W
2
@Richard,这段代码很容易更改以获取可与caret一起使用的训练和测试索引。如果需要,我可以展示一下。我还会研究rsample包,如果有什么进展,我会更新答案。 - missuse
这将是很棒的。对于caret,我目前不需要它。不幸的是,keras内置模型不包含我想要调整的所有参数(尤其是关于多个隐藏层我不确定)。所以我考虑使用rsample和tfruns来在外部进行调整。 - Richi W
尽管我没有提供所需的解决方案,但我仍想回复您。我检查了 rsample,但我没有找到使用该程序包进行分层时间序列交叉验证的方法。如果该程序包确实缺少此功能,我相信 topepo 如果你好好地请他的话,他会愿意将其纳入该程序包中。编辑:实际上,这似乎就是它:https://github.com/tidymodels/rsample/issues/42。看起来在完成后,它将成为内置功能。 - missuse
谢谢,我会将这个问题链接到他们的示例。 - Richi W

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