使用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
都可能非常大。
非常感谢
答案 0 :(得分: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的回答。
答案 1 :(得分: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 / 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
确保相同的行顺序验证结果:
{{1}}
答案 2 :(得分: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%
仍会执行矢量扫描。帮助页面的“注释”部分显示:
当前的实现不使用有序键。
所以这不能回答你的问题。