第3章 LASSO 变量筛选、Logistic 重拟合与 Nomogram

现行主案例执行入口(2026-09-19)

Myocardial_Infarction_Complications的唯一执行根位于仓库 04_Cases/Myocardial_Infarction_Complications/,参数由analysis_config.R维护。按案例运行指南执行,不能复制本章技术Demo的变量、折数或数值替代主案例。

任务 主案例脚本
读取与描述、固定成员 L01_Read_Check_Demo.R、L02_Data_Preparation.R
LASSO lambda.min按原始变量筛选、普通Logistic L03_Feature_Selection_Logistic.R
完整训练内筛选/调参的外层OOF L04_Internal_Validation.R
共享特征的RF/XGBoost/SVM与SHAP L05_ML_Comparison_SHAP.R
第5课集中纠错、Kappa冻结与留出对照 L06_Data_Audit_T0_Correction.R
单变量整体LRT与AIC机制展示 Teacher_Selection_Demonstrations.R
冻结系数列线图及验证 Export_Classroom_Artifacts.R、Validate_Review_Run.R、Check_Separation.py
Python完整统计主链快速展示 Export_Python_Raw.R、Python_Statistical_Workflow.py

Python演示保留原始缺失与固定成员,在各训练范围独立筛查、插补和选择lambda.min,再按原始变量拟合普通Logistic,训练OOF最大Kappa阈值冻结后用于测试。使用scikit-learn 1.9的SAGA,与R glmnet内层实现不同,不要求数值完全一致;R维护正式四模型比较。完整脚本课前预跑,最后一课仅快速展示数据→描述→筛选→拟合→评价,课后可依据代码与浓缩讲义复现。

All111是异常教学反例;Corrected执行已确认的时间、稀疏/零格、无变异和极低观测比例筛查。旧版T0_102不是最终纠正版。不要用已全训练插补的矩阵做CV,不从测试选变量、调参或选阈值。具体统计边界见教学契约。

通用技术Demo与原有代码片段

以下Demo_R_Project及其数值属于独立技术示范,保留作为片段查阅;本节默认值不覆盖上面的主案例配置。

本章解决什么问题:候选变量集已定、训练集已冻结之后,如何在训练集内做一次有纪律的变量筛选,并把筛选结果变成一个可解释、可报告、可画 Nomogram 的模型。主线只有一条:LASSO(Least Absolute Shrinkage and Selection Operator)选出非零 dummy → 映射回原始变量 → 无惩罚 Logistic 回归(Logistic Regression)重新拟合 → 报告 OR(Odds Ratio,比值比)与 95% CI(Confidence Interval,置信区间)→ 画 Nomogram。

本章定位: 本章精讲 Demo 脚本 Demo_R_Project/R/03_lasso_logistic_nomogram.R,回答三件事:LASSO 筛选的每一环在做什么、怎么让 Agent 原样运行它、运行后拿哪些数字核对。示例为技术 Demo:MASS::Pima.tr2(R Recommended package 直接加载,n = 300、事件 106,见 Demo_R_Project/results/run_summary.md),只用于演示代码与产物,不是第三个正式教学 Case——正式教学数据只有 Myocardial Infarction Complication(直播主案例)与 Early Stage Diabetes Risk Prediction(Mini Project),Demo 数字不进入任何正式教学口径。logit、OR、参照组等概念本身见讲义 Logistic 候选模型,本章只给最小提示,重心在代码与产物。

什么时候用

适合用本章流程的场景:

  • 候选变量相对事件数偏多、参数预算紧张(EPV,events per variable,每变量事件数不足),希望自动收缩而不是逐个凭 P 值取舍;
  • 需要一个透明、可解释、可审计的主线模型:筛选规则固定,能在交叉验证(CV,Cross-Validation)的每个训练折内被原样复制;
  • 变量中混有连续量与分类量,需要”dummy 级筛选、变量级报告”的规范映射。

不适合、请换章节的情况:

  • 候选变量很少且事件充足——直接按预设变量集拟合即可,筛选反而引入数据驱动的不稳定(讲义 Logistic 候选模型 的对照讨论);
  • 目标是纯预测性能、可接受黑箱——见第 5 章机器学习比较;
  • 想回答”这个模型好不好”——见第 4 章内部验证。本章产物是一个开发完毕的模型,本身不带任何性能证据。

输入与输出

输入(全部来自前序章节与单一配置源)

