【问题标题】:Stratified random sampling with no repeated IDs没有重复 ID 的分层随机抽样
【发布时间】:2021-09-27 05:47:38
【问题描述】:

我有一个数据集,其中每个id 有多个样本,可以分层为group 变量。我想做随机抽样,按group 分层,但不重复id(即每个id 在输出中只出现一次)。

我尝试修改一些现有的解决方案,但是,所有解决方案似乎都对数据进行了采样,并在各个组中包含来自单个 id 的多个样本:

我尝试了以下方法,认为 replace = FALSE 可能有助于确保每个 id 中只使用 1 个样本,但这仍然不能满足我的要求。

set.seed(1)
# Data 
data <- data.frame(
  id = c("A", "C", "B", "D", "E", "F", "A", "A", "B", "B", "B", "D", "D", "E", "E", "F"),
  group = c("1", "1", "2", "2", "3", "3", "2", "1", "1", "2", "3", "2", "3", "2", "1", "3"),
  length = c("54", "52", "43", "42", "60", "46", "59", "60", "51", "45", "47", "58", "48", "46", "56", "57"))

# Stratified random sampling by group 
sample <- data %>%
  distinct %>%
  group_by(group) %>%
  sample_n(2, replace = FALSE) %>%
  left_join(data)

sample 输出:

id group length
A   1   60      
C   1   52      
D   2   42      
A   2   59      
B   3   47      
E   3   60      

但是,如上所示,id= Agroup 1 和 2 中重复出现。我想要的理想输出应该是这样的,其中每个id 只出现一次,并且样本按group 分层:

id group length
A   1   54      
C   1   52      
B   2   43      
D   2   42      
E   3   60      
F   3   46

有没有办法自定义现有的解决方案,以便在对每个group 进行采样时,如果一个id 已经用于另一个group,它将被排除在外,而不是为另一个group 采样?我知道我可以将%&gt;% distinct(id) 添加到我的代码中,但我相信这将不再是随机的,因为distinct() 只是为id 选择了第一行。感谢您的帮助!

【问题讨论】:

  • 如果组的数量很小且已知数量,您是否能够在跟踪被采样的 id 的同时迭代组,以便在进入下一个组时,在采样之前执行 setdiff()?

标签: r random sampling


【解决方案1】:

我有一个候选解决方案,使用 for-loops。当然,该解决方案有点尴尬,并且有一些与您提供的数据相关的警告。但是,该脚本按预期工作。

# Split by group; this provides
# a list with each group.
data_list <- data %>% split(
        f = .$group
)

# shuffle the list to introduce
# randomness
shuffle <- sample(length(data_list))

data_list <- data_list[shuffle]

# Sample from the first indice
# which serves as a baseline for remaining
# samples
sampled_data <- data_list[[1]] %>%
        distinct(id, .keep_all = TRUE) %>%
        sample_n(2)


for (i in 2:length(data_list)) {
        
        # Proceed to next group
        new_data <- data_list[[i]]
        
        
        indicator <- new_data$id %in% sampled_data$id
        
        sampled_data <- bind_rows(
                sampled_data,
                new_data[!indicator,] %>% distinct(id, .keep_all = TRUE) %>% group_by(group) %>% sample_n(2)
        )
        
        
        
}

如果初始 sampled_data 存在特定的 ids,则此算法与您提供的 data 一起工作,否则,唯一 ID 的可用性将耗尽。

该算法首先使用split 将您的数据拆分为各个组,然后打乱list 的顺序以在您的distinct 函数中引入随机性。

初始抽样

我们首先从第一组中获取sample,然后将其作为其余组的基线。

首先从基线样本中存在的下一个索引中删除所有id。然后采样并将其绑定到列表,并创建一个data.frame

下一个示例

新的data.frame 现在由id 中不同的前两个组组成,并从data.frame 中存在的剩余索引中删除id

最终产品如下;

id group length
1  B     1     51
2  C     1     52
3  D     2     42
4  A     2     59
5  E     3     60
6  F     3     46

如果您提供的数据代表您的实际数据,那么该算法显然需要进行一些改进,因为根据seed,唯一值的可用性会随着您的初始id 而耗尽。

我没有提供seed,因为我找不到合适的。

【讨论】:

  • 感谢您的明确答复!我最终使用的解决方案与您的答案具有相似的逻辑,因为我首先引入了随机化。这是set.seed() 然后使用data2 &lt;- data[sample(nrow(data)),] 随机化所有行。由于现在这些行是随机的,我可以使用distinct() 而不必担心它只为每个id 选择第一行。然后我再次set.seed() 并使用data2 运行剩余的代码。需要使用seed,以便为组最大化唯一的id
  • 相当巧妙的修改! :-) 老实说很难。
  • 你能接受答案,所以我们知道它已经关闭了吗?或者你期待更多的答案? :-)
  • 我已经添加了我最后使用的解决方案作为接受的答案。再次感谢您的解决方案,在需要循环版本时会有所帮助。
  • 不——有了dplyrgroup_by(),你几乎不需要for-loop
【解决方案2】:

这是我最后使用的解决方案。

# Randomise rows
set.seed(x) # play around and set seed accordingly
data_rows <- sample(nrow(data))
data2 <- data[data_rows, ]

# Stratified random sampling 
set.seed(x) # play around and set seed accordingly
randomised <- data2 %>%
              distinct(id, .keep_all = TRUE) %>%
              group_by(group) %>% 
              sample_n(2, replace = FALSE) %>%
              ungroup() 

【讨论】:

  • 绝对是一个简洁的解决方案! :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-16
  • 2014-06-22
  • 1970-01-01
  • 2018-05-14
  • 2018-11-13
  • 2013-01-28
  • 2019-09-20
相关资源
最近更新 更多