如何确保一个分区包含来自因子每个水平的代表性观测数据?

7

我编写了一个小函数来将我的数据集分成训练和测试集。然而,当处理因子变量时,我遇到了麻烦。在代码的模型验证阶段,如果模型是建立在没有每个因子级别的代表性的数据集上,我会收到一个错误。我该如何修复partition()函数以包括因子变量的每个级别至少有一个观察值?

test.df <- data.frame(a = sample(c(0,1),100, rep = T),
                      b = factor(sample(letters, 100, rep = T)),
                      c = factor(sample(c("apple", "orange"), 100, rep = T)))

set.seed(123)
partition <- function(data, train.size = .7){
  train <- data[sample(1:nrow(data), round(train.size*nrow(data)), rep= FALSE), ]
  test <- data[-as.numeric(row.names(train)), ]
  partitioned.data <- list(train = train, test = test)
  return(partitioned.data)
}

part.data <- partition(test.df)
table(part.data$train[,'b'])
table(part.data$test[,'b'])

编辑 - 使用“caret”包和createDataPartition()新功能:

partition <- function(data, factor=NULL, train.size = .7){
  if (("package:caret" %in% search()) == FALSE){
    stop("Install and Load 'caret' package")
  }
  if (is.null(factor)){
    train.index <- createDataPartition(as.numeric(row.names(data)),
                                       times = 1, p = train.size, list = FALSE)
    train <- data[train.index, ]
    test <- data[-train.index, ]
  }
  else{
    train.index <- createDataPartition(factor,
                                       times = 1, p = train.size, list = FALSE)
    train <- data[train.index, ]
    test <- data[-train.index, ]
  }
  partitioned.data <- list(train = train, test = test)
  return(partitioned.data)
}

我知道这并不回答你的问题,但是基于这么少的观测值来对因子变量进行条件处理真的是个好主意吗?这些估计值很可能会非常不精确,可能会使你的样本外预测变得更糟而不是更好。 - RoyalTS
你说得对,这确实是个不好的想法。但是,在实际应用中,我永远不会在如此小的数据集上使用这个函数。我将其缩小,以便分区的test.df几乎肯定会有一些因子没有观察值。 - zap2008
我有同样的问题,但似乎第二个“partition”函数定义仅适用于一次一个因子。我理解你的问题是关于在“train”数据集中拥有一个包含输入列“b”和“c”的所有因子级别的分区,但“createDataPartition”仅适用于一列,例如:“partition(test.df,factor = test.df [,c(“b”,“c”)])”无法工作。 - David Leal
1个回答

6
尝试使用caret包,特别是函数createDataPartition()。它应该能够做到你需要的功能,可以从CRAN获取,主页在这里:caret - 数据拆分
我提到的函数部分是我一段时间以前在网络上找到的代码,然后稍微修改了一下,以更好地处理边缘情况(比如当你请求一个大于集合的样本大小或子集时)。
stratified <- function(df, group, size) {
  # USE: * Specify your data frame and grouping variable (as column
  # number) as the first two arguments.
  # * Decide on your sample size. For a sample proportional to the
  # population, enter "size" as a decimal. For an equal number
  # of samples from each group, enter "size" as a whole number.
  #
  # Example 1: Sample 10% of each group from a data frame named "z",
  # where the grouping variable is the fourth variable, use:
  #
  # > stratified(z, 4, .1)
  #
  # Example 2: Sample 5 observations from each group from a data frame
  # named "z"; grouping variable is the third variable:
  #
  # > stratified(z, 3, 5)
  #
  require(sampling)
  temp = df[order(df[group]),]
  colsToReturn <- ncol(df)

  #Don't want to attempt to sample more than possible
  dfCounts <- table(df[group])
  if (size > min(dfCounts)) {
    size <- min(dfCounts)
  }



  if (size < 1) {
    size = ceiling(table(temp[group]) * size)
  } else if (size >= 1) {
    size = rep(size, times=length(table(temp[group])))
  }
  strat = strata(temp, stratanames = names(temp[group]),
                 size = size, method = "srswor")
  (dsample = getdata(temp, strat))

  dsample <- dsample[order(dsample[1]),]
  dsample <- data.frame(dsample[,1:colsToReturn], row.names=NULL)
  return(dsample)

}

我会去看看。我以前听说过它,但从未使用过。 - zap2008
告诉我。我还有另一个函数的代码可以给你。 - Tommy Levi
在提供代码时,最好尝试说明“从哪里”或“从谁”获取了该代码,“可能是网络上的某个地方”。 - A5C1D2H2I1M1N2O1R2T1
是的,我在这个问题发布一年前就有了这段代码,然后我修改了原始代码,变成了你现在看到的样子。我认为这个答案比我之前引用但现在已经丢失的内容更有用。 - Tommy Levi
1
我是原始作者,你很可能是从SO的答案中得到这个版本的,因为那是我发布使用“sampling”包的版本的地方。自那以后,有两个改进版本:一个data.frame版本一个data.table版本(后者需要最新的“data.table”开发版本,但速度非常快)。 - A5C1D2H2I1M1N2O1R2T1
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接