在data.table中为每个ID选择第一个唯一匹配项 (r)

3

我有一个data.table,包含3列。前两列是关键ID,第三列代表ID可以拥有的所有可能值。

这里是一个例子:

DT <- data.table( 
            Pol_No = c('a','b','b','c','c','c','c','c','c','c')  
          , Veh_No = c(1,1,2,1,1,1,2,3,3,3)
          , Value = c(1,1,2,3,4,5,6,3,4,5)
          )
DT
    Pol_No Veh_No Value
 1:      a      1     1
 2:      b      1     1
 3:      b      2     2
 4:      c      1     3
 5:      c      1     4
 6:      c      1     5
 7:      c      2     6
 8:      c      3     3
 9:      c      3     4
10:      c      3     5

我需要筛选此表格,使得每个 Policy 和 Vehicle 的 Value 值都是唯一的。因此第 4 行将保留,但第 9 行将被筛选,因为在 [Pol_No:c , Veh_No:1] 已经分配了值 4。

期望结果如下:

   Pol_No Veh_No Value
1:      a      1     1
2:      b      1     1
3:      b      2     2
4:      c      1     3
5:      c      2     6
6:      c      3     4

我尝试了许多可能性,但最好的选择是:

Flt <- 
  DT[DT
     , .(Value)
     , on = .(Pol_No , Veh_No )
     , mult = 'first']
DT[ Value == Flt$Value,]

   Pol_No Veh_No Value
1:      a      1     1
2:      b      1     1
3:      b      2     2
4:      c      1     3
5:      c      2     6
6:      c      3     3

这基本正确,但是[c,3]的值已经在[c,1]中使用过了,因此仍然是错误的。

有什么办法可以过滤掉已经在同一键集中使用过的行吗?


也许可以使用递归选项 out <- DT[0];for(nm in unique(DT$Pol_No)) {tmp <- DT[Pol_No == nm]; for(nm2 in unique(tmp$Veh_No)) {tmp2 <- na.omit(tmp)[Veh_No == nm2];out <- rbind(out, tmp2[1]); tmp[Value %in% tmp2$Value[1], Value := NA]}}; out - akrun
3个回答

3

这样怎么样?

DT[,Count := sequence(.N),by = .(Pol_No,Value)][,.SD[min(Count),],by = .(Pol_No,Veh_No)]
   Pol_No Veh_No Value Count
1:      a      1     1     1
2:      b      1     1     1
3:      b      2     2     1
4:      c      1     3     1
5:      c      2     6     1
6:      c      3     4     2

你可以稍后删除 Count 列。

太棒了!我注意到当Value没有排序时它不起作用,但没关系。我还注意到的另一件事是,当没有可接受的值时,因为它已经在之前的车辆中使用过,它会返回一个已经使用过的值。例如, DT <- data.table( Pol_No = c('a','b','b','c','c','c','c','c','c','c','c','c','c','c') , Veh_No = c(1,1,2,1,1,1,2,3,3,3,4,4,5,5) , Value = c(1,1,2,3,4,5,6,3,4,5,2,3,2,3) )[c,5]返回3,而我只想进行筛选。我相信我有一个解决方法,我会添加上去。谢谢! - plcefrmyhd
实际上,这是一个复杂的集合优化问题。我正在跟随,希望有人能想出更好的解决方案。希望这个方案可以帮助您解决大部分问题。 - Ian Campbell
很酷的data.table解决方案,想法非常棒!已点赞! - ThomasIsCoding

1

感谢Ian Campbell解决了最难的部分!我在原帖中没有提到,但是在之前的车辆中使用了所有可接受的值的情况下,表格也应该过滤行(而不是返回已使用的值)

例如:

DT <- data.table(
            Pol_No = c('a','b','b','c','c','c','c','c','c','c','c','c','c','c')
          , Veh_No = c(1,1,2,1,1,1,2,3,3,3,4,4,5,5)
          , Value = c(1,1,2,3,4,5,6,3,4,5,2,3,2,3)
          )

使用Ian的代码:
DT2 <- DT[,Count := sequence(.N),by = .(Pol_No,Value)][,.SD[min(Count),],by = .(Pol_No,Veh_No)][,Count:=NULL][]

然后添加下一个过滤器似乎就可以解决问题了

DT2 <- DT2[, Dup :=  seq(.N) , by = c('Pol_No', 'Value') ]
DT2[Dup == 1,][,Dup:=NULL]

1:      a      1     1
2:      b      1     1
3:      b      2     2
4:      c      1     3
5:      c      2     6
6:      c      3     4
7:      c      4     2

1
这里是一个可能有帮助的递归函数。
f <- function(dt) {
  if (nrow(dt) == 1) {
    return(dt)
  }
  u <- f(dt[-.N])
  v <- dt[.N]
  if (all(is.na(u[v, Value, on = .(Pol_No, Veh_No)]) & !v[, Value] %in% u[Pol_No == v[, Pol_No], Value])) {
    return(rbind(u, v))
  }
  return(u)
}

such that

> f(DT)
   Pol_No Veh_No Value
1:      a      1     1
2:      b      1     1
3:      b      2     2
4:      c      1     3
5:      c      2     6
6:      c      3     4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接