【问题标题】:Take random sample by group按组随机抽样
【发布时间】:2013-08-17 23:55:27
【问题描述】:

我有一个由分布在 15 个不同 ID 中的近 50,000 行组成的数据框(每个 ID 都有数千个观察值)。数据框如下:

        ID  Year    Temp    ph
1       P1  1996    11.3    6.80
2       P1  1996    9.7     6.90
3       P1  1997    9.8     7.10
...
2000    P2  1997    10.5    6.90
2001    P2  1997    9.9     7.00
2002    P2  1997    10.0    6.93

我想为每个 ID 随机抽取 500 行(因此 P1 为 500,P2 为 500,......)并创建一个新的 df。我试试:

new_df<-df[df$ID %in% sample(unique(dfID),500),]

但它需要随机一个 ID,而我需要每个 ID 500 个随机行。

【问题讨论】:

标签: r dataframe sample


【解决方案1】:

这可作为dplyr 中的slice_sample 函数使用:

library(dplyr)
new_df <- df %>% group_by(ID) %>% slice_sample(n=500)

在旧版本的 R 中,该函数称为 sample_n,已被弃用。

【讨论】:

  • 在大型数据框上运行良好。
  • 从函数内的 for 循环调用时不起作用。在功能之外完美工作。任何人都有提示为什么?
  • 非标准评估/标准评估问题:stackoverflow.com/a/34187076/5088194
  • 我对这个解决方案的唯一问题是您只能获取最小组的最大样本数。假设一个 ID 有 499 行,但其他所有 ID 需要 500 行,它会抛出错误。
  • 仅供参考,因为 dplyr 动词变化很大:在 dplyr v.1 中,这已被slice_sample取代
【解决方案2】:

这是一个基于data.table 的优雅解决方案。您可以通过三个简单的步骤从面板数据集(平衡或不平衡)中随机抽取 ID:

第 1 步:将原始数据集中的唯一 ID 存储在向量中(我的数据集称为“main”,标识符称为“id”):

ids <- unique(main$id)

步骤 2: 从步骤 1 的向量中随机抽取 ID。在下面的示例中,我从向量“ids”中随机抽取 50 个 ID,并将它们存储在新的向量“draw”中:

draw <- ids %>% sample(50)

第 3 步:根据与第 2 步中绘制的 ID 的匹配对原始数据集中的行进行子集。

rsample <- main[main$id %in% draw, ]

