【发布时间】:2015-08-11 05:01:18
【问题描述】:
我希望使用data.table 来提高给定函数的速度,但我不确定我是否以正确的方式实现它:
数据
给定两个data.tables(dt 和 dt_lookup)
library(data.table)
set.seed(1234)
t <- seq(1,100); l <- letters; la <- letters[1:13]; lb <- letters[14:26]
n <- 10000
dt <- data.table(id=seq(1:n),
thisTime=sample(t, n, replace=TRUE),
thisLocation=sample(la,n,replace=TRUE),
finalLocation=sample(lb,n,replace=TRUE))
setkey(dt, thisLocation)
set.seed(4321)
dt_lookup <- data.table(lkpId = paste0("l-",seq(1,1000)),
lkpTime=sample(t, 10000, replace=TRUE),
lkpLocation=sample(l, 10000, replace=TRUE))
## NOTE: lkpId is purposly recycled
setkey(dt_lookup, lkpLocation)
我有一个函数可以找到包含thisLocation 和finalLocation 的lkpId,并且具有“最近的”lkpTime(即thisTime - lkpTime 的最小非负值)
功能
## function to get the 'next' lkpId (i.e. the lkpId with both thisLocation and finalLocation,
## with the minimum non-negative time between thisTime and dt_lookup$lkpTime)
getId <- function(thisTime, thisLocation, finalLocation){
## filter lookup based on thisLocation and finalLocation,
## and only return values where the lkpId has both 'this' and 'final' locations
tempThis <- unique(dt_lookup[lkpLocation == thisLocation,lkpId])
tempFinal <- unique(dt_lookup[lkpLocation == finalLocation,lkpId])
availServices <- tempThis[tempThis %in% tempFinal]
tempThisFinal <- dt_lookup[lkpId %in% availServices & lkpLocation==thisLocation, .(lkpId, lkpTime)]
## calcualte time difference between 'thisTime' and 'lkpTime' (from thisLocation)
temp2 <- thisTime - tempThisFinal$lkpTime
## take the lkpId with the minimum non-negative difference
selectedId <- tempThisFinal[min(which(temp2==min(temp2[temp2>0]))),lkpId]
selectedId
}
尝试解决方案
我需要为dt 的每一行获取lkpId。因此,我最初的直觉是使用*apply 函数,但是当n/nrow > 1,000,000 时(对我来说)太长了。所以我尝试实现data.table 解决方案,看看它是否更快:
selectedId <- dt[,.(lkpId = getId(thisTime, thisLocation, finalLocation)),by=id]
但是,我对data.table 还很陌生,与*apply 解决方案相比,这种方法似乎没有任何性能提升:
lkpIds <- apply(dt, 1, function(x){
thisLocation <- as.character(x[["thisLocation"]])
finalLocation <- as.character(x[["finalLocation"]])
thisTime <- as.numeric(x[["thisTime"]])
myId <- getId(thisTime, thisLocation, finalLocation)
})
对于 n = 10,000,两者都需要约 30 秒。
问题
有没有更好的方法使用data.table 将getId 函数应用于dt 的每一行?
2015 年 12 月 8 日更新
感谢来自@eddi 的指针,我重新设计了我的整个算法并使用了滚动连接(a good introduction),从而正确使用了data.table。稍后我会写一个答案。
【问题讨论】:
-
我建议尽量减少示例数据,如果您设法在 10-20 行上显示问题,您将让更多用户能够调查问题。此外,您当前的解决方案在我的机器上引发了多个警告。因此,拥有少量示例数据,您还可以发布预期的输出。
-
@jangorecki 我的问题不是关于代码或函数的问题本身,而是询问是否有更好的方法在大型数据集上使用
data.table.对于这个例子,警告可以被忽略(它们是函数找不到答案的地方——没关系)。 -
data.table不会神奇地加速完全相同的循环。相反,您应该重新考虑您的算法 - 查找最近的时间很容易通过滚动连接完成,但我不确定您的初始“过滤”操作是什么。 -
@eddi 我想最初的“过滤器”是最初尝试减少在@987654348 的每次迭代中“查找”(并最初合并)的数据量的挂起@。感谢有关加入的提示;我会重新考虑整个算法
标签: r data.table