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

mxg2im7a  于 2022-12-24  发布在  其他
关注(0)|答案(1)|浏览(94)

R脚本和包含多个csv文件格式数据集的文件夹的简化版本都可以在我的GitHub Repository中找到,该Repository位于Link中。
在名为“LASSO代码”的脚本中,将一个文件夹加载到R中,文件夹中装满了N个csv文件格式的数据集,并将它们分配到一个名为“datasets”的列表中,然后运行以下代码来拟合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循环,它确实可以运行,但是在58,500个数据集上运行需要12个小时,这是无法接受的慢。

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(datasets, function(i)
+   lm(formula = Y ~ ., data = datasets))
Error in terms.formula(formula, data = data) : 
duplicated name 'X1' in data frame using '.'
rta7y2nd

rta7y2nd1#

有没有想过把整个事情并行化?
首先,您可以更简洁地定义代码。

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)
clusterExport(CL, c('lst'))

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倍。

    • 您的错误**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列是第一个重复的,其它列都是干净的。

  • 数据:*
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)))

相关问题