复杂的data.table子集和向量操作

时间:2015-03-28 18:52:20

标签: r data.table

好的我有一个使用data.frames构建的复杂功能,并且为了加快速度,我转向了data.table。我对此完全陌生,所以我很困惑。无论如何,我已经做了一个更简单的玩具示例,我想做什么,但我无法弄清楚如何将其转换为data.table格式。以下是data.frame形式的示例:

    rows <- 10
    data1 <- data.frame(   id =1:rows,
                    a = seq(0.2, 0.55, length.out = rows),
                  b = seq(0.35, 0.7, length.out = rows),
                  c = seq(0.4, 0.83, length.out = rows),
                  d = seq(0.6, 0.87, length.out = rows),
                  e = seq(0.7, 0.99, length.out = rows),
                  f = seq(0.52, 0.90, length.out = rows)             
    )
    DT1 <- data.table(data1) #for later

    data2 <- data.frame(   id =3:1,
                   a = rep(3, 3),
                   d = rep(2, 3),
                   f = rep(1, 3)
    )
    m.names <- c("a", "d", "f")

    data1[match(data2$id, data1$id),m.names] <- data1[match(data2$id, data1$id),m.names] + data2[match(data2$id, data1$id),m.names]

所以请注意,在最后一步中,我希望在预先存在的数字和新数据之间执行添加,并将其矢量化为多个列。

在data.table格式中,我只是走了这么远:

    DT1[id %in% data2$id, m.names, with=FALSE]

这会选择我要添加的值,但之后我就输了。我将不胜感激任何帮助!

编辑:

好的我已经找到了它的一部分 - 我可以使用上面的最后一行代码来实现矢量化的添加部分,使用data2来存储添加的值,如下所示:

    data2[,m.names] <- data2[,m.names] + data.frame(DT1[id %in% data2$id, m.names, with=FALSE])

即使有250万行(在DT1中)和10,000行在data2和6个匹配列中,这只需要0.004秒,但我仍然需要将新数据2分配给数据1中相应的动态分配列

3 个答案:

答案 0 :(得分:3)

这是另一种方式,使用devel version data.table v1.9.5

require(data.table) ## v1.9.5+
setDT(data1)        ## data1 is now a data.table

cols1 = c("a", "d", "f")
cols2 = paste0("i.", cols1)
setkey(data1, id)   ## setkey and prepare for join
data1[data2, (cols1) := mapply(`+`, mget(cols1), mget(cols2), SIMPLIFY=FALSE)]
#     id         a         b         c    d         e         f
#  1:  1 3.2000000 0.3500000 0.4000000 2.60 0.7000000 1.5200000
#  2:  2 3.2388889 0.3888889 0.4477778 2.63 0.7322222 1.5622222
#  3:  3 3.2777778 0.4277778 0.4955556 2.66 0.7644444 1.6044444
#  4:  4 0.3166667 0.4666667 0.5433333 0.69 0.7966667 0.6466667
#  5:  5 0.3555556 0.5055556 0.5911111 0.72 0.8288889 0.6888889
#  6:  6 0.3944444 0.5444444 0.6388889 0.75 0.8611111 0.7311111
#  7:  7 0.4333333 0.5833333 0.6866667 0.78 0.8933333 0.7733333
#  8:  8 0.4722222 0.6222222 0.7344444 0.81 0.9255556 0.8155556
#  9:  9 0.5111111 0.6611111 0.7822222 0.84 0.9577778 0.8577778
# 10: 10 0.5500000 0.7000000 0.8300000 0.87 0.9900000 0.9000000

表单x[i] join 是在密钥列id上执行的。对于 data2 id列的每一行,都会找到 data1 中相应的匹配行。例如,对于来自 data2 id = 2,匹配的行是 data1 中的第二行。

在我们所有匹配的行之后,我们会在j中评估表达式,该表达式通过添加{col1中提供的 data1 列来更新{1}}和mget(cols1)

mget(cols2)是使用cols2前缀生成的,该前缀从 data.table i 中获取值 - 此处为 data2

HTH

答案 1 :(得分:1)

一种方法是在set循环中使用for,因为这涉及多个列。将第二个数据集转换为&#39; data.table&#39; (DT2),使用&#39; id&#39;设置密钥。列,并加入&#39; data1&#39;。为&#39; m.names&#39;创建列索引向量。加入后创建的数据集(&#39; indx1&#39;)和i.列中的列(&#39; indx2&#39;)。使用for循环,set NA元素在&#39; m.names&#39;列到&#39; 0&#39;,然后根据&lt; indx1&#39;汇总相应的列。和&#39; indx2&#39;。

DT2 <- as.data.table(data2)
DTNew <- setkey(DT2, id)[data1]
indx1 <- match(m.names, names(DTNew))
indx2 <- grep('i\\.', names(DTNew))

for(k in seq_along(indx1)){
  set(DTNew, i=which(is.na(DTNew[[indx1[k]]])), j= indx1[k], value=0)
  set(DTNew, i=NULL, j= indx2[k], value = DTNew[[indx1[k]]]+
                                      DTNew[[indx2[k]]])
  }

 res <- DTNew[,2:4 := NULL]
 setnames(res, names(data1))

使用修改过的&#39; data1&#39;

进行检查
  data1[match(data2$id, data1$id),m.names] <- data1[match(data2$id, 
       data1$id),m.names] + data2[match(data2$id, data1$id),m.names]
  all.equal(setDF(res), data1)
  #[1] TRUE

基准

On a 1e6 dataset, 

set.seed(24)
data1 <- cbind(id=1:1e6,as.data.frame(matrix(rnorm(1e6*10), ncol=10, 
         dimnames=list(NULL, letters[1:10])) ))
 set.seed(46)
 data2 <- data.frame(id= sample(1:1000, 100, replace=FALSE), 
             a= rnorm(100), d=rnorm(100), f= rnorm(100))
 m.names <- c("a", "d", "f")

 DT2 <- as.data.table(data2)
 system.time({
   DTNew <- setkey(DT2, id)[data1]
   indx1 <- match(m.names, names(DTNew))
   indx2 <- grep('i\\.', names(DTNew))

   for(k in seq_along(indx1)){
   set(DTNew, i=which(is.na(DTNew[[indx1[k]]])), j= indx1[k], value=0)
   set(DTNew, i=NULL, j= indx2[k], value = DTNew[[indx1[k]]]+
                                  DTNew[[indx2[k]]])
   }

  res <- DTNew[,2:4 := NULL]
  setnames(res, names(data1))
  })

 # user  system elapsed 
 # 0.082   0.005   0.086 

答案 2 :(得分:0)

好的,感谢@David Arenburg提出的建议。我已经稍微修改了它以获得以下我的首选解决方案

    text <- NULL
    for(i in 1:length(m.names)){
        text <- paste0(text, m.names[i], " = ", m.names[i], " + i.", m.names[i], ", ")
    }
    expr <- parse(text = paste0("\":=\"(", substr(text, 1, nchar(text)-2), ")" ))

    res2 <- DT1[data2, eval(expr)]