【讨论】:

    【解决方案3】:

    如果您有大型数据集,data.table 解决方案可能如下所示:

    library(data.table)
    
    # Generate 26 mil rows random data
    set.seed(2019)
    dt <- data.table(c1 = sample(length(LETTERS)*10^6), 
                     c2 = sample(LETTERS, replace = TRUE))
    
    # For each letter, sample 500 rows
    dt_sample <- dt[, .SD[sample(x = .N, size = 500)], by = c2]
    
    # We indeed sampled 500 rows for each letter
    dt_sample[, .N, by = c2][order(c2)]
    #>     c2   N
    #>  1:  A 500
    #>  2:  D 500
    #>  3:  G 500
    #>  4:  I 500
    #>  5:  M 500
    #>  6:  N 500
    #>  7:  O 500
    #>  8:  P 500
    #>  9:  Q 500
    #> 10:  R 500
    #> 11:  S 500
    #> 12:  T 500
    #> 13:  U 500
    #> 14:  V 500
    #> 15:  W 500
    #> 16:  Y 500
    #> 17:  Z 500
    

    reprex package (v0.2.1) 于 2019 年 4 月 23 日创建

    如果您的数据不平衡,即某些组恰好小于您所需的样本量(作为行数),那么您需要设置一个防御技巧,例如样本量应为min(500, .N) - 请参阅@ 987654322@。比如:

    dt[, .SD[sample(x = .N, size = min(500, .N))], by = c2]

    【讨论】:

      【解决方案4】:
      library(data.table) #1
      df <- data.table(df) #2
      df[,group_num := sample(2,.N,replace = TRUE,prob = c(500,.N-500)/.N),by = "ID"] #3
      df_sample = df[group_num == 1,] #4
      

      或者您可以将第 3 行和第 4 行更改为:

      df[,random_num := sample(.N,.N),by="ID"]
      df_sample  = df[random_num <=500,]
      

      【讨论】:

        【解决方案5】:

        虽然这不是很优雅的解决方案,但它可能会奏效。

        library(data.table)
        df <- data.table(df)
        f <- list()
        for(i in unique(df1$ID)){
         f[[i]] <- df1[id == i][sample(.N,(500))]
          }
         dfnew <- rbindlist(f)
        

        【讨论】:

          【解决方案6】:

          这是基础 R 中的一种方法。

          首先,要使用的先决条件示例数据:

          set.seed(1)
          mydf <- data.frame(ID = rep(1:3, each = 5), matrix(rnorm(45), ncol = 3))
          mydf
          #    ID         X1          X2          X3
          # 1   1 -0.6264538 -0.04493361  1.35867955
          # 2   1  0.1836433 -0.01619026 -0.10278773
          # 3   1 -0.8356286  0.94383621  0.38767161
          # 4   1  1.5952808  0.82122120 -0.05380504
          # 5   1  0.3295078  0.59390132 -1.37705956
          # 6   2 -0.8204684  0.91897737 -0.41499456
          # 7   2  0.4874291  0.78213630 -0.39428995
          # 8   2  0.7383247  0.07456498 -0.05931340
          # 9   2  0.5757814 -1.98935170  1.10002537
          # 10  2 -0.3053884  0.61982575  0.76317575
          # 11  3  1.5117812 -0.05612874 -0.16452360
          # 12  3  0.3898432 -0.15579551 -0.25336168
          # 13  3 -0.6212406 -1.47075238  0.69696338
          # 14  3 -2.2146999 -0.47815006  0.55666320
          # 15  3  1.1249309  0.41794156 -0.68875569
          

          二、抽样:

          do.call(rbind, 
                  lapply(split(mydf, mydf$ID), 
                         function(x) x[sample(nrow(x), 3), ]))
          #      ID         X1          X2         X3
          # 1.2   1  0.1836433 -0.01619026 -0.1027877
          # 1.1   1 -0.6264538 -0.04493361  1.3586796
          # 1.5   1  0.3295078  0.59390132 -1.3770596
          # 2.10  2 -0.3053884  0.61982575  0.7631757
          # 2.9   2  0.5757814 -1.98935170  1.1000254
          # 2.8   2  0.7383247  0.07456498 -0.0593134
          # 3.13  3 -0.6212406 -1.47075238  0.6969634
          # 3.12  3  0.3898432 -0.15579551 -0.2533617
          # 3.15  3  1.1249309  0.41794156 -0.6887557
          

          还有来自sampling 包的strata,当您想从每个组中取样不同尺寸时,这很方便:

          # install.packages("sampling")
          library(sampling)
          set.seed(1)
          x <- strata(mydf, "ID", size = c(2, 3, 2), method = "srswor")
          getdata(mydf, x)
          #            X1          X2         X3 ID ID_unit Prob Stratum
          # 2   0.1836433 -0.01619026 -0.1027877  1       2  0.4       1
          # 5   0.3295078  0.59390132 -1.3770596  1       5  0.4       1
          # 6  -0.8204684  0.91897737 -0.4149946  2       6  0.6       2
          # 8   0.7383247  0.07456498 -0.0593134  2       8  0.6       2
          # 9   0.5757814 -1.98935170  1.1000254  2       9  0.6       2
          # 14 -2.2146999 -0.47815006  0.5566632  3      14  0.4       3
          # 15  1.1249309  0.41794156 -0.6887557  3      15  0.4       3
          

          【讨论】:

            【解决方案7】:
            mydata1 is your original data(not tested)
            
            mydata2<- split(mydata1,mydata1$ID)
            names(mydata2)<-paste0("mydata2",1:length(levels(ID))) 
            mysample<-Map(function(x) x[sample((1:nrow(x)),size=500,replace=FALSE),], mydata2)
            
            library(plyr)# for rbinding the mysample
            ldply(mysample)
            

            【讨论】:

              【解决方案8】:

              如果其中一个 ID 小于 500 的方法。这里我使用了 mtcars 集:

              n <- 8
              df <- mtcars
              df$ID <- df$cyl
              
              FUN <- function(x, n) {
                  if (length(x) <= n) return(x)
                  x[x %in% sample(x, n)]
              }
              
              df[unlist(lapply(split(1:nrow(df), df$ID), FUN, n = 8)), ]
              

              【讨论】:

                【解决方案9】:

                试试这个:

                library(plyr)
                ddply(df,.(ID),function(x) x[sample(nrow(x),500),])
                

                【讨论】:

                  猜你喜欢
                  • 2016-09-06
                  • 2020-05-31
                  • 1970-01-01
                  • 2018-09-12
                  • 2018-08-31
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多