【问题标题】:Partitioning data set in r based on multiple classes of observations基于多类观察对 r 中的数据集进行分区
【发布时间】:2012-11-12 06:12:27
【问题描述】:

我正在尝试对 R 中的数据集进行分区,2/3 用于训练,1/3 用于测试。我有一个分类变量和七个数值变量。每个观测值都被分类为 A、B、C 或 D。

为简单起见,假设分类变量 cl 是前 100 个观测值的 A,观测值 101 到 200 的 B,C 到 300,D 到 400。我试图得到一个分区A、B、C 和 D 中每一个的 2/3 的观察值(而不是简单地获得整个数据集的 2/3 的观察值,因为每个分类的数量可能不相等)。

当我尝试从数据子集(例如 sample(subset(data, cl=='A')))中采样时,会重新排序列而不是行。

总而言之,我的目标是从 A、B、C 和 D 中的每一个中获得 67 个随机观察作为我的训练数据,并将 A、B、C 和 D 中每个的剩余 33 个观察作为测试数据存储.我发现了一个与我非常相似的问题,但它没有考虑多个变量。

【问题讨论】:

    标签: r random partitioning


    【解决方案1】:

    实际上有一个很好的包 caret 用于处理机器学习问题,它包含一个函数 createDataPartition() 几乎可以从每个级别采样 2/3提供的因素:

    #2/3rds for training
    library(caret)
    inTrain = createDataPartition(df$yourFactor, p = 2/3, list = FALSE)
    dfTrain=df[inTrain,]
    dfTest=df[-inTrain,]
    

    【讨论】:

      【解决方案2】:

      这可能会更长,但我认为它更直观,可以在基础 R 中完成;)

      # create the data frame you've described
      x <-
          data.frame(
              cl = 
                  c( 
                      rep( 'A' , 100 ) ,
                      rep( 'B' , 100 ) ,
                      rep( 'C' , 100 ) ,
                      rep( 'D' , 100 ) 
                  ) ,
      
              othernum1 = rnorm( 400 ) ,
              othernum2 = rnorm( 400 ) ,
              othernum3 = rnorm( 400 ) ,
              othernum4 = rnorm( 400 ) ,
              othernum5 = rnorm( 400 ) ,
              othernum6 = rnorm( 400 ) ,
              othernum7 = rnorm( 400 ) 
          )
      
      # sample 67 training rows within classification groups
      training.rows <-
          tapply( 
              # numeric vector containing the numbers
              # 1 to nrow( x )
              1:nrow( x ) , 
      
              # break the sample function out by
              # the classification variable
              x$cl , 
      
              # use the sample function within
              # each classification variable group
              sample , 
      
              # send the size = 67 parameter
              # through to the sample() function
              size = 67 
          )
      
      # convert your list back to a numeric vector
      tr <- unlist( training.rows )
      
      # split your original data frame into two:
      
      # all the records sampled as training rows
      training.df <- x[ tr , ]
      
      # all other records (NOT sampled as training rows)
      testing.df <- x[ -tr , ]
      

      【讨论】:

      • 太棒了!我还没有听说过 unlist 功能。这似乎正是我想要的,而且比我最终做的要短。
      【解决方案3】:

      以下将在您的 data.frame 中添加一个带有值 "train""test"set 列:

      library(plyr)
      df <- ddply(df, "cl", transform, set = sample(c("train", "test"), length(cl),
                                                    replace = TRUE, prob = c(2, 1)))
      

      您可以使用基本的ave 函数获得类似的东西,但我发现ddply 对于这种特殊用途非常干净(可读)。

      然后您可以使用subset 函数拆分数据:

      train.data <- subset(df, set == "train")
      test.data  <- subset(df, set == "test")
      

      跟进:要将每个组精确地分成 2/3 和 1/3 大小,您可以使用:

      df <- ddply(df, "cl", transform,
                  set = sample(c(rep("train", round(2/3 * length(cl)),
                                 rep("test",  round(1/3 * length(cl)))))
      

      【讨论】:

      • 你的意思是变换,而不是总结?
      • 我可能错了,但看起来这可能不会每次都给我每组的 2/3。这不是说概率是2/3吗?我的问题是我每次都需要每组 67 个。
      • @Danny,你是对的。不过没什么大不了的,请参阅我的编辑。
      【解决方案4】:

      在构建我自己的函数以使用多个分层因素进行交叉验证的数据分区时遇到了这个问题。您可以通过将数据分成 3 个(或 N 个)大小相等的部分来构建此类数据集,同时将每个层内的观察值平均划分为这些部分,然后选择三分之一作为测试集,然后将其余部分组合为训练集。我会处理 R 中的诸如 list 元素。

      这是我使用支持多个分层因素的基本包构建的一个函数,表示为您希望作为分层的字段的列号或列名(mtcars 数据集示例)。我认为它在功能上与 ddply 非常相似,除了您还可以使用列号并且生成的子集在 list 中给出:

      # Function that partitions data into a number of equally (or almost-equally) sized bins that do not overlap, and returns the data bins as a list
      # Useful for cross validation
      partition_data <- function(
          # Data frame to partition (default example: mtcars data, assuming rows correspond to observations)
          dat = mtcars,
          # Number of equally sized bins to partition to (default here: 2 bins)
          bins = 2,
          # Stratification element, homogeneous subpopulations according to a column that should be subsampled,
          # Observations within a substrata are divided equally to the partitioned bins
          stratum = NA
      ){
          # Total number of observations
          nobs <- dim(dat)[1]
          # Allocation vector, to be used for randomly distributing the samples to the bins
          loc <- rep(1:bins, times=ceiling(nobs/bins))[1:nobs]
      
      
          # If the dataset is stratified, each subpopulation is distributed equally to the bins, otherwise the whole population is the "subpopulation"
          if(missing(stratum)){
              pops <- list(sample(1:dim(dat)[1]))
          }else{
              uniqs <- na.omit(as.matrix(unique(dat[,stratum])))
              pops <- list()
              for(i in 1:nrow(uniqs)){
                  # If some of the stratified fields include NA-values, these will not be included in the sampling
                  w <- apply(as.matrix(dat[,stratum]), MARGIN=1, FUN=function(x) all(x==uniqs[i,]))
                  pops[[i]] <- sample(which(w))
              }
          }
          indices <- vector(length=nobs)
          # Assign the group indices according to permutated samples within each subpopulation
          indices[unlist(pops)] <- loc
          # Assign observations to separate locations in a list
          partitioned_data <- lapply(unique(indices), FUN=function(x) dat[x==indices,])
          # Return the result
          partitioned_data
      }
      

      它是如何工作的示例;在这个假设的例子中,人们希望因子“vs”和“am”在所有的 bin 中得到同样的表示:

      set.seed(1)
      
      # Stratified sampling, so that combinations of binary covariates vs = {0,1} & am = {0,1} appear equally over the randomized bins of data
      pt <- partition_data(mtcars, stratum=c("vs", "am"), bins=3)
      
      # Instances are distributed equally
      lapply(pt, FUN=function(x) table(x[,c("vs","am")]))
      #> lapply(pt, FUN=function(x) table(x[,c("vs","am")]))
      #[[1]]
      #   am
      #vs  0 1
      #  0 4 2
      #  1 3 2
      #
      #[[2]]
      #   am
      #vs  0 1
      #  0 4 2
      #  1 2 3
      #
      #[[3]]
      #   am
      #vs  0 1
      #  0 4 2
      #  1 2 2
      
      # 10 or 11 samples (=rows) per partition of data (data had 11 columns)
      lapply(pt, FUN=dim)
      
      # Training set containing 2/3 of the stratified samples
      # Constructed by dropping out the first third of samples
      
      train <- do.call("rbind", pt[-1])
      
      # Test set containing the remaining 1/3
      
      test <- pt[[1]]
      
      # 21 samples in training dataset
      print(dim(train))
      # 11 samples in testing dataset
      print(dim(test))
      
      
      
      > print(train)
                          mpg cyl  disp  hp drat    wt  qsec vs am gear carb
      Mazda RX4 Wag      21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
      Datsun 710         22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
      Hornet 4 Drive     21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
      Merc 450SE         16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
      Cadillac Fleetwood 10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
      Fiat 128           32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
      Toyota Corona      21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
      Dodge Challenger   15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
      Camaro Z28         13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
      Ford Pantera L     15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
      Volvo 142E         21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
      Hornet Sportabout  18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
      Duster 360         14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
      Merc 230           22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
      Merc 280           19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
      Merc 450SLC        15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
      Honda Civic        30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
      Pontiac Firebird   19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
      Porsche 914-2      26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
      Lotus Europa       30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
      Ferrari Dino       19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
      > print(test)
                           mpg cyl  disp  hp drat    wt  qsec vs am gear carb
      Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
      Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
      Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
      Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
      Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
      Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
      Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
      Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
      AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
      Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
      Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
      
      
      # Example of sampling without stratification; the binary covariates 'vs' and 'am' are probably not distributed equally over the bins
      lapply(pt2 <- partition_data(mtcars, bins=3), FUN=function(x) table(x[,c("vs","am")]))
      
      # Stratified according to a single covariate (cylinders)
      lapply(pt3 <- partition_data(mtcars, stratum="cyl", bins=3), FUN=function(x) table(x[,c("cyl")]))
      

      在讨论过的这个特定数据集中,使用 Anthony 回答中的 data.frame:

      xpt <- partition_data(x, stratum="cl", bins=3)
      # Same as:
      #xpt <- partition_data(x, stratum=1, bins=3)
      
      train_xpt <- do.call("rbind", xpt[-1])
      test_xpt <- xpt[[1]]
      #> summary(train_xpt[,"cl"])
      # A  B  C  D 
      #67 66 67 67 
      #> summary(test_xpt[,"cl"])
      # A  B  C  D 
      #33 34 33 33 
      

      【讨论】:

        猜你喜欢
        • 2017-06-29
        • 1970-01-01
        • 2015-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-20
        • 1970-01-01
        相关资源
        最近更新 更多