在 R 中将数据帧随机采样为 3 组

Randomly sample data frame into 3 groups in R

Objective:随机将一个数据帧分成3个样本。

这是一个笨拙的解决方案:

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 的评论。)