在 R 中按组采样,没有替换,但最终结果也不能包含任何重复
Sampling by Group in R with no replacement but the final result cannot contain any repeats as well
我正在尝试构建一个对照组。 ID_1是原始参与者,ID_2是对照。为简单起见,它们按性别和年龄匹配。我收到了一个如下所示的数据框:
ID_1 <- c(1,1,1,2,2,3,3,4,4,4)
Sex <- c("M","M","M","F","F","M","M","F","F","F")
Age <- c(23,23,23,35,35,44,44,35,35,35)
ID_2 <- c(321,322,323,630,631,502,503,630,631,632)
df <- data.frame(ID_1, Sex, Age, ID_2)
所以每个 ID_1 我都有几场比赛,我想在每组中抽样,只得到一个。我得到了:
library(dplyr)
random_ID_2 <- df %>% group_by(ID_1) %>% sample_n(size = 1, replace = F)
问题是我不想重复 ID_2。所以偶然的机会我最终可以将 ID_1 = 2 和 ID_1 = 4 配对到同一个控件 ID_2 = 630
我怎样才能确保这不会发生?
提前致谢。
这是一个可能的采样选项,如果有重复项,它将重新采样:
# handles case where no samples left
my_sample <- function(x, ...){
if (length(x) == 0L) return(NA) else sample(x, ...)
}
df %>%
group_by(ID_1) %>%
slice_sample(n = 1) %>%
ungroup() %>%
mutate(resample = duplicated(ID_2)) %>%
rowwise() %>%
mutate(ID_2 = if (resample) my_sample(df[df$ID_1 == ID_1 & df$ID_2 != ID_2, "ID_2"], 1) else ID_2) %>%
ungroup() %>%
select(-resample)
需要注意的一件事是,在您的数据框下方具有重复 ID_2
的行是有条件的采样。
输出
set.seed(17)
是采样相同ID_2
的情况:
df %>%
group_by(ID_1) %>%
slice_sample(n = 1)
ID_1 Sex Age ID_2
<dbl> <chr> <dbl> <dbl>
1 1 M 23 322
2 2 F 35 631
3 3 M 44 502
4 4 F 35 631
并测试上面的代码:
set.seed(17)
df %>%
group_by(ID_1) %>%
slice_sample(n = 1) %>%
ungroup() %>%
mutate(resample = duplicated(ID_2)) %>%
rowwise() %>%
mutate(ID_2 = if (resample) my_sample(df[df$ID_1 == ID_1 & df$ID_2 != ID_2, "ID_2"], 1) else ID_2) %>%
ungroup() %>%
select(-resample)
ID_1 Sex Age ID_2
<dbl> <chr> <dbl> <dbl>
1 1 M 23 322
2 2 F 35 631
3 3 M 44 502
4 4 F 35 632
>
再次强调我上面的观点 ID_1 == 4
是有条件的抽样,因为我们允许 ID_1 == 2
保持与 ID_2 == 631
的匹配并更改 ID_1 == 4
的匹配。
工作原理
- 像往常一样对数据进行采样。
- 然后我们检查
ID_2
中的重复项。注意:duplicated
returns TRUE
对于所有 后续 重复的 ID。
- 如果一行需要重新采样,那么我们使用行
mutate(ID_2 = if ...)
从原始数据帧中进行子集和采样
如果可以使用 data.table
解决方案:
dt <- setnames(
unique(
setorder(
setDT(copy(df))[, idx := 1:.N, by = ID_1], # add an index column for each ID_1 group
idx, ID_1) # sort by idx, ID_1
# for each Sex/Age group, sample unique values of ID_2 withouth replacement (pad with NA)
[, ID_3 := c(sample(unique(ID_2)), rep(NA, .N - uniqueN(ID_2))), by = c("Sex", "Age")],
by = "ID_1") # get the first row for each ID_1 group
[, c(1:3, 6)], "ID_3", "ID_2") # remove helper columns and rename "ID_3" to "ID_2"
我正在尝试构建一个对照组。 ID_1是原始参与者,ID_2是对照。为简单起见,它们按性别和年龄匹配。我收到了一个如下所示的数据框:
ID_1 <- c(1,1,1,2,2,3,3,4,4,4)
Sex <- c("M","M","M","F","F","M","M","F","F","F")
Age <- c(23,23,23,35,35,44,44,35,35,35)
ID_2 <- c(321,322,323,630,631,502,503,630,631,632)
df <- data.frame(ID_1, Sex, Age, ID_2)
所以每个 ID_1 我都有几场比赛,我想在每组中抽样,只得到一个。我得到了:
library(dplyr)
random_ID_2 <- df %>% group_by(ID_1) %>% sample_n(size = 1, replace = F)
问题是我不想重复 ID_2。所以偶然的机会我最终可以将 ID_1 = 2 和 ID_1 = 4 配对到同一个控件 ID_2 = 630
我怎样才能确保这不会发生?
提前致谢。
这是一个可能的采样选项,如果有重复项,它将重新采样:
# handles case where no samples left
my_sample <- function(x, ...){
if (length(x) == 0L) return(NA) else sample(x, ...)
}
df %>%
group_by(ID_1) %>%
slice_sample(n = 1) %>%
ungroup() %>%
mutate(resample = duplicated(ID_2)) %>%
rowwise() %>%
mutate(ID_2 = if (resample) my_sample(df[df$ID_1 == ID_1 & df$ID_2 != ID_2, "ID_2"], 1) else ID_2) %>%
ungroup() %>%
select(-resample)
需要注意的一件事是,在您的数据框下方具有重复 ID_2
的行是有条件的采样。
输出
set.seed(17)
是采样相同ID_2
的情况:
df %>%
group_by(ID_1) %>%
slice_sample(n = 1)
ID_1 Sex Age ID_2
<dbl> <chr> <dbl> <dbl>
1 1 M 23 322
2 2 F 35 631
3 3 M 44 502
4 4 F 35 631
并测试上面的代码:
set.seed(17)
df %>%
group_by(ID_1) %>%
slice_sample(n = 1) %>%
ungroup() %>%
mutate(resample = duplicated(ID_2)) %>%
rowwise() %>%
mutate(ID_2 = if (resample) my_sample(df[df$ID_1 == ID_1 & df$ID_2 != ID_2, "ID_2"], 1) else ID_2) %>%
ungroup() %>%
select(-resample)
ID_1 Sex Age ID_2
<dbl> <chr> <dbl> <dbl>
1 1 M 23 322
2 2 F 35 631
3 3 M 44 502
4 4 F 35 632
>
再次强调我上面的观点 ID_1 == 4
是有条件的抽样,因为我们允许 ID_1 == 2
保持与 ID_2 == 631
的匹配并更改 ID_1 == 4
的匹配。
工作原理
- 像往常一样对数据进行采样。
- 然后我们检查
ID_2
中的重复项。注意:duplicated
returnsTRUE
对于所有 后续 重复的 ID。 - 如果一行需要重新采样,那么我们使用行
mutate(ID_2 = if ...)
从原始数据帧中进行子集和采样
如果可以使用 data.table
解决方案:
dt <- setnames(
unique(
setorder(
setDT(copy(df))[, idx := 1:.N, by = ID_1], # add an index column for each ID_1 group
idx, ID_1) # sort by idx, ID_1
# for each Sex/Age group, sample unique values of ID_2 withouth replacement (pad with NA)
[, ID_3 := c(sample(unique(ID_2)), rep(NA, .N - uniqueN(ID_2))), by = c("Sex", "Age")],
by = "ID_1") # get the first row for each ID_1 group
[, c(1:3, 6)], "ID_3", "ID_2") # remove helper columns and rename "ID_3" to "ID_2"