我有大型数据集,其中的行测量相同的东西(基本上是带有一些噪音的重复)。作为我正在编写的更大函数的一部分,我希望用户能够使用他们选择的函数(例如,均值,中值)来折叠这些行。
我的问题是,如果我直接调用该函数,速度比使用match.fun(这是我需要的)快得多。 MWE:
require(data.table)
rows <- 100000
cols <- 1000
dat <- data.table(id=sample(LETTERS, rows, replace=TRUE),
matrix(rnorm(rows*cols), nrow=rows))
aggFn <- "median"
system.time(dat[, lapply(.SD, median), by=id])
system.time(dat[, lapply(.SD, match.fun(aggFn)), by=id])
在我的系统上,最后两行的计时结果:
user system elapsed
1.112 0.027 1.141
user system elapsed
2.854 0.265 3.121
对于更大的数据集,这变得非常引人注目。
最后一点,我意识到aggregate()可以做到这一点(并且似乎没有这种行为),但由于数据大小,我需要使用data.table对象。
答案 0 :(得分:3)
原因是gforce优化data.table适用于median
。如果设置options(datatable.verbose=TRUE)
,则可以看到。有关详细信息,请参阅help("GForce")
。
如果您比较其他功能,您会得到更多相似的时间:
fun <- median
aggFn <- "fun"
system.time(dat[, lapply(.SD, fun), by=id])
system.time(dat[, lapply(.SD, match.fun(aggFn)), by=id])
如果函数恰好得到支持,使用优化的可能解决方法是使用它来评估表达式构建,例如,使用可怕的eval(parse())
:
dat[, eval(parse(text = sprintf("lapply(.SD, %s)", aggFn))), by=id]
但是,使用match.fun
添加会丢失小安全性。
如果您有用户可以选择的功能列表,您可以这样做:
funs <- list(quote(mean), quote(median))
fun <- funs[[1]] #select
expr <- bquote(lapply(.SD, .(fun)))
a <- dat[, eval(expr), by=id]