问题 在rsample中结合滚动原点预测重采样和组V形交叉验证


我想用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

2334
2017-08-20 07:34


起源



答案:


正如@missuse解决方案的评论中所讨论的,github问题中记录了实现此目的的方法: https://github.com/tidymodels/rsample/issues/42

从本质上讲,这个想法是首先嵌套你的“块”然后 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

创建于2018-08-28由 代表包 (v0.2.0)。


7
2017-08-28 16:41





如果我理解正确,您希望创建重新采样,您可以在一个月内对所有数据进行培训,并在每个月对该月进行评估。 我不是 rsample 用户,但这可以通过基础R很容易实现。这是一种方法:

按月将数据拆分为列表

df <- split(some_data, some_data$month)

沿着定义列车和测试集的列表元素进行激励

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个元素的列表。


3
2017-08-20 08:50



谢谢你回复我。事实上,我想使用rsample,因为它具有对象设置,我的代码的其余部分已经构建。我还问rstudio和Max Kuhn,那个包的作者在那里工作。也许他也会帮忙。谢谢! - Richard
但它实际上是一个非常好的解决方案! - Richard
@Richard代码很容易改变,以获得可以使用的训练和测试索引 caret。如果需要,我可以证明。我也会研究一下 rsample 包装,如果我得到一些工作,我会更新答案。 - missuse
对于rsample这将是伟大的。对于插入符号,我现在不需要它。不幸的是,内置的keras模型不包含我想调整的所有参数(特别是我不确定多个隐藏层)。所以我想在外面使用rsample和tfruns。 - Richard
即使我没有运气提供所要求的解决方案,我也想回复你。我检查了 rsample我没有看到使用这个包进行分层时间序列交叉验证的方法。如果我真的不在包中,我相信如果你很好地问他,topepo会愿意加入这个功能。编辑:事实上这似乎是这样的: github.com/tidymodels/rsample/issues/42。看起来它会在完成时内置, - missuse


答案:


正如@missuse解决方案的评论中所讨论的,github问题中记录了实现此目的的方法: https://github.com/tidymodels/rsample/issues/42

从本质上讲,这个想法是首先嵌套你的“块”然后 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

创建于2018-08-28由 代表包 (v0.2.0)。


7
2017-08-28 16:41





如果我理解正确,您希望创建重新采样,您可以在一个月内对所有数据进行培训,并在每个月对该月进行评估。 我不是 rsample 用户,但这可以通过基础R很容易实现。这是一种方法:

按月将数据拆分为列表

df <- split(some_data, some_data$month)

沿着定义列车和测试集的列表元素进行激励

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个元素的列表。


3
2017-08-20 08:50



谢谢你回复我。事实上,我想使用rsample,因为它具有对象设置,我的代码的其余部分已经构建。我还问rstudio和Max Kuhn,那个包的作者在那里工作。也许他也会帮忙。谢谢! - Richard
但它实际上是一个非常好的解决方案! - Richard
@Richard代码很容易改变,以获得可以使用的训练和测试索引 caret。如果需要,我可以证明。我也会研究一下 rsample 包装,如果我得到一些工作,我会更新答案。 - missuse
对于rsample这将是伟大的。对于插入符号,我现在不需要它。不幸的是,内置的keras模型不包含我想调整的所有参数(特别是我不确定多个隐藏层)。所以我想在外面使用rsample和tfruns。 - Richard
即使我没有运气提供所要求的解决方案,我也想回复你。我检查了 rsample我没有看到使用这个包进行分层时间序列交叉验证的方法。如果我真的不在包中,我相信如果你很好地问他,topepo会愿意加入这个功能。编辑:事实上这似乎是这样的: github.com/tidymodels/rsample/issues/42。看起来它会在完成时内置, - missuse