Select 组内每个案例的非重复对照

Select a non-duplicate control for each case within a group

在给定的数据集中,case_control表示一行是case还是controlid是对case唯一的标识符但它可以重复 control 并且 group 表示集群。我需要 select 每个案例中的每个 group 一个控件,但是如果控件是前一个案例的 selected,则不能对下一个案例进行 selected,基于在 id 变量上。如果没有可用的控件,则必须删除案例。

我如何才能在具有约 1000 万行(200 万个案例和 800 万个控件)的超大数据集中快速工作?

数据集看起来像这样(https://docs.google.com/spreadsheets/d/1MpjKv9Fm_Hagb11h_dqtDX4hV7G7sZrt/edit#gid=1801722229)

group       case_control  id
cluster_1   case          11
cluster_1   control       21
cluster_1   control       22
cluster_1   control       23
cluster_2   case          12
cluster_2   control       21
cluster_2   control       22
cluster_2   control       24
cluster_3   case          13
cluster_3   control       21
cluster_3   control       22
cluster_3   control       25

预期输出必须如下所示

group       case_control    id
cluster_1   case            11
cluster_1   control         21
cluster_2   case            12
cluster_2   control         22
cluster_3   case            13
cluster_3   control         25

基数 R:

Reduce(\(x,y)rbind(x, y[which(!y$id %in% x$id)[1:2], ]), split(df[-(3:4),], ~group))

       group case_control id
1  cluster_1         case 11
2  cluster_1      control 21
5  cluster_2         case 12
7  cluster_2      control 22
9  cluster_3         case 13
12 cluster_3      control 25

请注意,我们只需要每个集群的第一个案例和第一个非重复控件,因此切片 1:2

Tidyverse:

df %>%
  slice(-(3:4))%>%
  group_split(group) %>%
  reduce(~rbind(.x, slice(anti_join(.y, .x, by = c("case_control", "id")), 1:2)))

# A tibble: 6 x 3
  group     case_control    id
  <chr>     <chr>        <int>
1 cluster_1 case            11
2 cluster_1 control         21
3 cluster_2 case            12
4 cluster_2 control         22
5 cluster_3 case            13
6 cluster_3 control         25

这是一个data.table方法。

代码可以缩短(很多),但我选择将每个步骤分开(并注释),这样您就可以看到执行了哪些操作并可以检查中间结果。

library(data.table)
#initialise vector for used ids
id.used <- as.numeric()
#split by group and loop 
L <- lapply(split(DT, by = "group"), function(x) {
  #select first row
  caserow <- x[1,]
  #select second to last row
  controlrow <- x[2:nrow(x), ]
  #match against id's already in use
  controlrow.new <- controlrow[!id %in% id.used, ]
  #sample random row from id's not already used
  controlrow.sample <- controlrow.new[controlrow.new[, .I[sample(.N, 1)], ]]
  #fill id.used (be carefull with the use of <<- !! google why..)
  id.used <<- c(id.used, controlrow.sample$id)
  #rowbind the sampled row to the caserow
  return(rbind(caserow, controlrow.sample))
})
# rowbind the list back together and cast to wide
dcast(rbindlist(L), group ~ case_control, value.var = "id")
#        group case control
# 1: cluster_1   11      21
# 2: cluster_2   12      24
# 3: cluster_3   13      25

使用的示例数据

DT <- fread("group       case_control  id
cluster_1   case          11
cluster_1   control       21
cluster_1   control       22
cluster_1   control       23
cluster_2   case          12
cluster_2   control       21
cluster_2   control       22
cluster_2   control       24
cluster_3   case          13
cluster_3   control       21
cluster_3   control       22
cluster_3   control       25")