【问题标题】:data.table mapping数据表映射
【发布时间】:2017-09-17 08:02:55
【问题描述】:

我需要对大型(数百万行)数据集进行批量重新映射。

样本数据:

DT = data.table(yr = sample(3)+2000, a1 = sample(12), a2 = sample(12))[order(yr)]
DT
      yr a1 a2
 1: 2001  2  8
 2: 2001  3 12
 3: 2001 10  4
 4: 2001  9  6
 5: 2002  7  7
 6: 2002 11  3
 7: 2002  5  2
 8: 2002  1 11
 9: 2003  8  5
10: 2003 12  1
11: 2003  6  9
12: 2003  4 10

DTmap = data.table(a1 = 1:12, b=10001:10012)
DTmap
    a1     b
 1:  1 10001
 2:  2 10002
 3:  3 10003
 4:  4 10004
 5:  5 10005
 6:  6 10006
 7:  7 10007
 8:  8 10008
 9:  9 10009
10: 10 10010
11: 11 10011
12: 12 10012

我想用 DTmap 中的映射映射 DT 中的 a1 和 a2 列。忽略它们只是 10000 种不同的事实 - 这是样本数据的人工制品,可以轻松检查结果的准确性。 我可以用这样的一系列连接来做到这一点:

setkey(DT,a1)
setkey(DTmap,a1)
DT.merge1 <- DT[DTmap]

setkey(DT.merge1,a2)
setnames(DTmap,c("a2","b"))
setkey(DTmap,a2)

DT.merge2 <- DT.merge1[DTmap]
DT.merge2
      yr a1 a2     b   i.b
 1: 2003 12  1 10012 10001
 2: 2002  5  2 10005 10002
 3: 2002 11  3 10011 10003
 4: 2001 10  4 10010 10004
 5: 2003  8  5 10008 10005
 6: 2001  9  6 10009 10006
 7: 2002  7  7 10007 10007
 8: 2001  2  8 10002 10008
 9: 2003  6  9 10006 10009
10: 2003  4 10 10004 10010
11: 2002  1 11 10001 10011
12: 2001  3 12 10003 10012

DT.merge2[, `:=` (a1 = NULL, a2 = NULL)]
setnames(DT.merge2,c("year","b1","b2"))
DT.merge2
    year    b1    b2
 1: 2003 10012 10001
 2: 2002 10005 10002
 3: 2002 10011 10003
 4: 2001 10010 10004
 5: 2003 10008 10005
 6: 2001 10009 10006
 7: 2002 10007 10007
 8: 2001 10002 10008
 9: 2003 10006 10009
10: 2003 10004 10010
11: 2002 10001 10011
12: 2001 10003 10012

似乎有一种方法可以在 data.table 语法中使用 by() 或其他方法来执行此操作,但我无法弄清楚。我是一个相当新的 R 编码器,但对其他语言有很多经验。 这可能吗?上面的代码相对较快,但是有很多编码(通过 setkey)。似乎将 by() 与索引一起使用,然后通过引用更新这些值会快得多。

【问题讨论】:

    标签: r mapping data.table


    【解决方案1】:

    您可以为此使用match

    DT[, `:=` (b1 = DTmap$b[match(a1,DTmap$a1)], b2 = DTmap$b[match(a2,DTmap$a1)])]
    

    或者按照@Frank 的建议:

    DT[DTmap, on = .(a1), b1 := i.b][DTmap, on = .(a2=a1), b2 := i.b]
    

    两种方法都可以得到:

    > DT
          yr a1 a2    b1    b2
     1: 2001  7  8 10007 10008
     2: 2001  4 11 10004 10011
     3: 2001  6 12 10006 10012
     4: 2001  8  2 10008 10002
     5: 2002 11  9 10011 10009
     6: 2002  9  5 10009 10005
     7: 2002 10  4 10010 10004
     8: 2002  2 10 10002 10010
     9: 2003 12  7 10012 10007
    10: 2003  3  1 10003 10001
    11: 2003  5  3 10005 10003
    12: 2003  1  6 10001 10006
    

    尤其是在连接多个列时,第二种方法要容易得多。


    要删除a1a2 列,只需添加[, c('a1','a2') := NULL],例如:

    DT[, `:=` (b1 = DTmap$b[match(a1,DTmap$a1)], b2 = DTmap$b[match(a2,DTmap$a1)])
       ][, c('a1','a2') := NULL]
    

    你会得到:

    > DT
          yr    b1    b2
     1: 2001 10007 10008
     2: 2001 10004 10011
     3: 2001 10006 10012
     4: 2001 10008 10002
     5: 2002 10011 10009
     6: 2002 10009 10005
     7: 2002 10010 10004
     8: 2002 10002 10010
     9: 2003 10012 10007
    10: 2003 10003 10001
    11: 2003 10005 10003
    12: 2003 10001 10006
    

    附带说明:当使用随机值生成器(如samplernorm)提供样本数据时,最好使用set.seed()

    使用的数据:

    set.seed(2004)
    DT = data.table(yr = sample(3)+2000, a1 = sample(12), a2 = sample(12))[order(yr)]
    DTmap = data.table(a1 = 1:12, b=10001:10012)
    

    【讨论】:

    • 完美。正是我想要的。
    • 知道了。下次我会使用 set.seed() 。在第一次加入@Frank 的解决方案时,为什么要使用 i.b?它只适用于那里的 b 。第二次,我明白了,否则它将是一个重复的列。
    • @JesseBlocher i. 前缀指的是i 中的列;基本上i 是您指定使用索引向量或参考数据表选择哪些行的位置(see here 了解更多信息 i 的含义)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多