加速大型data.frame中的3列R搜索

时间:2012-12-17 05:49:22

标签: r optimization data.table

好的。我已经对这个问题进行了大量编辑,以便a)使其更有意义,并且b)反映我对这个问题的看法。

我有两个数据集 - 让我们称之为set1和set2--每个约600万行。目前,我将它们作为data.tables加载到R中。

>set1<-data.table(read.csv('~/file1.csv', stringsAsFactors=F))
>setkey(set1, id1)
>head(set1)
     id1 start_unixtime end_unixtime seconds_diff        id2
1:  1674     1354741858   1354741858            0  227167461
2:  1674     1354752386   1354752951          565  227246263
3:  1674     1354764412   1354764412            0  227358796
4:  1674     1354773044   1354773776          732  227421295
5:  1674     1354778651   1354778651            0  227448774
6:  1674     1354810424   1354810424            0  227631113
>set2<-data.table(read.csv('~/file2.csv', stringsAsFactors=F))
>setkey(set2, id1)
>head(set2)
     id1    unix_timestamp event_name
1:  1674    1355202784           join
2:  1674    1354351118           join
3:  1674    1354349648           play
4:  1674    1354780517           join
5:  1674    1355278891           join
6:  1674    1354617262           join

要指出的一个有问题的细节:set2没有唯一键。只有每行的元组实际上是唯一的。在set1中,id2是唯一的。有趣的时光!

我执行的操作如下:对于set2中的每一行,我需要取unix_timestamp,找到set1start_unixtimestamp <= unix_timestamp <= end_unixtimestamp和id1匹配的行,然后将相应的set1.id2分配给set2中的相应行。 set2中的每一行都在set1中有一个条目,但并非set1中的每一行都在set2中有一个条目。一个id2可以分配给set2中的多个行。我最需要的是(注意:以下数据是假的,因为我还没有取得任何实际成功。):

>head(set2)
     id1    unix_timestamp event_name         id2
1:  1674        1355202784       join   227167461
2:  1674        1354351118       join   227157309
3:  1674        1354349648       play   227157309
4:  1674        1354780517       join   227157309
5:  1674        1355278891       join   271089456
6:  1674        1354617262       join   221729485

以下是数据表的 mess

set2[, id2 := set1[set2[, id1], list(start_unixtime, end_unixtime, id2)][(start_unixtime <= unix_timestamp & unix_timestamp <= end_unixtime), id2, by=id2]][, list(id2)][, id2:= id2]

谈谈我理解的事情:

  1. set2调用赋值运算符:=
  2. 右侧调用set1,它从set2的joining id1行开始。
  3. 选择了start_unixtimeend_unixtimeid2列。
  4. 从该结果中,完成第二组选择,得到id2 utc_timestamp id2介于start_unixtimeend_unixtime之间。< / LI>
  5. ...在这里,我认为我做了一件非常错误的事情 - 因为在这一步,我似乎总是有两列,每列都标有id2并且包含相同的结果。所以,我选择一栏......
  6. ...并指定它以进行分配。 (我不知道为什么会这样做两次。我找到了this SO post,它使用了第二个:=,而this one却没有,我根本不知道为什么。
  7. ......这不起作用。 @mnel提出了类似的内容:

    set2[set1, nomatch=0][unix_timestamp %between c(start_unixtime, end_unixtime, incbounds=T)]
    

    ...当我尝试使用他的测试数据时,它可以工作,但不能用我的数据。在我看来,我的数据可能是某种类型(字符?)data.table(或R句号)可能没有正确胁迫?我可能很密集,但我似乎无法弄清楚如何在as.integer()的指定列上调用data.table

    修改:是的,我的数据都是字符,我忘了data.table继承自data.frame。所以,有点set1$start_unixtime <- as.integer($set1$start_unixtime),至少我确定一切都是整数。但是,当我运行该命令时,我仍然得到这个:

    >head(set2)
    Empty data.table (0 rows) of 8 cols: id1,utc_timestamp,event_name,start_unixtime,end_unixtime,seconds_diff...
    

    加成 以下是我实际数据的摘要:

    set1 <-  as.data.table(list(id1 = c(1674L, 1674L, 1674L, 1674L, 1674L, 1674L), 
         start_unixtime = c(1354741858L, 1354752386L, 1354764412L, 1354773044L, 1354778651L, 1354810424L), 
         end_unixtime = c(1354741858L, 1354752951L, 1354764412L, 1354773776L, 1354778651L, 1354810424L), 
        seconds_diff = c(0L, 565L, 0L, 732L, 0L, 0L), 
        id2 = c(227167461L, 227246263L, 227358796L, 227421295L, 227448774L, 227631113L))
    set2 <- as.data.table(list(
        id1 = c(1674L, 1674L, 1674L, 1674L, 1674L, 1674L), 
        utc_timestamp = c(1354752431L, 1354780517L, 1354811978L, 1354824385L, 1354833271L, 1354862753L), 
        event_name = c("joinRegularTable_2", "joinRegularTable_2", "joinRegularTable_2", "joinRegularTable_2","joinRegularTable_2", "joinRegularTable_2"))
    

1 个答案:

答案 0 :(得分:3)

我不确定这会对您的数据有效,因为您可能需要发布更完整的示例,但以下内容可能会有效。它进行1次连接(二进制搜索),然后进行一次矢量扫描(在幕后创建几个长逻辑矢量,因此不太理想)

我提供了一个简单但更大的示例数据集,其中包含更多复制。

DT <- as.data.table(list(id1 = c(5L, 1L, 5L, 1L, 5L, 3L, 5L, 3L, 1L, 3L), 
    id2 = 1:10, startunix = 1:10, endunix = 5:14))

DA <- as.data.table(list(id1 = c(3L, 5L, 5L, 5L), unixtime = c(5L, 1L, 6L, 12L)))

setkey(DA,id1)
setkey(DT,id1)


DT[DA, nomatch=0][unixtime %between% c(startunix, endunix)]

   id1 id2 startunix endunix unixtime
1:   5   1         1       5        6
2:   5   3         3       7        6
3:   5   5         5       9        6
4:   5   7         7      11        6

要解释它在做什么,它是id1匹配,nomatch = 0表示不包括这些。这扩展到DA [J(5)]和DT [J(5)]中多行的所有组合 - 在这种情况下

 DA[J(5)]
   id1 unixtime
1:   5        1
2:   5        6
3:   5       12
> DT[J(5)]
   id1 id2 startunix endunix
1:   5   1         1       5
2:   5   3         3       7
3:   5   5         5       9
4:   5   7         7      11

因此,创建的合并数据集包含所有12种组合。 (4次3)

然后我使用函数betweendata.table包的一部分)来对unixtimestartunixendunix之间的值进行子集。

据我所知,您将无法使用二进制搜索来查找某个范围是否在某个范围内(但是@MatthewDowle,主data.table包作者在SO上处于活动状态并且可能会在此处跳转关于这是否可能或将来可能发表评论