为循环中的交叉验证准备 Test/Train 组

Preparing Test/Train sets for Cross Validaton in a loop

我正在尝试建立测试和训练组来进行交叉验证。我总共有 95 个个人 ID,并尝试像这样完成任务:

# create 95 unique IDs as individuals
set.seed(1)
indv <- stringi::stri_rand_strings(95, 4)

# specify Kfold
n.folds <- 5

folds <- cut(1:length(indv), breaks = n.folds, labels = FALSE)
# randomise the folds
folds <- sample(folds, length(folds)) 

samples.train <- list()
samples.test <- list()
foldSet <- list()

kfold.df <- data.frame("IID" = indv)

for (f in 1:n.folds) {
          samples.train[[f]] <- indv[folds != f]
          samples.test[[f]] <- indv[folds == f]

# replace to x (test) if the corresponding value is TRUE, and to y (train) if it is FALSE.
foldSet[[f]] <- ifelse(kfold.df$IID %in% 
                  samples.test[[f]], "test", "train")

# combine foldSet to datafarme.
kfold.df[[f]] <- cbind(kfold.df, foldSet[[f]])
} 

目标是准备 5 个测试和训练样本集来进行建模。但是我遇到了这个错误信息:

Error in data.frame(..., check.names = FALSE) : 
arguments imply differing number of rows: 95, 2

此外,尽管 samples.trainsamples.test 是正确的,但 foldSet 输出并不像预期的那样。你能帮我让这个循环工作吗?

更新: 这是在创建 foldSet 时不使用通配符的 for 循环:

for (f in 1:n.folds) {
samples.train[[f]] <- indv[folds != f]
samples.test[[f]] <- indv[folds == f]

foldSet <<- ifelse(kfold.df$IID %in% samples.test[[f]], "test", "train")
# combine foldSet to datafarme.
kfold.df <<- cbind(kfold.df, foldSet)
}

通过执行循环,您会发现 kfold.df 作为一个数据框列出了所有五个折叠 test/train 随机集。我希望每次迭代都创建与 f 相对应的测试集和训练集,因此,在五次迭代后,我将可以访问每个折叠的 Training/Testing 集以进行循环内的下一个操作,例如 kfold.df[foldSet == "train", "IID"]。我需要这个访问权限,因为我想用它来根据每个折叠的训练和测试 invd 对另一个更大的矩阵进行子集化,为应用于回归模型做准备。这就是为什么我使用 foldSet 的通配符来使循环能够自行创建但我未能管理它。

我认为你可能把事情复杂化了(这是我一直在做的事情...)

你不需要竭尽全力去做你想做的事。这个答案分为三个部分。

  1. 构建您正在寻找的数据框(我认为!)
  2. 为什么你真的不需要构建这个数据框
  3. 为什么不使用已有的东西呢?

第 1 部分

如果我没理解错的话,这就是您要查找的内容(减去字符串)。我还介绍了如何将它与实际数据一起使用。

library(tidyverse)

giveMe <- function(rowCt, nfolds){
  # set.seed(235) # removed seed after establishing working function to incite
  #  the expected randomness

  folds <- cut(1:rowCt, breaks = nfolds, labels = F)
  # randomise the folds
  folds <- sample(folds, length(folds)) 
  # create the folds' sets
  kfold.df <- map_dfc(1:nfolds,
                      ~ifelse(folds != .x, T, F)) %>% 
  setNames(., paste0("foldSet_",1:nfolds)) %>%  # name each field
  add_column(IID = 1:rowCt, .before = 1) # add indices to the left

  return(kfold.df) # return a data frame
}

given <- giveMe(95, 5)

giveMore <- giveMe(nrow(iris), 5) # uses the built-in iris data set

第 2 部分

您可以只创建随机折叠序列并将其与模型一起使用,您不需要将它们堆叠在数据框中。你必须循环遍历模型相同的次数,为什么不同时做呢?

folds <- sample(cut(1:nrow(iris), 5, # no seed-- random on purpose
                    labels = F))

tellMe <- map(1:5, # the folds start in col 2
              ~lm(Sepal.Length~., 
                  iris[ifelse(folds != .x,
                              T, F), 
                       1:4])) # dropped 'Species' groups' issue

查看模型性能:

map_dfr(1:5, .f = function(x){
  y = tellMe[[x]]
  sigma = sigma(y)
  rsq = summary(y)$adj.r.squared
  c(sigma = sigma, rsq = rsq)
})
# # A tibble: 5 × 2
#   sigma   rsq
#   <dbl> <dbl>
# 1 0.334 0.844
# 2 0.309 0.869
# 3 0.302 0.846
# 4 0.330 0.847
# 5 0.295 0.872 

预测和检查测试性能

# create a list of the predictec values from the test data
showMe <- map(1:5,
              ~predict(tellMe[[.x]], 
                       iris[ifelse(folds == .x,
                                   T, F), 1:4]))

# Grab comparable metrics like those from the models
map_dfr(1:5,
        .f = function(x){
          A = iris[ifelse(folds == x, T, F), ]$Sepal.Length
          P = showMe[[x]]
          sigma = sqrt(sum((A - P)^2) / length(A))
          rsq = cor(A, P)^2
          c(sigma = sigma, rsq = rsq)
        })
# # A tibble: 5 × 2
#   sigma   rsq
#   <dbl> <dbl>
# 1 0.232 0.919
# 2 0.342 0.774
# 3 0.366 0.884
# 4 0.250 0.906
# 5 0.384 0.790 

第 3 部分

在这里,我将使用 caret 库。但是,还有很多其他选择。

library(caret)

set.seed(1)
# split training and testing 70/30%
tr <- createDataPartition(iris$Species, p = .7, list = F)

# set up 5-fold val
trC <- trainControl(method = "cv", number = 5)

# train the model
fit <- train(Sepal.Length~., iris[tr, ], 
             method = "lm", 
             trControl = trC)
summary(fit)
# truncated results best model:
# Residual standard error: 0.2754 on 39 degrees of freedom
# Multiple R-squared:  0.9062,  Adjusted R-squared:  0.8941 

fit.p <- predict(fit, iris[-tr,])
postResample(fit.p, iris[-tr, ]$Sepal.Length)
#      RMSE  Rsquared       MAE 
# 0.2795920 0.8925574 0.2302402  

如果您想查看每个折叠的性能,您也可以这样做。

fit$resample
#        RMSE  Rsquared       MAE Resample
# 1 0.3629901 0.7911634 0.2822708    Fold1
# 2 0.3680954 0.8888947 0.2960464    Fold2
# 3 0.3508317 0.8394489 0.2709989    Fold3
# 4 0.2548549 0.8954633 0.1960375    Fold4
# 5 0.3396910 0.8661239 0.3187768    Fold5