【问题标题】:Flag duplicate obs between based on two ID variables标记基于两个 ID 变量之间的重复 obs
【发布时间】:2021-09-06 20:16:51
【问题描述】:

更新示例(参见规则)

我有带有 id1 和 id2 列的 data.table(如下所示)

data.table(id1=c(1,1,2,3,3,3,4), id2=c(1,2,2,1,2,3,2))
id1 id2
1 1
1 2
2 2
3 1
3 2
3 3
4 2

我想生成一个标志来识别 id1 和 id2 之间的重复关联。

规则:如果一个特定的 id1 已经与 id2 相关联,那么它应该被标记..一个唯一的 id2 应该只与一个 id1 相关联(见下面的解释)

a) 寻找有效的解决方案和 b) 仅使用基础和 data.table 函数的解决方案

id1 id2 flag
1 1
1 2 Y <== since id2=1is assicated with id1=1 in 1st row
2 2
3 1 Y <== since id2=1 is assicated with id1=1 in 1st row
3 2 Y <== since id2=2 is assicated with id1=2 in 3rd row
3 3
4 2 Y <== since id2=2 is assicated with id1=2 in 3rd row

【问题讨论】:

  • 您创建的示例数据data.table(id1=c(1,1,2,3,3,3,4), id2=c(1,2,2,1,2,3,2)) 与显示的不同
  • 我添加了更多细节
  • 也许您正在寻找igraph 关系
  • 是的。有没有简单的方法来使用 data.table
  • igraph 可能更高效

标签: r data.table


【解决方案1】:

这是一个棘手的问题。如果我理解正确的话,我对 OP 规则的翻译如下:

  1. 对于每个id1 组,只有一行被标记。
  2. 如果id1 组仅包含一行,则标记。
  3. id1 组内,所有在之前的组中使用过id2 的行都会被标记。
  4. 如果id1 组内有多行至今未被标记,则只有第一行被标记;标记所有其他行。

所以,方法是

  • 创建一个包含可用id2 值的向量,
  • 遍历id1 组,
    • 找到每个组中的第一行,其中 id2 值尚未在之前的组中被使用,
    • 标记所有其他行,
    • 并更新可用(未使用)id2 值的向量。
avail <- unique(DT$id2)
DT[, flag := {
  idx <- max(first(which(id2 %in% avail)), 1L)
  avail <- setdiff(avail, id2)
  replace(rep("Y", .N), idx, "")
}, by = id1][]
   id1 id2 flag
1:   1   1     
2:   1   2    Y
3:   2   2     
4:   3   1    Y
5:   3   2    Y
6:   3   3     
7:   4   2

警告

以上代码再现了 OP 提供的用例的预期结果。但是,可能存在其他用例和/或边缘情况,其中可能需要调整代码以符合 OP 的期望。例如,对于 id1 组,其中 all id2 的值已经在之前的组中被消耗,不清楚预期的结果。

编辑:

OP 已编辑预期结果,因此现在也标记了第 7 行。

这是我的代码的调整版本,它在编辑后重现了预期的结果:

avail <- unique(DT$id2)
DT[, flag := {
  idx <- first(which(id2 %in% avail))
  avail <- setdiff(avail, id2[idx])
  replace(rep("Y", .N), idx, "")
}, by = id1][]
   id1 id2 flag
1:   1   1     
2:   1   2    Y
3:   2   2     
4:   3   1    Y
5:   3   2    Y
6:   3   3     
7:   4   2    Y

数据

library(data.table)
DT = data.table(id1 = c(1, 1, 2, 3, 3, 3, 4),
                id2 = c(1, 2, 2, 1, 2, 3, 2))

【讨论】:

    【解决方案2】:

    这是一个非常复杂的链条,但我认为它会产生结果(您问题中的结果不符合您自己的逻辑):

    library(data.table)
    a = data.table(id1=c(1,1,2,3,3,3,4), id2=c(1,2,2,1,2,3,2))
    
    a[, .SD[1, ], by = id2][, 
                           Noflag := "no"][a, 
                                           on = .(id2, id1)][is.na(Noflag), 
                                                             flag := "y"][,
                                                                          Noflag := NULL][]
    

    里面有什么:

    • a[, .SD[1, ], by = id2] 通过id2 获取子组的第一行。此群组不应被标记,因此
    • [, Noflag := "no"] 将它们标记为“未标记”(看图。我说它很复杂)。我们需要将这个无标记表与原始表连接起来:
    • [a, on = .(id2, id1)]id1id2 上将最后一个表与原始a 连接起来。现在我们需要标记那些没有被标记为“不应该被标记”的行:
    • [is.na(Noflag), flag := "y"]。最后一部分是删除Noflag 不必要的列:
    • [, Noflag := NULL] 并添加 [] 以在屏幕上显示新表格。

    我同意@akrun 的评论,认为igraph 不仅更高效,而且更简单。

    【讨论】:

    • 谢谢,我也在探索 igraph。 (仅供参考,我最初发布的输出正是预期的结果,但后来我意识到我解释的规则不正确,所以我进行了编辑以避免其他用户混淆)
    • 不幸的是,运行您的代码由于某种原因我无法重现 OP 的预期结果。例如,使用您的代码,第 2 行没有被标记(应该是),而第 3 行被标记(不应该)。我不确定这是否是由于我在复制您的代码时出错。也许,如果您可以编辑您的答案并显示您的结果,那将会很有帮助。谢谢。
    • @Uwe,你的回答很好。在我回答的时候,我只是在许多编辑中迷失了(参见问题的编辑 2 到 5,不仅在预期结果表中,而且在规则的解释中)。老实说,通过阅读您的回答,我完全理解了规则。
    【解决方案3】:
    # replicate your data
    df <- data.table(id1=c(1,1,2,3,3,3,4), id2=c(1,2,2,1,2,3,2))
    
    
    # create and append a new, empty column that will late be filled with the info whether they match or not
    empty_col <- rep(NA, nrow(df)) #create empty vector
    df[ , `:=` (duplicate = empty_col)] #append to existing data set
    
    # create loop, to iteratively check if statement is true, then fill the new column accordingly  
    # note that instead of indexing the columns (e.g. df[,3] you could also use their names (e.g. df$flag)  
    
    
    for (i in 1:nrow(df)){
      if (i>=2 & df[i,1] == df[1:i-1,1]){ #check if value in loop I matches that of any rows before (1:i)
        df[i,3] = 1 
      }
      else {
        df[i,3] = 0 # when they dont match: 0, false
      }
    }
    
    # note that the loop only starts in the second row, as i-1 would be 0 for i=1, such that there would be an error. 
    
    
    

    【讨论】:

    • 我更喜欢 data.table 而不是 for-loop,因为它效率不高。仅供参考,我更新了示例以使其更简洁。
    • 如果是这样,您的问题仍然不是很清楚。也许编辑它说你正在寻找'a)一个有效的解决方案和b)一个只使用基础和data.table函数的解决方案'会更清楚。也许如果你详细说明当你说“我可以分多个步骤思考逻辑,但想知道是否有简单的方法来实现这一点”时你的想法会有所帮助
    • 谢谢,我按照你的建议更新了。仅供参考,我认为我可以通过迭代来解决这个问题,但没有一个选项是有效的。
    猜你喜欢
    • 1970-01-01
    • 2019-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多