输入 说明
Demo_R_Project/data/derived/cohort_clean.csv 清理后队列(第 2 章产物)
Demo_R_Project/results/tables/split_manifest.csv 划分成员表;本章只读取 split == "train" 的行(Demo 实跑:train n = 211、事件 75)
Demo_R_Project/config/config.R 预测变量集合、结局 type 与正类 Yes、种子、lasso_lambda = "lambda.min"、inner_folds = 5

输出(全部在训练集范围内生成)

产物 内容
results/tables/lasso_coefficients.csv 全部 9 个 dummy 的 LASSO 惩罚系数(含被压到 0 的),仅用于筛选
results/tables/lasso_selected_terms.csv 入选 dummy 清单(7 行)
results/tables/lasso_selected_variables.csv dummy → 原始变量映射表(7 个变量)
results/tables/logistic_or_ci.csv 重拟合模型的 log(OR)、OR、95% CI、P 值(9 行,含截距)
results/figures/lasso_path.png / lasso_cv.png LASSO 系数路径图与交叉验证曲线
results/figures/logistic_or_forest.png / nomogram.png OR 森林图与 Nomogram
results/models/lasso_mainline.rds 训练集预处理对象 + 入选变量 + lambda + 最终 glm(供第 5 章锁定复用)
results/book_fragments/ch03_lasso_summary.md / ch03_logistic_summary.md 供教材引用的结果片段(本章”实际运行结果”一节直接 include)

每个产物都登记在 results/artifact_manifest.csv,可追溯生成脚本、来源数据与分析范围(training set)。

脚本与产物

唯一脚本:Demo_R_Project/R/03_lasso_logistic_nomogram.R(约 220 行),由 run_all.R 统一调度,也可在 Demo_R_Project 根目录单独执行 Rscript R/03_lasso_logistic_nomogram.R。六个工序依次是:

  1. 读取队列与成员表,只保留训练集行;
  2. 训练集预处理:中位数/众数插补、因子编码,参数只在训练集学习;
  3. cv.glmnet 做 LASSO,按配置取 lambda.min;
  4. 非零 dummy 映射回原始变量;
  5. 用入选原始变量重拟合无惩罚 glm,计算 OR 与 95% CI;
  6. 出四张图与 Nomogram,写教材片段。

预处理函数 fit_preprocessor() / apply_preprocessor()、设计矩阵函数 build_design() 与映射函数 map_terms_to_variables() 统一定义在 Demo_R_Project/R/00_setup.R——一处定义、多章复用,这是折内口径一致性的代码保障。依赖 glmnet、rms、ggplot2 等(Demo 实跑 R 4.5.2、glmnet 4.1.10;完整版本见 results/logs/sessionInfo.txt)。

让 Agent 执行

让 Agent 运行本脚本,标准说法是给出脚本路径、运行目录、约束与验收数字。本脚本只做训练集内的筛选与拟合,不做性能评价、不碰测试集。

让 Agent 运行本章脚本并核对产物。替换方括号后整体复制发送;完整版 Prompt 见 prompts/03_LASSO与Logistic.md。

【背景】我在做一个二分类预测模型项目。数据已完成清理与训练/留出测试划分,
有 split 成员表;预测变量集合、结局、正类、种子、lambda 规则集中在配置文件。
【当前任务】在 Demo_R_Project 根目录运行:Rscript R/03_lasso_logistic_nomogram.R
只做训练集内的 LASSO 变量筛选、原始变量映射、无惩罚 Logistic 重拟合、OR/CI 与 Nomogram。
先检查输入并给出简要计划,然后继续执行指定任务;不要在计划后等待用户再次确认。
只有输入冲突、字典冲突、必须改变方法或命中暂停条件时停止。
【约束】1. 原样运行,不改代码、种子、折数与 lambda 规则;
2. 只用 split == "train" 的行;插补/编码/标准化参数只在训练集学习;
3. LASSO 惩罚系数只用于筛选;最终效应量必须来自无惩罚 glm 重拟合,
   不得按 P 值对入选变量二次增删;
4. 不做单变量筛选或 Stepwise,更不允许把它们的入选变量与 LASSO 结果合并;
5. 结果数字以实际运行输出为准,不得凭记忆填写。
【输出要求】报告 4 张 CSV、4 张 PNG、lasso_mainline.rds 与两个 book fragment 的完整路径。
【验收】逐项报告:lambda.min;设计矩阵 dummy 总数、入选 dummy 数、映射回原始变量数、
最终系数数(含截距);入选变量名单;CI 方法;glm 收敛与分离标志。
【暂停条件】LASSO 无入选变量、glm 不收敛或出现分离警告、CI 非有限——
任一发生即停止并报告证据,不自行换方法。

