如何在R中用map()实现无循环的交叉验证变量筛选?
问题场景
我将N个训练集存入列表dat:
dat <- map(tscv$train, ~subset(data, select = .x))
针对每个训练集,使用glmnet包运行Lasso回归并设置100个lambda值,得到每个训练集对应不同lambda的变量子集列表selected:
selected <- map(dat, ~coef.glmnet(glmnet(.x[ , names(Xlagged)], .x[ , "errs"], alpha = 1, lambda = lambdas)))
selected是嵌套列表,例如selected$Training1930$s0对应Training1930训练集在s0正则化参数下的最优变量子集。现在需要针对每个lambda值,对dat中的所有训练集筛选对应变量。
尝试以下代码时出现报错:
dat |> map(~subset(.x, select = map_depth(selected, 2, ~.)))
错误信息:
Error in which(!as.logical(j)) :
'list' object cannot be coerced to type 'logical'
已编写可行的函数但用到了for循环,希望换成更简洁的purrr风格写法:
a <- function(nom, s){ return(dat[[nom]][, selected[[nom]][[s]]]) } mylist <- list() for(s in names(selected$Training1638)) { mylist[[s]] <- print(map(names(dat), ~a(.x, s))) }
可复现代码如下:
tscv <- list() tscv[["train"]] <- list() tscv[["train"]][["Training1"]] <- c(1,2,3,4) tscv[["train"]][["Training2"]] <- c(1,2,3,4,5) set.seed(1) full_data <- data.frame(replicate(10,rnorm(10))) dat <- map(tscv$train, ~full_data[.x, ]) # 模拟每个训练集对应10个lambda的变量子集 selected = list(list(list())) for (i in paste("s", 1:10, sep = "")) { selected[["Training1"]][[i]] <- sample(paste("X", 1:10, sep = ""), 3, rep = TRUE) selected[["Training2"]][[i]] <- sample(paste("X", 1:10, sep = ""), 3, rep = TRUE) }
解决方案
可以利用purrr::transpose调整selected的嵌套结构,再结合map2匹配训练集与变量子集,完全避免for循环:
library(purrr) # 转置selected结构:从「训练集→lambda」转为「lambda→训练集」 selected_transposed <- transpose(selected) # 遍历每个lambda分组,同步处理对应训练集和变量子集 mylist <- map(selected_transposed, ~map2(dat, .x, function(data_subset, vars) { data_subset[, vars] }))
更简洁的匿名函数写法:
mylist <- map(transpose(selected), ~map2(dat, .x, ~.x[, .y]))
逻辑说明
transpose(selected)将列表结构反转:原本selected$Training1$s1对应Training1的s1变量,转置后变为selected_transposed$s1$Training1,让同一lambda下所有训练集的变量子集处于同一层级。- 外层
map遍历每个lambda分组,内层map2同时遍历dat(训练集数据)和转置后的selected(当前lambda下各训练集的变量子集),直接提取对应列完成筛选。
内容的提问来源于stack exchange,提问作者entropy
相关产品推荐
相关产品推荐

