R语言for循环中如何索引动态命名的新建数据框列
问题描述
我使用R内置数据集mtcars作为示例,需要完成两项操作:
- 步骤1:通过for循环批量运行回归模型,所有模型自变量固定,因变量随循环迭代更换;
- 步骤2:对步骤1输出的每个回归模型的残差做正态得分变换。
初始实现代码
library(tidyverse) data("mtcars") # 逻辑说明 # Step 1: 因变量 ~ cyl + disp + hp + drat # Step 2: 对Step1得到的残差做变换 # 提取所有作为因变量的列名(排除2-5列的自变量) outcome = colnames(mtcars[, -c(2:5)]) for (i in outcome) { # Step 1: 循环更换因变量拟合模型 formula = as.formula(paste0(i, "~ cyl + disp + hp + drat")) model = lm(formula, data = mtcars, na.action = na.exclude) # 将每个模型的残差存为新列,列名加后缀.res mtcars[, paste0(i, ".res")] = residuals(model) # Step 2: 变换残差,存为加后缀.invn的新列 mtcars[, paste0(i, ".invn")] = qnorm((rank(mtcars[,get(paste0(i,".res"))],na.last="keep")-0.5)/sum(!is.na(mtcars[,get(paste0(i,".res"))]))) }
报错信息
运行代码到步骤2时抛出错误:
Error in get(paste0(i, ".res")) : object 'mpg.res' not found
排查过程
- 初步判断索引逻辑有问题:如果直接写固定列名
mtcars[, 'mpg.res']索引不会报错; - 场景限制:列名是随循环变量
i动态生成的,无法直接写死带引号的固定列名; - 已尝试方案:使用
eval(parse())拼接索引语句,但没有成功。
补充说明:我知道用
purrr::map或者apply系列函数可以更简单实现这个需求,但是我想搞懂for循环场景下这个问题的正确解法。
报错原因与解决方法
报错核心是get()函数的查找逻辑问题:get()默认会在当前运行环境中查找独立的命名对象,不会自动进入mtcars数据框内部查找列,而mpg.res是mtcars的列,不是环境里的独立变量,自然会报找不到对象的错。
这个场景完全不需要用get()或者eval(parse())这类容易出问题的非标准计算方案,R原生的[[索引就支持直接传入字符串类型的列名,完美适配动态生成列名的场景,修正后的代码如下:
library(tidyverse) data("mtcars") outcome = colnames(mtcars[, -c(2:5)]) for (i in outcome) { # 拟合模型 model_formula <- as.formula(paste0(i, "~ cyl + disp + hp + drat")) lm_model <- lm(model_formula, data = mtcars, na.action = na.exclude) # 提前生成动态列名,存为变量方便复用 res_col_name <- paste0(i, ".res") trans_col_name <- paste0(i, ".invn") # 保存残差,用[[索引传入动态列名 mtcars[[res_col_name]] <- residuals(lm_model) # 提前提取残差向量,避免反复索引数据框 res_vector <- mtcars[[res_col_name]] # 计算变换后的值 mtcars[[trans_col_name]] <- qnorm( (rank(res_vector, na.last = "keep") - 0.5) / sum(!is.na(res_vector)) ) }
几个注意点:
- 动态索引数据框列优先用
[[,比[,]更稳妥,不会出现意外的维度折叠问题; - 动态生成的列名可以提前存为字符串变量,不需要每次索引都重复写
paste0,减少代码冗余; - 后续计算需要重复用到的列值,可以提前提取为单独的向量,既简化代码,也能提升运行效率。
内容的提问来源于stack exchange,提问作者celilati
相关产品推荐
相关产品推荐