运行成功后进入核对关口:按下文”验收清单”逐项对数。若数字不符,让 Agent 先贴证据(控制台输出、文件内容)再提修改,修改仅限环境与路径——不允许以”修复”为名改种子、lambda 规则或变量名单。

核心实现

三个数不能混:dummy 数、原始变量数、系数数

本章最容易乱的是三个计数。设计矩阵的列(dummy):连续变量各占一列,分类变量按非参照水平展开成多个 dummy;入选原始变量数:非零 dummy 映射回的变量个数;最终模型系数数:重拟合后公式里所有项(含截距、含分类变量的全部非参照水平)。本章实例是 9 个候选 dummy → 7 个入选 dummy → 7 个原始变量 → 9 个系数(含截距)——两个 7 是巧合,数的是三种不同的东西。核对任何产物前,先问”这个数在数什么”。

分类变量的保留规则必须钉死:任一非参照 dummy 入选,即保留整个原始变量。本例 bmi_cat 只有 bmi_ge30 的 dummy 入选,但重拟合时 bmi_cat 整体进入公式,两个非参照水平都会回到模型里——这保证编码结构与参照组不被筛选动作破坏。

LASSO:cv.glmnet 与 lambda.min

03_lasso_logistic_nomogram.R(摘录 1:设计矩阵与 cv.glmnet)
design <- build_design(train_imp, config$predictors)
X_train <- design$X
y_train <- train_imp$y
set.seed(config$seed)
cvfit <- glmnet::cv.glmnet(X_train, y_train, family = "binomial",
                           nfolds = config$inner_folds)
lambda_use <- cvfit[[config$lasso_lambda]]   # "lambda.min"
if (!is.finite(lambda_use)) stop("cv.glmnet returned non-finite ", config$lasso_lambda)
coef_mat <- as.matrix(stats::coef(cvfit, s = config$lasso_lambda))

三个细节。第一,glmnet 只接受数值矩阵,build_design() 用 model.matrix 展开因子、去掉截距列,并保存”列 → 原始变量”的映射 col_var——后面的变量映射全靠这张表;第二,种子与折数(5 折)都来自单一配置源 config.R,脚本里没有第二份;第三,lambda 规则锁定为 lambda.min,它不是”试到结果好看为止”的旋钮——要换 lambda.1se,改配置、留记录、全链条重跑。

从 dummy 回到原始变量

03_lasso_logistic_nomogram.R(摘录 2:非零 dummy 判定与变量映射)
coef_df$variable <- unname(design$col_var[coef_df$term])
coef_df$selected <- abs(coef_df$coefficient) > .Machine$double.eps

sel_terms_df <- coef_df[coef_df$selected, , drop = FALSE]
if (nrow(sel_terms_df) == 0L)
  stop("LASSO selected no variables at ", config$lasso_lambda,
       "; aborting (no silent fallback allowed).")
selected_vars <- map_terms_to_variables(sel_terms_df$term, design$col_var)
n_sel_dummies <- nrow(sel_terms_df)
n_sel_vars <- length(selected_vars)

入选判定用数值非零(.Machine$double.eps),不是”系数看起来很小就算剔除”。映射函数本身只有一行核心逻辑,定义在 00_setup.R:

00_setup.R(map_terms_to_variables 定义)
map_terms_to_variables <- function(selected_terms, col_var) {
  unique(unname(col_var[selected_terms]))
}

lasso_selected_variables.csv 落盘的就是这张映射表(每个变量带了哪些入选 dummy)。注意脚本的硬闸门:LASSO 一个变量都没选出来时直接停止,而不是”换个 lambda 再试试”——零入选是数据与方法的结论,需要人来决策,不是脚本静默绕过的理由。

Logistic 必须重新拟合

重要为什么不能用 LASSO 惩罚系数报告 OR

LASSO 的系数被人为向 0 收缩,是有偏估计,也没有可用的标准误、P 值与置信区间——它唯一的合法用途是筛选。要报告效应量(OR 与 CI),必须用入选变量重新拟合无惩罚的 glm,让估计与推断都回到标准 Logistic 框架。直接把惩罚系数取指数当 OR 报告,是本章最常见的硬伤。

