如何用向量化方式为嵌套数据批量应用模型并动态命名列表列?
动态为嵌套tibble添加命名模型列表列
问题背景
手动将多组模型应用到嵌套数据中,为tibble的列表列命名,但模型数量较多时手动编写效率极低,需要用向量化方式替代,核心难点是动态分配列名。目标是得到每行对应一个因变量的tibble,第一列为因变量字符向量,后续各列为对应命名的lm模型列表列。
示例代码(reprex)
library(tidyverse) set.seed(123) # 增加随机种子保证结果可复现 data <- mtcars |> mutate(c1 = rbinom(nrow(mtcars), prob=0.05, size = 1), c2 = rbinom(nrow(mtcars), prob = 0.1, size =1), c3 = rbinom(nrow(mtcars), prob = 0.5, size = 1)) candidates <- c("c1","c2","c3") covars <- c("disp","hp","drat","wt") outcomes <- c("mpg","qsec") dat_long <- data |> pivot_longer(cols=all_of(outcomes), names_to = "outcome", values_to = "value") dat_n <- dat_long |> group_by(cyl) |> nest() # 定义各个模型函数 c1_mod <- function(df){ lm(value ~ c1 + disp + hp, data = df) } c2_mod <- function(df){ lm(value ~ c2 + disp + drat, data = df) } c3_mod <- function(df){ lm(value ~ c3 + drat + wt, data = df) } # 手动添加模型列的方式(需优化) dat_n_manual <- dat_n |> mutate(c1 = map(data, c1_mod), c2 = map(data, c2_mod), c3 = map(data, c3_mod))
解决方案
以下三种简洁的向量化实现方式,核心是将模型函数与目标列名绑定,动态生成列:
方法1:使用mutate配合大爆炸运算符!!!
先将模型函数整理成命名列表,再用!!!将列表展开为mutate的参数,一次性生成所有模型列:
# 构造命名模型函数列表,键即为目标列名 model_funs <- list( c1 = c1_mod, c2 = c2_mod, c3 = c3_mod ) # 批量添加模型列 dat_n <- dat_n |> mutate(!!!map(model_funs, \(fun) map(data, fun)))
方法2:使用purrr::reduce逐列添加
适合需要逐步处理的场景,逐个遍历模型函数列表并动态添加列:
dat_n <- dat_n |> reduce(names(model_funs), \(current_dat, col_name) { current_dat |> mutate(!!sym(col_name) := map(data, model_funs[[col_name]])) }, .init = dat_n)
这里!!sym(col_name)将字符型列名转换为mutate可识别的符号,实现动态列名赋值。
方法3:生成模型列后绑定到原tibble
先单独生成所有模型列表列,再用bind_cols合并到原数据:
# 生成包含所有模型列的tibble model_cols <- map(model_funs, \(fun) map(dat_n$data, fun)) |> as_tibble() # 合并到原数据 dat_n <- dat_n |> bind_cols(model_cols)
原尝试失败的原因
- 循环传递的是索引而非模型函数,未正确引用目标模型;
- R函数参数为传值机制,循环内对
dat_n的修改不会影响外部变量; - 未使用动态列名语法(如
!!sym()),无法将字符转为合法列名。
内容的提问来源于stack exchange,提问作者Claire Welsh
相关产品推荐
相关产品推荐

