【问题标题】:Update subset of data.table based on join基于连接更新data.table的子集
【发布时间】:2013-01-21 04:05:15
【问题描述】:

我有两个数据表,DT1 和 DT2:

set.seed(1)
DT1<-data.table(id1=rep(1:3,2),id2=sample(letters,6), v1=rnorm(6), key="id2")
DT1
##    id1 id2         v1
## 1:   2   e  0.7383247
## 2:   1   g  1.5952808
## 3:   2   j  0.3295078
## 4:   3   n -0.8204684
## 5:   3   s  0.5757814
## 6:   1   u  0.4874291

DT2<-data.table(id2=c("n","u"), v1=0, key="id2")
DT2
##    id2 v1
## 1:   n  0
## 2:   u  0

我想根据与 DT2 的连接来更新 DT1,但只针对 DT1 的一个子集。例如,对于DT1[id1==3],我希望第 4 行中 v1 的值会更新为以下结果:

DT1
##    id1 id2         v1
## 1:   2   e  0.7383247
## 2:   1   g  1.5952808
## 3:   2   j  0.3295078
## 4:   3   n          0
## 5:   3   s  0.5757814
## 6:   1   u  0.4874291

我知道如何更新表(使用:= 赋值运算符)、如何连接表(DT1[DT2])以及如何对表进行子集化(DT1[id1==3])。但是我不确定如何同时完成这三个操作。

编辑: 请注意,原始示例仅尝试更新一列,但我的实际数据需要更新许多列。考虑 DT1b 和 DT2b 中的其他场景:

set.seed(2)
DT1b<-DT1[,v2:=rnorm(6)] # Copy DT1 and add a new column
setkey(DT1b,id2)
DT1b
##    id1 id2         v1          v2
## 1:   2   e  0.7383247 -0.89691455
## 2:   1   g  1.5952808  0.18484918
## 3:   2   j  0.3295078  1.58784533
## 4:   3   n -0.8204684 -1.13037567
## 5:   3   s  0.5757814 -0.08025176
## 6:   1   u  0.4874291  0.13242028

DT2b<-rbindlist(list(DT2,data.table(id2="e",v1=0))) # Copy DT2 and add a new row
DT2b[,v2:=-1] # Add a new column to DT2b
setkey(DT2b,id2)
DT2b
##    id2 v1 v2
## 1:   e  0 -1
## 2:   n  0 -1
## 3:   u  0 -1

根据@nmel 和@BlueMagister 的有用回答,我为更新的场景想出了这个解决方案:

DT1b[DT2b[DT1b[id1 %in% c(1,2)],nomatch=0],c("v1","v2"):=list(i.v1,i.v2)]
DT1b
##    id1 id2         v1          v2
## 1:   2   e  0.0000000 -1.00000000
## 2:   1   g  1.5952808  0.18484918
## 3:   2   j  0.3295078  1.58784533
## 4:   3   n -0.8204684 -1.13037567
## 5:   3   s  0.5757814 -0.08025176
## 6:   1   u  0.0000000 -1.00000000

【问题讨论】:

    标签: r join data.table subset assignment-operator


    【解决方案1】:

    我能想到的最简单的方法也是输入id1。 例如

    setkey(DT1, id2,id1)
    DT2[, id1 := 3]
    setkey(DT2, id2, id1)
    
    # use i.v1 to reference v1 from the i component
    DT1[DT2, v1 := i.v1 ]
    
    
    DT1
       id1 id2        v1
    1:   2   e 0.7383247
    2:   1   g 1.5952808
    3:   2   j 0.3295078
    4:   3   n 0.0000000
    5:   3   s 0.5757814
    6:   1   u 0.4874291
    

    【讨论】:

    • 感谢您的回答和其他 cmets,@mnel。这种方法适用于原始示例,并且可以很容易地扩展到需要更新多个列而不是只更新一个列的情况。但如果子集更复杂(例如id1==3 | id1 ==2;请参阅更新的问题),似乎将 id1 键添加到 DT2 会变得更复杂。
    【解决方案2】:

    这类似于mnelsolution,但使用ifelse 而不是第二个密钥。

    DT1[DT2, v1  := ifelse(id1==3, i.v1, v1),nomatch=0]
    

    【讨论】:

    • 感谢您的回答,@BlueMagister。这绝对适用于示例。如果表需要更新许多列,而不仅仅是一个列,您将如何修改您的答案?将其扩展到两列,我认为您的解决方案的中间部分类似于c("v1","v2"):=list(ifelse(id1==3,i.v1,v1),ifelse(id1==3,i.v2,v2))
    • @dnlbrky,多个 ifelse 语句的问题是您必须多次评估 id1==3ifelse。如果您将子集作为键控的一部分(二分搜索),效率会高得多。
    • 是的,@mnel,我应该明确指出,我从@BlueMagister 扩展解决方案的尝试效率不高,而且我正在寻找比我的尝试更好的东西。我喜欢@BlueMagister 的这种方法在向子集添加更多条件方面很灵活(例如,id1==3 | id==2)。我更新了我的问题以包含多列和多个子集条件的场景。
    • 我发现它对左 data.table 子集的左连接非常有帮助。 Data.table 提供更快的ifelse() 版本,即fifelse()
    【解决方案3】:

    这些天我一直在思考这个问题。以下是我的解决方案。

    DT1[DT2, names(DT2):= DT2, on= 'id']
    

    或者,如果您不想在 DT2 有私有变量时向 DT1 添加新变量:

    common.var <- intersect(names(DT1), names(DT2))
    DT1[DT2, c(common.var) := DT2[, common.var, with= FALSE] ,on= 'id']
    

    【讨论】:

      猜你喜欢
      • 2020-04-16
      • 1970-01-01
      • 2014-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-10
      • 1970-01-01
      相关资源
      最近更新 更多