03_lasso_logistic_nomogram.R(摘录 3:glm 重拟合与收敛/分离闸门)
fml <- stats::as.formula(paste("y ~", paste(selected_vars, collapse = " + ")))
separation_flag <- FALSE
glm_warnings <- character()
fit_glm <- withCallingHandlers(
  stats::glm(fml, data = train_imp, family = stats::binomial),
  warning = function(w) {
    glm_warnings <<- c(glm_warnings, conditionMessage(w))
    if (grepl("fitted probabilities numerically 0 or 1", conditionMessage(w)))
      separation_flag <<- TRUE
    invokeRestart("muffleWarning")
  })
if (length(glm_warnings) > 0L)
  log_msg("03 glm warnings: ", paste(glm_warnings, collapse = " || "))
if (!fit_glm$converged) stop("Final logistic model did not converge; aborting.")
if (separation_flag) stop("Separation detected in final logistic model; aborting (no silent method switch).")

公式由入选原始变量拼成(y ~ npreg + glu + bp + bmi + ped + age + bmi_cat),bmi_cat 作为因子整体进入,R 自动按参照水平展开 dummy。withCallingHandlers 把拟合中的警告拦截下来检查:不收敛或出现分离(fitted probabilities numerically 0 or 1)即停止并报告——不静默换 Firth、不悄悄删变量,换方法属于设计变更,必须先记录再决策。

OR 与 95% CI:profile likelihood 优先

03_lasso_logistic_nomogram.R(摘录 4:CI 计算与方法记录)
ci_method <- "profile_likelihood"
ci_mat <- tryCatch(suppressMessages(stats::confint(fit_glm)),
                   error = function(e) NULL)
if (is.null(ci_mat) || any(!is.finite(ci_mat))) {
  ci_method <- "wald"
  ci_mat <- stats::confint.default(fit_glm)
}
if (any(!is.finite(ci_mat)))
  stop("Non-finite confidence interval in final logistic model; aborting.")

confint() 默认给出 profile likelihood(轮廓似然)区间,在系数较大或样本不富裕时比 Wald 区间更稳;只有它失败才回退 Wald,并把实际方法写进 logistic_or_ci.csv 的 ci_method 列。核对产物时这一列必须是 profile_likelihood;CI 出现非有限值同样直接停止。

Nomogram:表达工具,不是验证证据

03_lasso_logistic_nomogram.R(摘录 5:rms Nomogram)
dd <- rms::datadist(train_imp)
old_dd <- getOption("datadist")
options(datadist = "dd")
fit_lrm <- rms::lrm(fml, data = train_imp, x = TRUE, y = TRUE)
nom <- rms::nomogram(fit_lrm, fun = stats::plogis, fun.at = seq(0.1, 0.9, 0.1),
                     lp = FALSE, funlabel = "P(type = Yes)")

rms::lrm() 用同一公式、同一数据重拟合(与 glm 结果一致,只是换成 rms 体系),datadist 提供变量分布摘要,fun = plogis 把总分映射回预测概率 P(type = Yes)。Nomogram 的价值是把模型变成一张可读、可手工打分的图;它不提供任何性能证据——模型好不好,是第 4 章内部验证回答的问题。

单变量筛选与 Stepwise:只作方法对照,不回流主模型

警告筛选纪律:一条主线,不接受混合名单

单变量 P 值筛选、Stepwise 与 LASSO 是三条常见筛选路线,它们在同一批数据上常给出不同名单——这种不一致本身就是数据驱动筛选不稳定的证据(对照讨论见讲义 Logistic 候选模型)。本代码库的纪律:

  • 主线只有 LASSO 一条链,入选集合以 lambda.min 处的非零 dummy 为准;
  • 单变量筛选与 Stepwise 的结果至多作为方法对照出现在讨论中,本章不提供对照实现;
  • 不得把对照路线的入选变量与 LASSO 结果求并集或交集;
  • 任何对照路线选出的变量都不得回流主模型。

理由有三:多重数据驱动选择会放大过拟合且无法审计;一条链才能在每个 CV 折内被完整复制(第 4 章逐折重做 LASSO,对照路线做不到同步复制);混合名单无法回答”这个变量是哪条规则放进来的”。想比较三条路线的结论差异,去读讲义;想让模型可审计、可复现,守住这一条链。

实际运行结果

以下数字全部由 R/03_lasso_logistic_nomogram.R 在 Demo 训练集(n = 211、事件 75)上真实运行生成并冻结;重跑 Demo 时若与这些数字不符,先按”验收清单”排查,不要默认”差不多就行”。

LASSO 筛选结果

