R:使用 tapply 按因素抽样观察

R: Sampling observations by factors using tapply

我有一个大数据集,我正试图从中抽样行。每行都有一个家庭 ID,每个家庭 ID 可能有一行或多行。我想通过为每个家庭 ID 随机抽取一行来解析数据集。我试图通过使用 tapply()split() + lapply() 函数来完成此操作,但无济于事。下面是重现我的问题的代码 - 因子级别和数据条目的大小和范围反映了我正在使用的数据集。

set.seed(63)
f1 <- factor(c(rep(30000:32000, times=1), 
               rep(30500:31700, times = 2), 
               rep(30900:31900, times = 3)))
f2 <- factor(rep(sample(1:7, replace = TRUE), times = length(f1)/7))
x1 <- round(matrix(rnorm(length(f1)*300), nrow = length(f1), ncol = 300),3)
df <- data.frame(f1, f2, x1)

接下来,我使用 tapplyf1 中对每个因子采样一行,然后检查重复项。 (f2 是索引观察的另一个方面的次要因素,但 [希望] 与此无关;我将其包含在内只是为了完全公开我的数据集的结构)。

s1 <- tapply(1:nrow(df), df$f1, sample, size=1)
any(duplicated(s1))

使用duplicated的第二行代码的输出是TRUE,这意味着有重复。难住了,我尝试 split 看看是否是问题所在。

df.split <- split(1:nrow(df), df$f1)
any(duplicated(df.split))

这里 duplicated 的输出是 FALSE,所以问题不是 split。然后我将输出 df.splitlapplysample 一起使用,看看问题是否出在 tapply.

df.unique <- unlist(lapply(df.split, sample, size = 1, replace = FALSE, 
                           prob = NULL))
any(duplicated(df.unique))

在第一行中,我从输出列表的df.split的每个元素中采样了一个值,然后我使用unlist转换为一个向量。这里 duplicated 的输出也是 TRUE.

samplelapply 中的某处发生了奇怪的事情(因为 tapply 只是调用 lapply)。我不确定如何解决这个问题(我搜索了 SO 和 Google 并没有发现与我的问题相关的任何内容),所以非常感谢任何帮助!

编辑:我希望有人能告诉我为什么上面使用 tapplylapply 的代码没有按预期工作。 Arthur 提供了一个很好的答案,我也为 sample 编写了一个循环。我想知道为什么上面的代码运行不正常。

我会这样做:

library(data.table)
data.table(df)[,.SD[sample(.N,1)],by='f1']

... 但实际上,如果您只想要一个索引而不是实际的子集 table ,那么您使用 tapply 的原始方法会更快;但是,您必须注意到 sample(n) 实际上在 length(n)==1 时在 1:n 中采样。参见 ?sample。这个版本是防错的:

 s1 <- tapply(1:nrow(df), list(df$f1), function(v) v[sample(1:length(v), 1)])` is error prooff