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::clusterMap
, parLapply
的多元变体:
# 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/