3

使用 R 包data.table,无需对数据进行全矢量扫描,就可以找到给定间隔内的值。例如

>DT<-data.table(x=c(1,1,2,3,5,8,13,21,34,55,89))
>my.data.table.function(DT,min=3,max=10)
   x
1: 3
2: 5
3: 8

哪里DT可以是一张很大的桌子。

额外的问题:是否可以对一组不重叠的间隔做同样的事情,例如

>I<-data.table(i=c(1,2),min=c(3,20),max=c(10,40))
>I
   i min max
1: 1   3  10
2: 2  20  40
> my.data.table.function2(DT,I)
   i  x
1: 1  3
2: 1  5
3: 1  8
4: 2 21
5: 2 34

两者都I可以DT很大。非常感谢

4

3 回答 3

3

这是@user1935457 提出的代码的变体(请参阅@user1935457 帖子中的评论)

system.time({

 if(!identical(key(DT), "x")) setkey(DT, x)
 setkey(IT, min)

 #below is the line that differs from @user1935457 
 #Using IT to address the lines of DT creates a smaller intermediate table
 #We can also directly use .I 
 target.low<-DT[IT,list(i=i,min=.I),roll=-Inf, nomatch = 0][,list(min=min[1]),keyby=i]
 setattr(IT, "sorted", "max")

 # same here
 target.high<-DT[IT,list(i=i,max=.I),roll=Inf, nomatch = 0][,list(max=last(max)),keyby=i]
 target <- target.low[target.high, nomatch = 0]
 target[, len := max - min + 1L]

 rm(target.low, target.high)
 ans.roll2 <- DT[data.table:::vecseq(target$min, target$len, NULL)][, i := unlist(mapply(rep, x = target$i, times = target$len, SIMPLIFY=FALSE))]
 setcolorder(ans.roll2, c("i", "x"))
})
#    user  system elapsed 
#    0.07    0.00    0.06 


system.time({ 
 # @user1935457 code
 })
#    user  system elapsed 
#    0.08    0.00    0.08 

identical(ans.roll2, ans.roll)
#[1] TRUE

这里的性能增益并不大,但随着越来越大,它会更加DT敏感IT。再次感谢@user1935457 的回答。

于 2013-05-27T06:00:24.597 回答
2

首先,vecseq它不会作为可见函数从 中导出data.table,因此它的语法和/或行为可能会在未来对包的更新中发生更改而不会发出警告。此外,除了最后的简单检查之外,这是未经测试的。identical

顺便说一句,我们需要一个更大的例子来展示与矢量扫描方法的区别:

require(data.table)

n <- 1e5L
f <- 10L
ni <- n / f

set.seed(54321)
DT <- data.table(x = 1:n + sample(-f:f, n, replace = TRUE))
IT <- data.table(i = 1:ni, 
                 min = seq(from = 1L, to = n, by = f) + sample(0:4, ni, replace = TRUE),
                 max = seq(from = 1L, to = n, by = f) + sample(5:9, ni, replace = TRUE))

DT,数据表是 的一个不太随机的子集1:nIT,区间表是 中的ni = n / 10非重叠区间1:n。对所有间隔进行重复矢量扫描ni需要一段时间:

system.time({
  ans.vecscan <- IT[, DT[x >= min & x <= max], by = i]
})
 ##  user  system elapsed 
 ## 84.15    4.48   88.78

可以在区间端点上执行两个滚动连接(请参阅 中的roll参数?data.table)以一举获得所有内容:

system.time({
  # Save time if DT is already keyed correctly
  if(!identical(key(DT), "x")) setkey(DT, x)

  DT[, row := .I]

  setkey(IT, min)

  target.low <- IT[DT, roll = Inf, nomatch = 0][, list(min = row[1]), keyby = i]

  # Non-overlapping intervals => (sorted by min => sorted by max)
  setattr(IT, "sorted", "max")

  target.high <- IT[DT, roll = -Inf, nomatch = 0][, list(max = last(row)), keyby = i]

  target <- target.low[target.high, nomatch = 0]
  target[, len := max - min + 1L]


  rm(target.low, target.high)

  ans.roll <- DT[data.table:::vecseq(target$min, target$len, NULL)][, i := unlist(mapply(rep, x = target$i, times = target$len, SIMPLIFY=FALSE))]
  ans.roll[, row := NULL]
  setcolorder(ans.roll, c("i", "x"))
})
 ## user  system elapsed 
 ## 0.12    0.00    0.12

确保相同的行顺序验证结果:

setkey(ans.vecscan, i, x)
setkey(ans.roll, i, x)
identical(ans.vecscan, ans.roll)
## [1] TRUE
于 2013-05-22T03:14:52.987 回答
0

如果您不想进行全矢量扫描,则应首先将变量声明为您的键data.table

DT <- data.table(x=c(1,1,2,3,5,8,13,21,34,55,89),key="x")

然后你可以使用%between%

R> DT[x %between% c(3,10),]
   x
1: 3
2: 5
3: 8

R> DT[x %between% c(3,10) | x %between% c(20,40),]
    x
1:  3
2:  5
3:  8
4: 21
5: 34

编辑:正如@mnel 指出的那样,%between%仍然进行矢量扫描。帮助页面的注释部分说:

当前实现不使用有序键。

所以这不能回答你的问题。

于 2013-05-21T10:23:53.943 回答