【问题标题】:Vectorization using apply family of functions in R使用 R 中的应用函数族进行矢量化
【发布时间】:2020-08-18 01:20:56
【问题描述】:

我正在使用线性回归模型,我想手动计算一些性能指标。我使用 Leave One Out Cross-Validation (LOOCV) 拆分数据。

下面的 R 代码给了我想要的结果,但需要很长时间,因为我将 for 循环与 LOOCV 一起使用。

有没有一种方法可以快速重写我的代码,例如,在 R 中应用一系列函数?

数据集从here上传

wdbc <- read_excel("Folds5x2_pp.xlsx") 
wdbc[] <- lapply(wdbc, scale)

   dim(wdbc)

9568    5

   head(wdbc)

1 -0.629 -0.987  1.82  -0.00952  0.521
2  0.742  0.681  1.14  -0.975   -0.586
3 -1.95  -1.17  -0.185  1.29     2.00 
4  0.162  0.237 -0.508  0.228   -0.462
5 -1.19  -1.32  -0.678  1.60     1.14 
6  0.888  0.404 -0.173 -0.996   -0.627

fitted_value <- rep(0,nrow(wdbc))

for(i in 1:nrow(wdbc)){
test<-wdbc[i,]
training<-wdbc[-i,]
m=lad(PE ~ ., data=training, method="BR")

co.data = coef(m)
x = cbind(1, as.matrix(test[, !(colnames(test) %in% "PE")]))
fitted_value[i] <- x %*% co.data
    }

R2<-(cor(wdbc$PE,fitted_value)^2) 
SAD<-sum(abs(wdbc$PE-fitted_value))

c(round(SAD,2) ,round(R2,2))

注意 1

问题中使用的数据仅用于解释,因为在我的项目中,我有许多高维度的数据集。

编辑

根据@Dominic van Essen 的回答,我使用parallel 包中的parSapply 函数使用以下R 代码,但它比for 循环花费更多时间。

library(parallel)

mycluster=makeCluster(detectCores()-1) 
wdbc <- read_excel("Folds5x2_pp.xlsx") 
wdbc[] <- lapply(wdbc, scale)
clusterExport(mycluster,c("lad","wdbc")) 

fitted_value = parSapply(mycluster,seq_len(nrow(wdbc)),function(i) {
    for(i in 1:nrow(wdbc)){
    test<-wdbc[i,]
    training<-wdbc[-i,]
    m=lad(PE ~ ., data=training, method="BR")

    co.data = coef(m)
    x = cbind(1, as.matrix(test[, !(colnames(test) %in% "PE")]))
  }
    return (x %*% co.data)
})

注意 2

我有 8 个核心,“PE”是我数据集中的因变量。

【问题讨论】:

  • 简单评论一下:apply 系列函数不会对 R 中的操作进行矢量化处理。它们是完成任务的便捷包装函数,但您通常不会看到显着的速度提升。
  • @bzki,我明白了。那么,您认为有一种方法可以加快我的代码速度吗?因为我有许多不同维度的数据集,这将花费太多时间。
  • 很遗憾,我不知道您的问题的答案(比我更有知识的人可以回答)。对于具有最小二乘拟合的线性回归,有一种简单的方法可以通过仅对模型进行一次拟合来获得 LOO CV 误差:robjhyndman.com/hyndsight/loocv-linear-models。我不知道最小绝对偏差拟合是否有类似的东西。

标签: r machine-learning regression vectorization apply


【解决方案1】:

您可以使用 sapply 而不是 for... 轻松地重新编写循环,尽管正如 bzki 评论的那样,仅此一项不会加速您的代码:

# sapply version:
fitted_value = sapply(seq_len(nrow(wdbc)),function(i) {
    # put all the gubbins in here
    # ...
    return (x %*% co.data)
})

但是,如果您的计算机上有多个可用内核,或者 - 甚至更好 - 访问具有许多处理器的服务器,则可以使用“并行”包中的 parSapply 轻松并行化 sapply 循环,如如本例所示:

# slow sapply loop (takes 12s):
data=123
answer = sapply(1:12,function(i) {
    Sys.sleep(1)
    return(data+i)
})
# faster parallel version (takes 4s on my laptop with 4 cores):
library(parallel)
mycluster=makeCluster(detectCores()-1) # leave 1 core available for system 
data=123
clusterExport(mycluster,"data") # specify variable(s) that should be available to parallel function
answer = parSapply(mycluster,1:12,function(i) {
    Sys.sleep(1)
    return(data+i)
})
stopCluster(mycluster)

【讨论】:

  • 这种并行版本的函数是否也可用于其他包(例如 tidyverse)?
  • 您可以使用 furrr 包,它允许您使用 purrr 包中的 map、map2、pmap 等并行函数。
  • @jeza - 这很烦人,不幸的是我不熟悉特定的 lad()coef() 函数,如果这些是你的问题的一部分,你可以使用它们来提供帮助。但我有几个问题: 1. 您是否检查过您的计算机是否有多个可用于并行化的内核(detectCores() 的输出是什么?); 2.你的函数中PE是什么?它是您还应该为并行函数提供的另一个变量吗?
  • @Dominic van Essen,我有 8 个核心,“PE”是我数据集中的因变量。
  • @jeza - 好的 - 我没有注意到这一点。您不需要 for... 循环。 parSapply 应该自己做。尝试简单地删除for... 行和底部对应的}
猜你喜欢
  • 2018-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-27
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 2015-08-25
相关资源
最近更新 更多