LASSO 变量筛选摘要 · 本片段由 Demo 脚本自动生成,供教材引用

  • 设计矩阵 dummy 总数:9(含 bmi_cat 的非参照水平)。
  • cv.glmnet(family = binomial,5 折)选择 lambda.min = 0.0144691。
  • 入选 dummy 数:7;映射回原始变量数:7;最终 Logistic 系数数(含截距):9。
  • 入选原始变量:npreg、glu、bp、bmi、ped、age、bmi_cat。
  • 说明:LASSO 惩罚系数仅用于筛选;最终效应量来自无惩罚 glm 重拟合。

图 3.1 展示每个 dummy 的惩罚系数随 log(lambda) 收缩的路径,虚线位于 log(lambda.min);图 3.2 是 cv.glmnet 的 5 折交叉验证曲线,二项偏差的最低点对应 lambda.min = 0.0144691。

图 3.1: LASSO 系数路径图:每个 dummy 的惩罚系数随 log(lambda) 收缩,虚线位于 log(lambda.min)(Demo 训练集实跑)
图 3.2: cv.glmnet 交叉验证曲线:二项偏差随 log(lambda) 变化,最低点即 lambda.min(5 折,Demo 训练集实跑)

最终 Logistic 模型

最终 Logistic 模型 · 本片段由 Demo 脚本自动生成,供教材引用

无惩罚 glm 重拟合(LASSO 仅用于筛选)。CI 方法:profile_likelihood。

项 log(OR) OR 95% CI P 值
(Intercept) -7.9811 0.000 0.000-0.008 0.0000
npreg 0.1632 1.177 1.037-1.346 0.0138
glu 0.0352 1.036 1.023-1.051 0.0000
bp -0.0210 0.979 0.944-1.015 0.2480
bmi 0.0564 1.058 0.963-1.165 0.2413
ped 1.5766 4.839 1.259-20.001 0.0246
age 0.0189 1.019 0.977-1.063 0.3751
bmi_catbmi_25_29 -0.0861 0.918 0.190-5.298 0.9172
bmi_catbmi_ge30 0.8059 2.239 0.380-15.456 0.3862
  • 模型收敛:TRUE;分离警告:FALSE。

图 3.3 把上表画成森林图:log 刻度,误差线为 profile likelihood 95% CI,横线跨过 1 表示方向不确定。图 3.4 是基于同一模型画出的 Nomogram。

图 3.3: 最终 Logistic 模型的 OR 森林图:x 轴为 log 刻度,误差线为 profile likelihood 95% CI;横线跨过 1 表示方向不确定(Demo 训练集实跑)
图 3.4: 基于重拟合 Logistic 模型的 Nomogram:各变量在 Points 轴读分,加总为 Total Points 后映射到 P(type = Yes)(Demo 训练集实跑)

结果怎么读

  • 三个数的实际口径:候选 9 个 dummy(7 个连续变量 + bmi_cat 的 2 个非参照水平)→ lambda.min = 0.0144691 处 7 个非零 dummy(skin 与 bmi_catbmi_25_29 被压到 0)→ 7 个原始变量(skin 整体出局;bmi_cat 凭 bmi_ge30 一个 dummy 按规则保留)→ 重拟合 9 个系数(截距 + 6 个连续变量 + bmi_cat 的 2 个水平)。lasso_coefficients.csv 里 skin 的系数是 0、selected = FALSE,这就是”被剔除”的落盘形态。
  • OR 表读法:glu OR = 1.036(1.023–1.051)、npreg OR = 1.177(1.037–1.346)、ped OR = 4.839(1.259–20.001,区间很宽)有统计学关联;bp、bmi、age 的区间跨过 1——它们留在模型里是因为 LASSO 选了它们,预测模型的变量去留由筛选规则决定,不按 P 值二次筛选。bmi_cat 两个水平区间很宽,反映分组后格子样本有限,不作临床解释。
  • 图读法:路径图看”谁先被压到 0”;CV 曲线看最低点位置;森林图看区间与 1 的关系;Nomogram 按各变量读 Points、加总、映射概率。
  • Demo 数字的边界:bmi_cat 是技术演示派生变量,其分组切点是演示用固定规则,不解释为临床阈值;本页所有数字只用于复跑核对,不得外推为任何人群的风险证据。

验收清单

运行成功后按此表逐项核对(参考值为 Demo 实跑结果,出自 fragment 与 CSV):

