我有一些分组数据,每个项目一行。我想按组进行分层抽样,有两个限制:(1)一定的总样本量;(2)样本应在各组之间尽可能均匀地分配(即组样本量的最小sd
)。
理想情况下,我们从每个组中选择相同(固定)数量的项目,当组大小>=
所有组所需的size
时,这没有问题。但是,有时团体规模小于size
。但是,项目总数始终高于总样本量。例如,如果总样本量为 12,有四个不同的组,我们理想情况下希望从每个组中选择 3 个项目
size_tot <- 12
n_grp <- 4
size <- size_tot / n_grp
一些数据:
d2 <- data.table(id = 1:16,
grp = rep(c("a", "b", "c", "d"), c(9, 4, 2, 1)))
d2
# id grp
# 1: 1 a
# 2: 2 a
# 3: 3 a
# 4: 4 a
# 5: 5 a
# 6: 6 a
# 7: 7 a
# 8: 8 a
# 9: 9 a
# 10: 10 b
# 11: 11 b
# 12: 12 b
# 13: 13 b
# 14: 14 c
# 15: 15 c
# 16: 16 d
我最初的逻辑是"如果项目数等于或大于size
,则从组中抽取size
项目,否则只需从组中选择所有项目"。另请参阅此处,此处和此处。
set.seed(1)
d2[ , if(.N >= size) .SD[sample(x = .N, size = size)] else .SD, by = "grp"]
# grp id
# 1: a 3
# 2: a 9
# 3: a 5
# 4: b 13
# 5: b 10
# 6: b 11
# 7: c 14
# 8: c 15
# 9: d 16
在项目数量足够(a和b)的两组中,我们从各抽样3个项目。在小组(c和d)中,我们只是选择了所有的东西,即分别是2和1。这导致总样本数量为 9,即小于所需的总样本数量 12。因此,我们需要从具有剩余项目的较大组中抽取其他项目,以实现所需的总样本量。在这种情况下,所需的抽样将是"b"中的1个附加项目和"a"中的两个附加项目。
以下是我对sd
最低分区的看法。总样本量可以分为四组,如下所示:
library(partitions)
cmp <- compositions(n = size_tot, m = 4)
然后可以将分区从低sd
(组之间的样本大小相等 - 所需)到高sd
排序:
std <- apply(cmp, 2, sd)
cmp2 <- cmp[ , order(std)]
cmp2[ , 1:10]
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,] 3 4 3 3 4 3 4 2 3 2
# [2,] 3 3 4 3 3 4 2 4 2 3
# [3,] 3 3 3 4 2 2 3 3 4 4
# [4,] 3 2 2 2 3 3 3 3 3 3
和团体规模:
d1[ , .(n = .N), by = "grp"]
# grp n
# 1: a 9
# 2: b 4
# 3: c 2
# 4: d 1
但是如何将这个分区(总和为 12)与组样本量(不一定总和为 12)相匹配?还有人在这里闻到XY问题吗?因此,是否有我忽略的替代方法?
PS:我考虑过比例分配(比例抽样),但是当组大小的分布充分偏斜时,这种抽样显然不尊重绝对总样本量,并且不会在组间均匀分布样本(例如 caret::createDataPartition
和strata::balancedstratification
)
你的答案差不多了。只需对 cmp2 进行筛选,即可获得满足采样大小小于或等于组大小条件的第一个采样集:
#Create a set of indices of sampling sizes that fit the criteria
original_groups <- d2[, .N, by = grp][,N]
valid_indexes <- apply(cmp2, 2, function(x) all(x <= original_groups))
#Take the first of these valid indices (lowest variance)
sampling_sizes <- cmp2[,which(valid_indexes)[1]]
#Create a sampling size variable on the datatable
d2[, sampling_size := rep(sampling_sizes, original_groups)]
#Sample as before
d2[ , .SD[sample(x = .N, size = sampling_size)], by = "grp"]