【问题标题】:Generating the large number of samples in r在 r 中生成大量样本
【发布时间】:2020-05-16 06:00:27
【问题描述】:

我有一个 50 行 4 列的数据框。我想获得许多 12 行的样本数据框可能是一百万,我不希望我的两个样本数据框相同。我使用了以下代码

    df_l <- list()
    for(i in 1:6000000) {
    set.seed(100+i)
    a <- df[sample(nrow(df),12,replace=T),]
    df_l[[i]] <- a
   rownames(df_l[[i]]) <- 1:12 
   }

但我的困惑是这可能不是有效的方法,我不知道两个示例数据帧是否相同。

【问题讨论】:

    标签: r sampling


    【解决方案1】:

    是的,这不是很有效。

    1) 您只需设置一次种子。

    2) R 是一种解释型语言,在使用基本的分支函数(例如,if、for、while ...)或变量操作(例如,a

    只需将 1:6000000 与 1:12 交换并获取 6000000 的样本向量,这将使您的程序运行得更快。您只需要安排数据的排列方式即可。

    3) 试试replicate

    【讨论】:

      【解决方案2】:

      由于几个原因(如下所述),您尝试的方法相当缓慢,而且它还涉及大量数据重复,这通常效率不高。

      首先,您使用循环来执行采样,这在 R 中通常很慢。尝试“矢量化”您的计算几乎总是更好,这意味着尝试使用相同的基本 R 命令来完成所有这些操作。在这种情况下,我们可以同时对所有子样本进行行采样,然后将采样的行分配给每个子样本。

      其次,您正在创建一个列表 - df_l,其中包含大量重复的原始 df 数据。这很浪费(您不需要多次存储相同的数据),并且需要做很多工作。再次使用索引访问原始数据几乎总是更好。

      因此,将这些放在一起,我们可以创建一种更快的方法,而且不会复制数据:

      首先,一些测试数据:

      df=data.frame(matrix(sample(1:200),ncol=4))
      

      现在,我们无需复制大量新数据帧列表中的数据,而是创建一个采样索引矩阵:

      make_index_samples=function(df,n) {
          return(matrix(sample(nrow(df),12*n,replace=T),nrow=n))
      }
      random_indices=make_index_samples(df,1000)
      

      所以,现在,我们不再使用 df_l[[n]] 访问随机采样的 df n(如在原始示例中),而是使用:

      my_random_df=df[random_indices[n,],]
      

      我们可以使用microbenchmark 来看看这有多快:

      # (almost) original sampling
      make_samples_original=function(df,n) {
          df_l=list()
          set.seed(123)
          for(i in seq_len(n)) {
              df_l[[i]]=df[sample(nrow(df),12,replace=T),]
          }
          return(df_l)
      }
      
      # compare making list of new dfs to making matrix of indices:
      library(microbenchmark)
      microbenchmark(make_samples_original(df,1000),make_index_samples(df,1000))
      # Unit: microseconds
      #                             expr        min          lq        mean      median         uq        max neval
      #  make_samples_original(df, 1000) 103515.198 111525.9985 116499.0323 115045.9485 118883.329 200982.370   100
      #     make_index_samples(df, 1000)    234.193    246.0805    307.6667    249.3815    300.382    755.873   100
      

      因此,对索引进行采样的速度大约快了 300 倍。

      现在,关于“重复”相同的样本:正如@ThomasIsCoding 所指出的,12 个完全相同的样本数量非常大(2e20),因此您不太可能得到任何完美的“碰撞”。
      但是,如果您认为“相同”还包括具有相同行集但顺序不同的两个样本,则 只有 50^12/factorial(12) 组合或 5e11。这可能看起来很多,但“生日悖论”(https://en.wikipedia.org/wiki/Birthday_problem) 表明您需要采样大约 7e5 次,就有可能发生至少一次“碰撞”。

      因此,对于 100 万次随机化,您可能会拥有一两个具有相同行集的样本。对于许多应用程序来说,这可能不是一个大问题。如果它适合您,您可以检查每个随机化以确保它以前没有发生过,但这可能会取消大部分或所有更快采样的好处...

      无论如何,这是一种方法:

      首先,我们制作了比我们实际想要的更多的随机样本,这样我们就可以丢弃任何重复的样本并仍然有足够的样本:

      set.seed(123)
      random_indices=make_index_samples(df,1000100) # 1 million +100 extra
      

      然后,我们为每个随机样本构造一个名称,以唯一标识其中的采样行,但(在这种情况下)无需担心行的顺序:

      random_index_names=apply(random_indices,1,function(row) paste(sort(row),collapse="_"))
      

      我们可以检查是否有任何冲突(这将通过重复的名称显示),并丢弃这些:

      sum(duplicated(random_index_names)) # I got 1 duplicate!  
      random_indices.no_duplicates=random_indices[-duplicated(random_index_names),][1:1000000,]
      

      【讨论】:

        【解决方案3】:

        你可以试试下面的代码:

        • 采样时无需更换
        n <- nrow(df)
        df_1 <- replicate(6000000,df[sample(n,12),],simplify = FALSE)
        
        • 取样时更换
        n <- nrow(df)
        df_1 <- replicate(6000000,df[sample(n,12,replace = TRUE),],simplify = FALSE)
        

        关于相同数据帧的关注点,这取决于您从中采样的空间大小。对于你的情况,

        • 如果不允许替换,则空间大小为choose(50,12)*factorial(12),比6000000 大得多。因此,碰撞的概率很低。

        • 如果允许替换,你的空间大小为50**12*factorial(12),比没有替换的场景还要大。因此,碰撞的概率会低得多。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-18
          • 2020-07-29
          • 2012-11-06
          • 2019-08-25
          • 2018-08-26
          相关资源
          最近更新 更多