核对项 参考值 不对时怀疑什么
lambda.min 0.0144691 种子或折数被改;glmnet 版本差异
设计矩阵 dummy 总数 9 因子水平变化;bmi_cat 没建成 3 水平
入选 dummy 数 7 lambda 规则被换成 lambda.1se
入选原始变量数与名单 7:npreg、glu、bp、bmi、ped、age、bmi_cat(无 skin) dummy → 变量映射丢失;把 dummy 数当变量数
最终系数数(含截距) 9 按入选 dummy 重拟合(bmi_cat 只剩一个水平,编码结构被破坏)
CI 方法 profile_likelihood(查 ci_method 列) 回退到了 Wald,需查原因
收敛与分离 收敛 TRUE、分离警告 FALSE 数据或编码异常
产物齐全 4 表 + 4 图 + lasso_mainline.rds + 2 个 fragment 路径或权限问题;脚本未跑完

常见错误

  1. 拿 LASSO 惩罚系数直接算 OR 报告。 惩罚系数有偏、无正式推断,只能用于筛选;效应量一律来自无惩罚 glm 重拟合。
  2. 按入选 dummy 重拟合。 症状:bmi_cat 只剩 bmi_ge30 一个水平,系数数对不上。正确做法是按原始变量重拟合,让分类变量所有非参照水平回到模型。
  3. 报告”入选 9 个变量”。 把 dummy 数当变量数;本章口径是 9 dummy → 7 变量 → 9 系数。
  4. 按 P 值删掉 bp、bmi、age。 这是对入选集合的二次数据驱动筛选;变量去留在 LASSO 一步已经完成。
  5. 让 glmnet 直接吃因子列。 glmnet 需要数值设计矩阵;必须先 model.matrix 展开并保留 col_var 映射,否则无法回到原始变量。
  6. 临时把 lambda.min 换成 lambda.1se“让模型更简洁”。 规则在 config.R 锁定;要改就改配置、留记录、全链条重跑。
  7. 用全队列(含测试集)跑 LASSO。 这是泄漏;本章脚本只读 split == "train" 的行,测试集直到第 5 章锁定后才被读取一次。
  8. 把 Nomogram 当模型好用的证据。 它是表达工具;性能证据只能来自第 4 章的折外预测与第 5 章的留出测试。
  9. 把单变量显著而 LASSO 未选的变量”补回来”。 对照路线不回流主模型(见上节纪律)。
  10. 分离警告后静默换方法。 脚本的设计是停止并报告;Firth、删变量、并格子都属于设计变更,先记录再决策。

复用时替换什么

  • 数据与结局:换 config.R 的 dataset、outcome、positive_class,并保证第 1、2 章产物(清理队列、成员表)已按新数据生成;
  • 预测变量集合与类型:predictors_numeric / predictor_factor;bmi_cat 是技术演示派生变量,正式项目里替换或删除其派生规则(bmi_cat_breaks / bmi_cat_labels);
  • 预处理细节:00_setup.R 的 fit_preprocessor() / apply_preprocessor()(插补值与因子水平规则);
  • lambda 规则与折数:lasso_lambda、inner_folds;
  • Nomogram 标签:funlabel、fun.at;
  • 换正式案例(Myocardial Infarction Complication 或 Early Stage Diabetes Risk Prediction,见 ../../04_Cases/)后必须整条 run_all.R 重跑,所有数字以新运行为准——Demo 数字只用于演示,不得沿用进正式案例。

相关资源

  • 本章脚本:Demo_R_Project/R/03_lasso_logistic_nomogram.R;预处理与映射函数:Demo_R_Project/R/00_setup.R;参数单一来源:Demo_R_Project/config/config.R
  • 产物登记与追溯:Demo_R_Project/results/artifact_manifest.csv;完整 Prompt:prompts/03_LASSO与Logistic.md
  • 概念讲义:Logistic 候选模型(logit/OR/筛选路线对照)、模型评价与内部验证(下一步)
  • Agent 协作:把任务交给 Agent、让 Agent 使用 R 与 Python
  • 扩展阅读:glmnet 包 https://cran.r-project.org/package=glmnet;rms 包 https://cran.r-project.org/package=rms
提示下一步

进入第 4 章:把本章整条链(训练集预处理 → LASSO → 原始变量映射 → Logistic 重拟合)放进每个 outer 训练折内完整重跑,用折外(out-of-fold, OOF)预测计算 ROC、校准与 DCA——内部验证评价的是这套流程的期望表现,而不是这一个模型的表观分数。