r - 加速大型数据帧中的 3 列 R 搜索

标签 r optimization data.table

好吧。我对这个问题进行了大量编辑,以便 a) 使其更有意义,b) 反射(reflect)我对问题的看法。

我有两个数据集——我们称之为 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

这是我编写的一团困惑数据表:

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_timestampid2 位于 start_unixtimeend_unixtime 之间。
  5. ...在这里,我认为我做了一些严重错误的事情 - 因为在这一步,我似乎总是有两列,每列都标记为 id2 并包含相同的结果。因此,我选择一列...
  6. ...并指定它进行赋值。 (我不知道为什么要这样做两次。我发现 this SO post ,它使用了第二个 := ,而 this one 没有,我根本不知道为什么。

...这不起作用。 @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 个连接(二分搜索),然后进行一次向量扫描(这会在幕后创建一对长逻辑向量,因此并不理想)

我提供了一个简单但更大的示例数据集,并且有更多的复制。

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)

然后,我使用函数 Between ( data.table 包的一部分)对 unixtime 位于 之间的那些值进行子集化startunixendunix

据我所知,您将无法使用二分搜索来查找某些内容是否在某个范围内(但是@MatthewDowle,主要 data.table 包作者在 SO 上很活跃并且可能会跳到这里评论这是否可能或将来可能发生)

关于r - 加速大型数据帧中的 3 列 R 搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13908952/

相关文章:

r - 为什么knitr缓存的data.table`:=`失败?

r - ggplot2:更改条形图中每个方面的颜色

标记化时从法语冠词缩写中删除特殊撇号

algorithm - 可分离函数在多个维度上最小化更难的直观原因

c++ - 如果我不使用 <iostream> 的功能,我可以避免包含它吗?

R中data.table包中fread速度的原因

删除组中带有 NA 的行,假设该组包含至少一个非 NA 值

r - 在不加载的情况下列出 R 数据文件的内容

sorting - 如何根据一个向量的值对另一个向量进行排序

javascript - jQuery 选择器优化