由于几个原因(如下所述),您尝试的方法相当缓慢,而且它还涉及大量数据重复,这通常效率不高。
首先,您使用循环来执行采样,这在 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,]