使用 apply 函数替换在 R 中的 dfs 列表上迭代运行向后逐步回归的 for 循环,以减少计算时间

标签 r performance for-loop machine-learning feature-selection

R 脚本和包含多个 csv 文件格式数据集的文件夹的简化版本都可以在我的 GitHub 存储库中找到,该存储库位于此 Link 中。 .

在我名为“LASSO code”的脚本中,将充满 N 个 csv 文件格式数据集的文件夹加载到 R 并将它们全部分配给名为“数据集”的列表后,我运行了以下代码来适应 N 个 LASSO 回归,每个数据集一个:

set.seed(11)     # to ensure replicability
LASSO_fits <- lapply(dfs, function(i) 
               enet(x = as.matrix(select(i, starts_with("X"))), 
                    y = i$Y, lambda = 0, normalize = FALSE))

现在,我想为向后消除逐步回归复制相同的过程,我们将通过仅使用 stats 库中的 step() 函数)使用另一个 apply 函数而不是使用循环来保持简单。问题是这样的,我唯一知道如何做到这一点是通过首先建立来初始化或准备它,然后运行它:

set.seed(100)      # for reproducibility
full_fits <- vector("list", length = length(dfs))
Backward_Stepwise_fits <- vector("list", length = length(dfs))

然后才拟合所有 Backward_Stepwise_fits,但我无法弄清楚如何将 full_fits 和 Backward_Stepwise_fits 放入同一个 apply 函数中,我能想到的唯一方法是使用 for 循环并将它们堆叠在彼此内部,但这在计算上会非常低效。我将运行这两个数据集的数量 N 是 260,000!

我编写了一个确实可以运行的 for 循环,但花了 12 个小时才在 58,500 个数据集上完成运行,速度慢得令人无法接受。 我使用的代码如下:

set.seed(100)      # for reproducibility
for(i in seq_along(dfs)) {
  full_fits[[i]] <- lm(formula = Y ~ ., data = dfs[[i]])
  Backward_Stepwise_fits[[i]] <- step(object = full_fits[[i]], 
                        scope = formula(full_fits[[i]]),
                        direction = 'backward', trace = 0) }

我已尝试以下操作,但在控制台中收到相应的错误消息:

> full_model_fits <- lapply(dfs, function(i)
+   lm(formula = Y ~ ., data = dfs))
Error in terms.formula(formula, data = data) : 
duplicated name 'X1' in data frame using '.'

最佳答案

有没有想过并行化整个事情?

首先,您可以更简洁地定义代码。

system.time(
  res <- lapply(lst, \(X) {
    full <- lm(Y ~ ., X)
    back <- step(full, scope=formula(full), dir='back', trace=FALSE)
  })
)
#  user  system elapsed 
# 3.895   0.008   3.897 

system.time(
  res1 <- lapply(lst, \(X) step(lm(Y ~ ., X), dir='back', trace=FALSE))
)
#  user  system elapsed 
# 3.820   0.016   3.833 

stopifnot(all.equal(res, res1))

结果相同,但没有时间差异。

现在,使用parallel::parLapply .

library(parallel)

CL <- makeCluster(detectCores() - 1L)

system.time(
  res2 <- parLapply(CL, lst, \(X) step(lm(Y ~ ., X), dir='back', trace=FALSE))
)
#  user  system elapsed 
# 0.075   0.032   0.861 

stopCluster(CL)

stopifnot(all.equal(res, res2))

在这台机器上大约快 4.5 倍。

如果我们现在想要使用 res2 运行向前逐步选择如scope= ,我们需要parallel::clusterMapparLapply 的多元变体:

# CL <- makeCluster(detectCores() - 1L)

res3 <- clusterMap(CL, \(X, Y) step(lm(Y ~ 1, X), scope=formula(Y), dir='forw', trace=FALSE),
                   lst, res2)

# stopCluster(CL)

注意:这产生了与使用 for 相同的系数。您在评论中显示的循环:

stopifnot(all.equal(lapply(FS_fits, coef), unname(lapply(res3, coef))))

您的错误 duplicated name 'X1' in data frame using '.'意味着,在您的某些数据集中有两列名为 "X1" 。以下是找到它们的方法:

names(lst$dat6)[9] <- 'X1'  ## producing duplicated column X1 for demo 

sapply(lst, \(x) anyDuplicated(names(x)))
# dat1  dat2  dat3  dat4  dat5  dat6  dat7  dat8  dat9 dat10 dat11 
# 0     0     0     0     0     9     0     0     0     0     0 
# ...

结果显示,数据集 dat6 9 th 列是(第一个)重复项。其他的都很干净。


数据:

set.seed(42)
n <- 50
lst <- replicate(n, {dat <- data.frame(matrix(rnorm(500*30), 500, 30))
cbind(Y=rowSums(as.matrix(dat)%*%rnorm(ncol(dat))) + rnorm(nrow(dat)), dat)}, simplify=FALSE) |> 
  setNames(paste0('dat', seq_len(n)))

关于使用 apply 函数替换在 R 中的 dfs 列表上迭代运行向后逐步回归的 for 循环,以减少计算时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74904943/

相关文章:

Javascript 索引性能

c# - 针对页面上特定用户操作的 ASP.NET MVC 网站代码(包括 Javascript)的性能分析

c++ - for循环的奇数输出

当路径包含 html/pdf 文档的 "~"但适用于 ioslides 时,R Markdown 编织失败

r - 更改 Hmisc summary() 返回的摘要变量

r - by() 在数据帧上应用均值函数时出错。发生了什么?

python - 枚举列表中的特定项目

根据之前的值替换值

performance - 是否可以改善功能容器的渐近性?

javascript - nodejs 中的 for 循环不适用于 redis hexists