我想更快地做到这一点:
set.seed(100)
pos <- sample(1:100000000, 10000000, replace=F)
df <- data.table(pos, name="arbitrary_string")
query <- sample(1:100000000, 10000, replace=F)
df_list <- lapply(query, function(x) subset(df, pos >= x - 10000 & pos <= x + 10000))
output <- rbindlist(df_list)
所以基本上,我循环遍历位置X的向量并从数据帧中提取每一行,该数据帧在&#34; pos&#34;落在定义为[X - 10000,X + 10000]的范围之间的列。我希望在&#34;输出&#34 ;;中可以多次表示一些行。这是可取的。排序不必与此问题中发布的代码返回的顺序相同。
这个玩具示例基于更大的数据集,我估计在上面编程的单核上运行需要大约10,000小时。因此,对这个问题有一个更快速的解决方案对我来说很有价值。我喜欢纯R解决方案,但我对涉及其他语言的解决方案持开放态度。
答案 0 :(得分:2)
在这个解决方案中,我将假设行排序并不重要(不幸的是,如果您需要原始帖子中的确切行排序,它将无法工作)。我建议的方法是:
cumsum
确定您需要来自pos变量中每个范围的元素的次数。df$pos
函数的单次调用,确定cut
的每个元素落入的范围。df
进行一次子集化。这种方法减少了扫描df
并抓取子集的次数,这应该会产生显着的加速。让我们从一个可重复的例子开始:
library(data.table)
set.seed(144)
pos <- sample(1:100000000, 10000000, replace=F)
df <- data.table(pos, name="arbitrary_string")
query <- c(100000, 101000, 200000)
现在,让我们确定每个范围内需要行的范围和次数:
query.cut <- rbind(data.frame(x=query-10000, y=1), data.frame(x=query+10001, y=-1))
query.cut <- query.cut[order(query.cut$x),]
query.cut$y <- cumsum(query.cut$y)
query.cut
# x y
# 1 90000 1
# 2 91000 2
# 4 110001 1
# 5 111001 0
# 3 190000 1
# 6 210001 0
我们将获取pos
值90000-90999一次的行,pos
值为91000-110000两次的行,pos
值为110001-111000的行一次,行为{{ 1}}值190000-210000一次。
要确定元素属于哪个范围,我们可以使用pos
函数,在cut
表中查找相关的复制数:
query.cut
对于我们当前的小查询,几乎所有行都不会被采用。最后一步是抓住每一行适当的次数。
num.rep <- query.cut$y[as.numeric(cut(df$pos, query.cut$x))]
num.rep[is.na(num.rep)] <- 0
table(num.rep)
# num.rep
# 0 1 2
# 9995969 2137 1894
即使使用相对较少的查询(在此处为300),我们也可以获得非常可靠的加速:
output <- df[rep(1:nrow(df), times=num.rep),]
随着查询集的大小增加,新方法的优势应该变得更大,因为它仍然仅通过OP <- function(query) {
df_list <- lapply(query, function(x) subset(df, pos >= x - 10000 & pos <= x + 10000))
rbindlist(df_list)
}
josilber <- function(query) {
query.cut <- rbind(data.frame(x=query-10000, y=1), data.frame(x=query+10001, y=-1))
query.cut <- query.cut[order(query.cut$x),]
query.cut$y <- cumsum(query.cut$y)
query.cut <- query.cut[!duplicated(query.cut$x, fromLast=T),]
num.rep <- query.cut$y[as.numeric(cut(df$pos, query.cut$x))]
num.rep[is.na(num.rep)] <- 0
df[rep(1:nrow(df), times=num.rep),]
}
set.seed(144)
big.query <- sample(df$pos, 300)
system.time(OP(big.query))
# user system elapsed
# 196.693 17.824 217.141
system.time(josilber(big.query))
# user system elapsed
# 3.418 0.124 3.673
进行一次传递,而原始方法为df$pos
中的每个元素进行一次传递{1}}(又称新方法渐近更快)。