将函数列表应用于数据框中相应列的快速方法(矢量化?)

Fast way (vectorization?) to apply a list of functions to corresponding columns in a data frame

我有一个可变长度的命名列表,包含函数

mFunc <- list(A = identity, B = exp)

我也有一个 data.frame 名称是 mFunc:

名称的超集
dat <- data.frame(A = 1:3, B = 1:3, C = 1:3)

我想做的是将mFunc中的所有函数应用到dat中的相应列中。如果我手动做,我会做

dat$A <- mFunc$A(dat$A)
dat$B <- mFunc$B(dat$B)

预期结果为:

#   A         B C
# 1 1  2.718282 1
# 2 2  7.389056 2
# 3 3 20.085537 3

我正在考虑对 mFunc

的名称使用循环
library(plyr)
dat[, names(mFunc)] <- llply(names(mFunc), function(n) mFunc[[n]](dat[[n]]))

这给了我想要的结果。我想知道在不使用隐式循环的情况下是否有更快(矢量化)的方法?

基准

在讨论之后,我用 llplylapply 解决方案做了一些基准测试:

library(dplyr)
library(tidyr)
library(microbenchmark)

makeTestData <- function(n, m = 10) {
   ret <- matrix(1, m, n)
   as.list(as.data.frame(ret))
}

d1e7 <- makeTestData(1e7)
d1e6 <- makeTestData(1e6)
d1e5 <- makeTestData(1e5)
d1e4 <- makeTestData(1e4)

(mb <- microbenchmark(dplyr_1e7 = llply(d1e7,  sum),
                      base__1e7 = lapply(d1e7, sum),
                      dplyr_1e6 = llply(d1e6,  sum),
                      base__1e6 = lapply(d1e6, sum),
                      dplyr_1e5 = llply(d1e5,  sum),
                      base__1e5 = lapply(d1e5, sum),
                      dplyr_1e4 = llply(d1e4,  sum),
                      base__1e4 = lapply(d1e4, sum),
                      unit = "s"))

#  Unit: seconds
#       expr         min          lq        mean      median          uq        max neval  cld
#  dplyr_1e7 0.516720538 0.589467527 0.684496553 0.626970606 0.713114314 1.50305854   100    d
#  base__1e7 0.510537527 0.568317076 0.650109031 0.593912047 0.654385189 1.65846752   100   cd
#  dplyr_1e6 0.498767583 0.574463177 0.678273933 0.614690247 0.695204388 1.22767911   100    d
#  base__1e6 0.490427644 0.543392591 0.610153445 0.587542713 0.638059997 1.03797757   100   c 
#  dplyr_1e5 0.045505505 0.047664210 0.076620745 0.049840086 0.062213015 0.83327188   100  b  
#  base__1e5 0.043557401 0.046385212 0.079063845 0.050644790 0.079270672 0.54647818   100  b  
#  dplyr_1e4 0.004488338 0.004746704 0.005941600 0.004970133 0.005845892 0.03784612   100 a   
#  base__1e4 0.004290173 0.004527419 0.006759915 0.004698939 0.005140864 0.04752546   100 a

以及一些统计数据:

mb %>% group_by(expr) %>% summarise(mean = mean(time)) %>%
   separate(expr, c("system", "size"), sep = "\_+") %>%
   spread(system, mean) %>% mutate(ratio = base / dplyr)   

# Source: local data frame [4 x 4]

#    size      base     dplyr     ratio
#   (chr)     (dbl)     (dbl)     (dbl)
# 1   1e4   6759915   5941600 1.1377262
# 2   1e5  79063845  76620745 1.0318856
# 3   1e6 610153445 678273933 0.8995679
# 4   1e7 650109031 684496553 0.9497623

结论

因此,base解决方案对于大数据集快 6%,对于小数据集慢 13%(!)。两者的执行时间绝对相同。因此,我可以接受使用相同动词族的小延迟(即使存在:无论如何对我来说都不明显)。