【问题标题】:Find values in a given interval without a vector scan无需矢量扫描即可查找给定区间内的值
【发布时间】:2013-05-21 09:10:18
【问题描述】:

使用 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

IDT 都可以很大。 非常感谢

【问题讨论】:

  • 您是否将自己限制为整数值键?
  • 不,可以订购的任何类型都在我的问题范围内。为什么?
  • 浮点数更成问题,仅此而已。
  • 对不起,我不清楚。我想说@user1935457 提出的代码也适用于浮点数。

标签: r data.table intervals


【解决方案1】:

这是@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 的回答。

【讨论】:

    【解决方案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:n 的随机子集IT,间隔表是ni = n / 101:n 中的非重叠间隔。对所有 ni 间隔进行重复矢量扫描需要一段时间:

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

    可以在区间端点上进行两次滚动连接(参见?data.table 中的roll 参数),一举搞定一切:

    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
    

    【讨论】:

    • 非常感谢您的回答。我只是有点担心:target.low &lt;- IT[DT, roll = Inf, nomatch = 0] 行创建了一个与 DT 具有相同行数的结构。因此计算的复杂度为n*log(i),其中i=nrow(IT)n=nrow(DT)。然而,我们可以轻松地将这段代码转换为i*log(n) 复杂度,这在大多数情况下是一个优势 n>>i。我将代码发布在单独的答案中。无论如何,我将您的答案标记为已接受,因为您提供了所有关键想法。
    【解决方案3】:

    如果您不想进行全矢量扫描,则应首先将变量声明为 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% 仍然进行矢量扫描。帮助页面的注释部分说:

    当前实现不使用有序键。

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

    【讨论】:

    • 这些仍然是矢量扫描!
    • 非常感谢您的回答。我只是感觉使用有序键+滚动连接应该很容易定位区间的上下界,然后得到中间的所有值。但是我没有注意到data.table 包中的between 函数。如果这个函数做矢量扫描,这可能意味着我寻找的功能还没有实现。
    猜你喜欢
    • 2020-08-17
    • 1970-01-01
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多