You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数内循环执行线性回归,如何将残差存入原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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 16:01:29