在 r 中生成大量样本

Generating the large number of samples in r

我有一个 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 
   }

但我的困惑是这可能不是执行此操作的有效方法,我不知道两个样本数据帧是否相同。

您可以试试下面的代码:

  • 采样时不放回
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)

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

  • 如果您不允许更换,您的 space 尺码是 choose(50,12)*factorial(12),比 6000000 大很多。因此,碰撞的可能性很小。

  • 如果允许替换,你的space大小是50**12*factorial(12),比没有替换的场景还要大。这样碰撞的概率就会小很多。

是的,这不是很有效。

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

2) R 是一种解释型语言,在使用基本的分支函数(例如 if、for、while ...)或变量操作(例如 a <- a+1)时非常慢。对于只需要很短时间的功能,口译尤其要花费时间。 所以,你想调用一个同时做很多事情的函数,因为当声明时,它很快,因为它可能以更快的方式实现(它们通常是编译的)。

只需将 1:6000000 替换为 1:12 并采用 6000000 的样本向量,这将使您的程序 运行 更快。你只需要安排你如何安排数据。

3) 尝试 replicate

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

首先,您正在使用循环来执行采样,这在 R 中通常很慢。尝试 'vectorize' 您的计算几乎总是更好,这意味着尝试使用相同的方法进行所有计算基本 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 倍。

现在,关于 'repeated' 个相同的样本:正如@ThomasIsCoding 所指出的,完全相同的样本 12 的数量非常大 (2e20),所以你不太可能得到任何完美的 'collisions'.
但是,如果您认为 'the same' 也包括具有相同行集但顺序不同的两个样本,则存在 only 50^12/factorial(12) 组合,或者5e11。这可能看起来很多,但是 'birthday paradox' (https://en.wikipedia.org/wiki/Birthday_problem) 表明您 需要采样大约 7e5 次才可能至少有一个 'collision'。

因此,对于 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,]