函数内循环执行线性回归,如何将残差存入原DataFrame?
问题:线性回归残差无法存入原始数据框
需求是对多个因变量执行线性回归,将每个模型的残差以[因变量名].res作为列名添加到原始数据框中,但执行自定义函数后,原始数据框df并未出现预期的新列。
原始代码如下:
library(tidyverse) library(stringr) set.seed(5) df <- data.frame( id = c(1:100), age = sample(20:80, 100, replace = TRUE), sex = sample(c("M", "F"), 100, replace = TRUE, prob = c(0.7, 0.3)), type = sample(letters[1:4], 100, replace = TRUE), bmi = sample(15:35, 100, replace = TRUE), sbp = sample(75:160, 100, replace = TRUE), cat_outcome1 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.68, 0.32)), cat_outcome2 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.65, 0.35)), cat_outcome3 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.60, 0.40)), cat_outcome4 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.45, 0.55)), dog_outcome1 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.68, 0.32)), dog_outcome2 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.65, 0.35)), dog_outcome3 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.60, 0.40)), dog_outcome4 = sample(c(0L, 1L), 100, replace = TRUE, prob = c(0.45, 0.55)) ) outcome = colnames(df)[str_detect(colnames(df), "outcome")] test_function = function(vars_dep, vars_indep, input_data){ for (z in vars_dep) { formula = as.formula(paste0(z, " ~ ", vars_indep)) model = lm(formula, data = input_data, na.action = na.exclude) # Take the residual from each model, create a new col with the suffix '.res' input_data[, paste0(z, ".res")] = residuals(model) } }
原因分析
R的函数默认采用传值调用,函数内部的input_data是原始数据框df的副本,对副本的修改不会影响外部的原始数据框。函数执行完毕后,副本被销毁,因此原始df没有变化。
解决方案
方案1:让函数返回修改后的数据框
修改函数,最后返回处理后的input_data,然后重新赋值给df:
test_function = function(vars_dep, vars_indep, input_data){ for (z in vars_dep) { formula = as.formula(paste0(z, " ~ ", vars_indep)) model = lm(formula, data = input_data, na.action = na.exclude) input_data[, paste0(z, ".res")] = residuals(model) } # 返回修改后的数据框 return(input_data) } # 调用函数并重新赋值给df df <- test_function(outcome, "age + sex + bmi + sbp", df)
方案2:使用tidyverse风格的代码(更简洁)
利用dplyr::across批量处理所有因变量,避免循环,代码更符合tidyverse规范:
df <- df %>% mutate(across(all_of(outcome), ~ residuals(lm(.x ~ age + sex + bmi + sbp, data = cur_data())), .names = "{col}.res"))
这里all_of(outcome)指定要处理的因变量列,.names = "{col}.res"自动生成带.res后缀的新列名,cur_data()确保lm使用当前数据框的上下文。
方案3:直接修改全局环境的变量(不推荐)
可以用<<-操作符直接修改全局环境中的df,但这种方式会破坏函数的封装性,容易引发副作用,不建议在生产代码中使用:
test_function = function(vars_dep, vars_indep){ for (z in vars_dep) { formula = as.formula(paste0(z, " ~ ", vars_indep)) model = lm(formula, data = df, na.action = na.exclude) df[, paste0(z, ".res")] <<- residuals(model) } } test_function(outcome, "age + sex + bmi + sbp")
执行上述任一方案后,df中就会出现cat_outcome1.res、cat_outcome2.res等预期的残差列。
内容的提问来源于stack exchange,提问作者celilati
相关产品推荐
相关产品推荐

