【问题标题】:Speed up vector-lookup for data.table加快 data.table 的向量查找
【发布时间】:2015-01-12 18:47:11
【问题描述】:

data.table 中的每一行,我需要从向量中找到最接近的较低数字。下面的最小工作示例可以完成这项工作,但速度太慢了,尤其是对于较长的 pre.numbers 向量(实际数据中约有 100 万个元素)。

library(data.table)
set.seed(2)
pre.numbers <- sort(floor(runif(50000, 1, 1000000)))
the.table <- data.table(cbind(rowid=1:10000, current.number=floor(runif(1000, 1, 100000)), closest.lower.number=NA_integer_))
setkey(the.table, rowid)
the.table[, closest.lower.number:=max(pre.numbers[pre.numbers<current.number]), by=rowid]

必须有更聪明的方法来做到这一点。向量数和data.table中的数字没有关系。

【问题讨论】:

  • 您可以将 minimal 示例的问题大小减少到几行,并使用set.seed 使其可重现。

标签: r data.table


【解决方案1】:

这个怎么样?使用 data.table 的滚动连接:

DT = data.table(pre = pre.numbers, 
       current.number = pre.numbers+0.5, key="current.number")
setkey(the.table, current.number)
ans = DT[the.table, roll=Inf, rollends=FALSE]

由于您处理的是整数,我刚刚添加了 0.5(0 到 1 之间的任何数字都可以)从 pre.numbers 创建 DT

最后一步执行 LOCF 滚动连接(最后一次观察结转)。对于current.number(键列)的每个值,在DTcurrent.number(键列)中查找匹配的行。如果没有匹配,则前滚最后一个观察值。如果匹配发生在开始/结束处,则结果为NA (rollends = FALSE)。

为了更好地说明发生了什么,请考虑以下案例:

# pre.numbers:
# c(10, 11)

# the.table:
# current.numbers
#               9
#              10
#              11

我们首先将 pre.numbers 转换为 DT,这将产生列

# DT:
# pre  current.numbers (key col)
#  10             10.5
#  11             11.5

对于the.table中的每个值:

#  9 -> falls before 10.5, LOCF and rollends = FALSE => result is NA
# 10 -> falls before 10.5 => same as above
# 11 -> falls between 10.5 and 11.5, LOCF matches with previous row = 10.5 
#       corresponding pre = 10.

HTH


这是我用来生成数据的代码:

require(data.table)
set.seed(1L)
pre.numbers = sort(floor(runif(50000, 1, 1000000)))
the.table = data.table(rowid=1:10000, current.number=floor(runif(1000, 1, 100000)))

【讨论】:

  • 我的想法正是如此,但无法制定正确的 rollrollends 组合......滚动连接对我来说仍然是最难理解的部分。
【解决方案2】:

这是一个矢量化的解决方案:

algo1 = function()
{
    vec     = the.table$current.number
    indices = findInterval(vec-0.1, pre.numbers)
    res     = ifelse(indices==0, 0, vec[indices])

    the.table$closest.lower.number = res
}

algo2 = function()
{
    setkey(the.table, rowid)
    the.table[, closest.lower.number:=max(pre.numbers[pre.numbers<current.number]), by=rowid]
}

在我的机器上:

t1 = system.time(algo1())
#> t1
#user  system elapsed 
#0.0      0.0     0.0 

t2 = system.time(algo2())
#> t2
#user  system elapsed 
#9.73    0.00    9.73 

【讨论】:

  • 谢谢,但我认为这将捕获最接近的较低或相等的数字。我严格需要最接近的较低(不等于),可以为此修改吗?
  • @Chris 也许findInterval(thetable$current.number*(1-.Machine$double.eps), pre.numbers)
  • 嗯 findInterval(4-.Machine$double.eps, c(1,2,4,5)) 返回 3。而 findInterval 的选项无法处理这一点。我将编辑我的帖子。
  • 您只使用整数吗?还是可以是任何数字?
  • 任何数字都可以,但我没有小数。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-01
  • 2013-01-02
  • 1970-01-01
  • 1970-01-01
  • 2013-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多