【发布时间】:2017-10-24 17:34:49
【问题描述】:
我需要在包含因子和数字列的大型(许多 GB)表中反复查找“最近”行。使用dplyr,它看起来像这样:
df <- data.frame(factorA = rep(letters[1:3], 100000),
factorB = sample(rep(letters[1:3], 100000),
3*100000, replace = FALSE),
numC = round(rnorm(3*100000), 2),
numD = round(rnorm(3*100000), 2))
closest <- function(ValueA, ValueB, ValueC, ValueD) {
df_sub <- df %>%
filter(factorA == ValueA,
factorB == ValueB,
numC >= 0.9 * ValueC,
numC <= 1.1 * ValueC,
numD >= 0.9 * ValueD,
numD <= 1.1 * ValueD)
if (nrow(df_sub) == 0) stop("Oh-oh, no candidates.")
minC <- df_sub[which.min(abs(df_sub$numC - ValueC)), "numC"]
df_sub %>%
filter(numC == minC) %>%
slice(which.min(abs(numD - ValueD))) %>%
as.list() %>%
return()
}
这是上面的一个基准:
> microbenchmark(closest("a", "b", 0.5, 0.6))
Unit: milliseconds
expr min lq mean median uq max neval
closest("a", "b", 0.5, 0.6) 25.20927 28.90623 35.16863 34.59485 35.25468 108.3489 100
优化此功能以提高速度的最佳方法是什么?即使内存中有很大的df,也有空闲的RAM,但考虑到对这个函数的多次调用,我希望它尽可能快。
使用data.table 代替dplyr 会有帮助吗?
这是我迄今为止尝试过的两个优化:
dt <- as.data.table(df)
closest2 <- function(ValueA, ValueB, ValueC, ValueD) {
df_sub <- df %>%
filter(factorA == ValueA,
factorB == ValueB,
dplyr::between(numC, 0.9 * ValueC, 1.1 * ValueC),
dplyr::between(numD, 0.9 * ValueD, 1.1 * ValueD))
if (nrow(df_sub) == 0) stop("Oh-oh, no candidates.")
minC <- df_sub[which.min(abs(df_sub$numC - ValueC)), "numC"]
df_sub %>%
filter(numC == minC) %>%
slice(which.min(abs(numD - ValueD))) %>%
as.list() %>%
return()
}
closest3 <- function(ValueA, ValueB, ValueC, ValueD) {
dt_sub <- dt[factorA == ValueA &
factorB == ValueB &
numC %between% c(0.9 * ValueC, 1.1 * ValueC) &
numD %between% c(0.9 * ValueD, 1.1 * ValueD)]
if (nrow(dt_sub) == 0) stop("Oh-oh, no candidates.")
dt_sub[abs(numC - ValueC) == min(abs(numC - ValueC))][which.min(abs(numD - ValueD))] %>%
as.list() %>%
return()
}
基准测试:
> microbenchmark(closest("a", "b", 0.5, 0.6), closest2("a", "b", 0.5, 0.6), closest3("a", "b", 0.5, 0.6))
Unit: milliseconds
expr min lq mean median uq max neval cld
closest("a", "b", 0.5, 0.6) 25.15780 25.62904 36.52022 34.68219 35.27116 155.31924 100 c
closest2("a", "b", 0.5, 0.6) 22.14465 22.46490 27.81361 31.40918 32.04427 35.79021 100 b
closest3("a", "b", 0.5, 0.6) 13.52094 13.77555 20.04284 22.70408 23.41452 142.73626 100 a
这可以进一步优化吗?
【问题讨论】:
-
如何获取 C 和 D 的顺序索引并使用二进制搜索?
-
怎么样?我试过
setkey(dt, numC, numD),但似乎没有什么不同。 -
如果您可以并行查找这些(使用 ValueA、ValueB、ValueC、ValueD 的向量而不是单个值),我想您将能够比顺序查找快得多(即显然,您打算如何“重复”执行此操作,因为您正在以这种方式进行基准测试)。
-
感谢@Frank 的建议。我只能在有限的范围内并行化(例如,不是调用函数一百万次,而是使用长度为 5 的值向量调用它 200,000 次)。鉴于此,并行化会有所帮助吗?
标签: r performance dplyr data.table subset