在 R 中将数据帧随机采样为 3 组
Randomly sample data frame into 3 groups in R
Objective:随机将一个数据帧分成3个样本。
- 一个样本占行数的 60%
- 其他两个样本占行数的 20%
- 样本不应与其他样本重复(即未经替换的样本)。
这是一个笨拙的解决方案:
allrows <- 1:nrow(mtcars)
set.seed(7)
trainrows <- sample(allrows, replace = F, size = 0.6*length(allrows))
test_cvrows <- allrows[-trainrows]
testrows <- sample(test_cvrows, replace=F, size = 0.5*length(test_cvrows))
cvrows <- test_cvrows[-which(test_cvrows %in% testrows)]
train <- mtcars[trainrows,]
test <- mtcars[testrows,]
cvr <- mtcars[cvrows,]
一定有更简单的东西,也许在一个包中。 dplyr
具有 sample_frac
功能,但它似乎针对单个样本,而不是拆分成多个样本。
接近,但不是这个问题的完整答案:
Random Sample with multiple probabilities in R
您需要精确分区吗?如果没有,
set.seed(7)
ss <- sample(1:3,size=nrow(mtcars),replace=TRUE,prob=c(0.6,0.2,0.2))
train <- mtcars[ss==1,]
test <- mtcars[ss==2,]
cvr <- mtcars[ss==3,]
应该做。
或者,正如@Frank 在评论中所说,您可以split()
将原始数据保留为列表的元素:
mycars <- setNames(split(mtcars,ss), c("train","test","cvr"))
不是最漂亮的解决方案(尤其是对于较大的样本),但它确实有效。
n = nrow(mtcars)
#use different rounding for differet sizes/proportions
times =rep(1:3,c(0.6*n,0.2*n,0.2*n))
ntimes = length(times)
if (ntimes < n)
times = c(times,sample(1:3,n-ntimes,prob=c(0.6,0.2,0.2),replace=FALSE))
sets = sample(times)
df1 = mtcars[sets==1,]
df2 = mtcars[sets==2,]
df3 = mtcars[sets==3,]
没有替换的选项
使用插入符号包。
library(caret)
inTrain <- createDataPartition(mtcars$mpg, p = 0.6, list = FALSE)
train <- mtcars[inTrain, ]
inTest <- createDataPartition(mtcars$mpg[-inTrain], list = FALSE)
test <- mtcars[-inTrain,][inTest, ]
cvr <- mtcars[-inTrain,][-inTest, ]
基础包。
## splitData
# y column of data to create split on
# p list of percentage split
splitData <- function(y, p = c(0.5)){
if(sum(p) > 1){
stop("sum of p cannot exceed 1")
}
rows <- 1:length(y)
res <- list()
n_sample = round(length(rows) * p)
for( size in n_sample){
inSplit <- sample.int(length(rows), size)
res <- c(res, list(rows[inSplit]))
rows <- rows[-inSplit]
}
if(sum(as.matrix(p)) < 1){
res <- c(res, list(rows))
}
res
}
split_example_2 <- splitData(mtcars$mpg, p = c(0.6, 0.2))
split_example_3 <- splitData(mtcars$mpg)
如果您想获得每个组的精确且可重现的数字(尽可能接近您可以达到的比例,请记住组大小必须是整数),而不是让组大小随机变化每次执行随机拆分时,请尝试:
sample_size <- nrow(mtcars)
set_proportions <- c(Training = 0.6, Validation = 0.2, Test = 0.2)
set_frequencies <- diff(floor(sample_size * cumsum(c(0, set_proportions))))
mtcars$set <- sample(rep(names(set_proportions), times = set_frequencies))
然后你可以简单地通过
拆分成一个数据帧列表
mtcars <- split(mtcars, mtcars$set)
所以例如验证集的数据框现在作为 mtcars$Validation
访问,或者您可以拆分为单独的数据框:
mtcars_train <- mtcars[mtcars$set == "Training", ]
mtcars_validation <- mtcars[mtcars$set == "Validation", ]
mtcars_test <- mtcars[mtcars$set == "Test", ]
在某些情况下,例如本例,您不能将数据准确地拆分为 60%、20%、20%,但此方法保证两个 20% 集的大小不应超过一个彼此:
> set_frequencies
Training Validation Test
19 6 7
检查它是否按预期工作:
> table(mtcars$set)
Test Training Validation
7 19 6
(基于 Ben Bolker 的回答和 liori 的评论。)
Objective:随机将一个数据帧分成3个样本。
- 一个样本占行数的 60%
- 其他两个样本占行数的 20%
- 样本不应与其他样本重复(即未经替换的样本)。
这是一个笨拙的解决方案:
allrows <- 1:nrow(mtcars)
set.seed(7)
trainrows <- sample(allrows, replace = F, size = 0.6*length(allrows))
test_cvrows <- allrows[-trainrows]
testrows <- sample(test_cvrows, replace=F, size = 0.5*length(test_cvrows))
cvrows <- test_cvrows[-which(test_cvrows %in% testrows)]
train <- mtcars[trainrows,]
test <- mtcars[testrows,]
cvr <- mtcars[cvrows,]
一定有更简单的东西,也许在一个包中。 dplyr
具有 sample_frac
功能,但它似乎针对单个样本,而不是拆分成多个样本。
接近,但不是这个问题的完整答案: Random Sample with multiple probabilities in R
您需要精确分区吗?如果没有,
set.seed(7)
ss <- sample(1:3,size=nrow(mtcars),replace=TRUE,prob=c(0.6,0.2,0.2))
train <- mtcars[ss==1,]
test <- mtcars[ss==2,]
cvr <- mtcars[ss==3,]
应该做。
或者,正如@Frank 在评论中所说,您可以split()
将原始数据保留为列表的元素:
mycars <- setNames(split(mtcars,ss), c("train","test","cvr"))
不是最漂亮的解决方案(尤其是对于较大的样本),但它确实有效。
n = nrow(mtcars)
#use different rounding for differet sizes/proportions
times =rep(1:3,c(0.6*n,0.2*n,0.2*n))
ntimes = length(times)
if (ntimes < n)
times = c(times,sample(1:3,n-ntimes,prob=c(0.6,0.2,0.2),replace=FALSE))
sets = sample(times)
df1 = mtcars[sets==1,]
df2 = mtcars[sets==2,]
df3 = mtcars[sets==3,]
没有替换的选项
使用插入符号包。
library(caret)
inTrain <- createDataPartition(mtcars$mpg, p = 0.6, list = FALSE)
train <- mtcars[inTrain, ]
inTest <- createDataPartition(mtcars$mpg[-inTrain], list = FALSE)
test <- mtcars[-inTrain,][inTest, ]
cvr <- mtcars[-inTrain,][-inTest, ]
基础包。
## splitData
# y column of data to create split on
# p list of percentage split
splitData <- function(y, p = c(0.5)){
if(sum(p) > 1){
stop("sum of p cannot exceed 1")
}
rows <- 1:length(y)
res <- list()
n_sample = round(length(rows) * p)
for( size in n_sample){
inSplit <- sample.int(length(rows), size)
res <- c(res, list(rows[inSplit]))
rows <- rows[-inSplit]
}
if(sum(as.matrix(p)) < 1){
res <- c(res, list(rows))
}
res
}
split_example_2 <- splitData(mtcars$mpg, p = c(0.6, 0.2))
split_example_3 <- splitData(mtcars$mpg)
如果您想获得每个组的精确且可重现的数字(尽可能接近您可以达到的比例,请记住组大小必须是整数),而不是让组大小随机变化每次执行随机拆分时,请尝试:
sample_size <- nrow(mtcars)
set_proportions <- c(Training = 0.6, Validation = 0.2, Test = 0.2)
set_frequencies <- diff(floor(sample_size * cumsum(c(0, set_proportions))))
mtcars$set <- sample(rep(names(set_proportions), times = set_frequencies))
然后你可以简单地通过
拆分成一个数据帧列表mtcars <- split(mtcars, mtcars$set)
所以例如验证集的数据框现在作为 mtcars$Validation
访问,或者您可以拆分为单独的数据框:
mtcars_train <- mtcars[mtcars$set == "Training", ]
mtcars_validation <- mtcars[mtcars$set == "Validation", ]
mtcars_test <- mtcars[mtcars$set == "Test", ]
在某些情况下,例如本例,您不能将数据准确地拆分为 60%、20%、20%,但此方法保证两个 20% 集的大小不应超过一个彼此:
> set_frequencies
Training Validation Test
19 6 7
检查它是否按预期工作:
> table(mtcars$set)
Test Training Validation
7 19 6
(基于 Ben Bolker 的回答和 liori 的评论。)