为循环中的交叉验证准备 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.train
和 samples.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 部分
如果我没理解错的话,这就是您要查找的内容(减去字符串)。我还介绍了如何将它与实际数据一起使用。
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
我正在尝试建立测试和训练组来进行交叉验证。我总共有 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.train
和 samples.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 部分
如果我没理解错的话,这就是您要查找的内容(减去字符串)。我还介绍了如何将它与实际数据一起使用。
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