2017-04-20 43 views
2

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

的样本数据:

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 

我想列A1和A2 DT与DTmap映射映射。忽略它们只有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语法什么的范围内做到这一点,但我不能图出来。我是一个非常新的R编码器,但有很多其他语言的经验。 这可能吗?以上是相对较快的,但有很多种类的编码(通过setkey)。看起来,使用by()与索引然后通过引用更新这些值会更快。

回答

2

你可以使用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) 
+0

完美。正是我在找什么。 –

+0

明白了。下次我将使用set.seed()。在第一次加入@ Frank的解决方案时,为什么要使用i.b?它适用于那里。第二次,我明白,否则它会是一个重复的列。 –

+0

@JesseBlocher'i'前缀是指'i'中的列;基本上'i'是指定要用索引向量或引用data.table选择哪些行([请参阅此处](https://rawgit.com/wiki/Rdatatable/data.table/vignettes/datatable-intro .html)了解更多信息“我”的含义) – Jaap