Skip to content

01 · 新建目录与代码改造(检查点 B)

执行日期:2026-08-07 状态:⚠️ 主体完成,两处待决(见 §7)

这一步不产生科学结果,但它决定后面所有结果是不是在正确的代码上算出来的。 本步查出一个足以让整个"干净重跑"作废的问题(§3.1)。

★ 本页是「当天在流程 v3 下做了什么」的历史记录,不整页改写

本页所有 "v3" 字样保持原样——它们记录的是改代码那一刻依据的是哪版流程, 改成 "v4" 等于篡改记录。但其中一处的语义已被 v4 作废,必须点名:

本页写的v4 之后影响
§3.4 的 step4_survivor语义作废 —— v4 第 4 步不判存活、只分流,否决移到 4X⬜ 需改名(如 repl_flag_to_4X),32 / 29 的引用跟着改。尚未做
§3.4 的三分法本身repl_class / repl_strengthv4 沿用
§3.5 富集用「第 4 步存活集」⚠️ 改为用 4X 之后的集合输入集要跟着换,尚未做
§3.6 共定位=措辞升级关卡✅ v4 沿用
§7 tier 的三个选项✅ 用户已拍板 ①彻底删十文件 47 处,尚未开始

现行流程一律以 分析流程定稿 v4 为准。


一、新目录

运行目录(WSL)/home/research/mr-pipeline-r9v3,属主 research,1.5 M
编辑目录(F 盘)F:\project\mr-pipeline-r9v3,独立 git 仓(不 fork 旧仓历史)
来源mr-pipeline-r9_full_20260807.tar.zst 解出,不 cp 现场
results/空(解包时 --exclude='*/results/*'
data/两个软链原样保留,指向 /mnt/d/mrdata/与旧目录共用只读原始数据
校验analysis/ 脚本数 61 = 旧目录;F↔WSL 逐字节 137 相同 / 0 不同

1.1 ★ 编辑与运行分离(本轮新立的规矩)

WSL 默认用户是 lovejj,而流水线在 research 名下。由此定死单向规则:

只在 F 盘编辑,只在 WSL 运行。反向拷贝一律视为事故。

固化成 _sync_to_wsl.shrsync --delete自动逐字节 cmp 校验,不通过则非零退出。 理由是 2026-07-31 的教训:F 盘副本比 WSL 落后,回灌前跑出的结果是旧代码算的, 而这种错误在结果上看不出来

1.2 行尾:查出 4 个混合行尾文件并统一

git init 时 Git 警告要把 LF 转成 CRLF。查证后发现两件事

  1. 立即关闭转换:core.autocrlf=false + .gitattributes* -text(字节原样存取)。 不关的话,将来任何一次 git checkout 都会把 R 脚本变成 CRLF,同步回 WSL 就出事 (历史教训:CRLF 致锚点 0 命中)。
  2. 实测原仓 87 个脚本里 4 个本来就是 CRLFanalysis/01_screen.Ranalysis/17_replicate_external.Rtools/verify_input_sanity.Rconfig.yaml —— 恰好是 2026-08-05 T1D EAF 修复时在 Windows 侧编辑过的那批。 因为本步要编辑其中两个,LF 的新内容插进 CRLF 文件会造成文件内混合行尾, 所以先统一为 LF(仅删 CR,其余字节不动),并以 research 身份验证 R/YAML 解析通过。

二、instrument_definition 按原文重写

config.yaml 原文案自注「未直接读到原文全文」,并把 Sun 2023 的 "excluding the HLA region" 转述成「排除 HLA」——容易被读成 HLA 蛋白被删掉了

已按 00 §6 核实到的原文重写,新增: 原文原句、cis 判定依据、以及本数据 14,287 / 1,955 / 1,954 三处与原文逐位吻合的校验。


三、代码改造

3.1 ★★ 最重要的一处:setwd 硬编码(计划外,但不修则整轮作废)

12 个脚本硬编码 setwd("/home/research/mr-pipeline-r9")tools/stamp_cache_fingerprints.R 更指向更早的 /home/research/mr-pipeline

后果:在 v3 新目录里跑,这些脚本会切回旧目录、读旧结果、把新结果写进旧目录。 所谓"干净重跑"会变成一半新一半旧,而且日志里看不出来

改法:统一为三级解析,全 16 处文本完全相同(便于一次 grep 核验):

r
# 优先级:环境变量 MRPIPE_ROOT > 本脚本所在目录的父目录 > 当前工作目录
setwd(local({
  r <- Sys.getenv("MRPIPE_ROOT", ""); if (nzchar(r)) return(r)
  a <- commandArgs(trailingOnly = FALSE); f <- grep("^--file=", a, value = TRUE)
  if (!length(f)) return(getwd())
  d <- dirname(normalizePath(sub("^--file=", "", f[1])))
  if (basename(d) %in% c("analysis", "tools")) dirname(d) else d
}))

实测验证:从 /tmp 调用 analysis/ 下的脚本,解析到 /home/research/mr-pipeline-r9v3

未改动的:36_control_definition_sensitivity.R(R13 线,按约定不碰)、 tools/ 下若干针对 R13/AMD 的一次性核验脚本(不在 R9 主链上)。

3.2 in_MHC:四处口径统一为单一真值源

新增 analysis/_mhc.R,取 UKB-PPP 工具表自带的 MHC (= Sun 2023 原文 25.5–34.0 Mb,实测双向差集为空)。不自己按坐标划线—— 那是数据提供方按其实际施加的规则打的标记,是事实。

替换掉的四处:

位置原来问题
29_enrichment.R手写 20 个蛋白名是死代码,分组其实用的是 32 产出的 in_MHC
32_targets_table.R同一份手写列表含 5 个不在 cis 池里的(C4A/C4B/NOTCH4/SKIV2L/VARS2),漏 12 个在池里的
35_reverse_mr.R坐标 25–34 Mb判定与"剔长程 LD 块"混用同一组常量
37_drug_target_phewas.R坐标 25–34 Mb只覆盖有 PheWAS 的蛋白

★ 手写列表对旧的 31 蛋白显著集恰好无影响(命中的 11 个全在列表内)—— 旧结果在这一点上没错,但机制脆弱,换数据即错。

35_reverse_mr.R 里刻意保留两个不同的区间并写明理由: 判定用 25.5–34.0(定义),剔长程 LD 块仍用 25–34(更宽更保守,目的不同)。

3.3 写死断言:assert_mhc_consistency()

接入 32 / 35 / 37,启动即校验,不符立刻失败:

cis 1955 行 / 1954 蛋白(= Sun 2023 正文)
作者 MHC 列 ≡ 25.5–34.0 Mb 坐标(双向差集为空)→ 27 个
Zheng 口径 26–34 Mb → 26 个;两者差集恰为 SCGN

写成断言而不是注释,因为注释不会在数据换掉时报警,而本项目已有两次 "改了上游、下游静默沿用旧口径"的教训。

3.4 ★ 第 4 步:外部复制三分法

step4_survivor 这个列名在 v4 下已经不成立

当时按 v3 写的,v3 说第 4 步有否决权,所以「存活」这个词是对的。 v4 把否决权收回到 4X,第 4 步只分流不判真伪——一个叫 "survivor" 的列 会让下一个读代码的人以为「不在这个集合里 = 已被淘汰」,而 v4 下它们只是被送去 4X

★ 这正是本项目反复踩的那类坑:列名承载了一个已经改掉的判定语义,而代码照跑不报错。 待改名为 repl_flag_to_4X 或类似,尚未做

17_replicate_external.R 新增 repl_class / repl_strength / step4_survivor

旧的二值 replicated两件不同的事混在一起:

  1. b_repl 缺失(NA)在下游常被当成"没复制上"= 阴性。 但没测过不等于测了没有——tools/decode_platform_check_r9dm.R:71 的注释 早就写着「跨平台复制属未做,不是阴性」,只是从没落到判定逻辑里
  2. 方向一致但 p ≥ 0.05 被判 FALSE。v3 规定方向一致即通过,显著与否只作强度标注—— 复制队列样本量通常远小于发现队列,拿 p < 0.05 当门槛等于用功效不足去否定一个方向正确的结果。

3.5 富集改用第 4 步存活集

29_enrichment.R 输入集由 MR 显著集改为第 4 步存活集(依据 C1R 预印本的 "the validated proteins")。缺 step4_survivor 列时直接 stop(), 而不是静默退回旧口径。保留 MR 显著集作对照组; 含 MHC vs 不含 MHC 的对比必须保留——那是本课题"MHC 需要处理"的核心证据。

3.6 共定位=措辞升级关卡(新增列)

32 新增 coloc_upgrade / mhc_verification_done / coloc_upgrade_note。 升级条件 = PP.H4 > 0.8 跨病符号一致 (非 MHC 或已补 MHC 专用核验)。

mhc_verification_done 写成显式列而非隐含 FALSE:将来补做时改一处即可, 且报告里能看出是「未做」而不是「未通过」。

3.7 缓存与断点

  • 01_screen.R 缓存指纹 v2v3
  • config.yaml resume: truefalse(从零重算,供可复现性证明; 该文件原注释里就写着"要从零验证有效性时改回 false")

3.8 跨病符号一致性:已存在,无需新增(仅验证)

计划里列为"新增",实测已经实现且正确32_targets_table.R): 按蛋白在 5 个并发症上 n_pos == 0 | n_neg == 0 判定, 且 n_disease < 2 标「单病(不适用)」而非「未通过」——与 v3 要求一致。

★ 计划里的担心「名字像但定义不同」完全成立,两者确是两回事:

位置算什么是什么
27_diabetes_contrast.R:61sign(bc) != sign(bd)蛋白对并发症 vs 对糖尿病 → 方向背离
32_targets_table.Rn_pos==0 | n_neg==0同一蛋白跨多个并发症的符号 → 跨病符号一致性

四、用旧结果做的预演(只读,不写)

改完的判定逻辑套到旧结果上,看会得出什么。这是第 4 步的跑前预期。

4.1 三分法(并发症 57 对)

类别对数蛋白数
concordant(方向一致)3224
not_assessable(不可评估)1919
opposite(方向相反)66

旧二值口径 replicated == TRUE 只有 12 对。

★ 19 个 not_assessable 恰好等于 Maculopathy 的全部 19 对—— 该结局的 repl_dataset 是「无外部队列(FinnGen 单源)」,压根没有外部队列可判, 旧口径却把它们全算作"没复制上"。

4.2 蛋白层存活

MR 显著蛋白31
第 4 步存活蛋白31
被第 4 步整体否决0

6 个有 opposite 记录的蛋白(AGER · HCG22 · LTB · TRIM40 · APOE · SIGLEC5) 各只有 1 对,在其他并发症上仍有存活对,故蛋白层全部存活。

推论(写进跑前预期):富集的输入集在本数据上与旧口径相同(31 个蛋白), 所以富集结果预期不变;变的是对数记账(57 → 51)与措辞。


五、验证记录

检查结果
改过的 R 脚本语法解析17 / 17 通过(以 research 身份)
config.yaml YAML 解析通过;resume = FALSE
根目录解析(从 /tmp 调用)解析到 /home/research/mr-pipeline-r9v3
assert_mhc_consistency()通过:cis 1955/1954;MHC 27;Zheng 26;差集 = SCGN
F ↔ WSL 逐字节137 相同 / 0 不同
4 个文件 CR 计数全部 0

六、怎么解读 / 去向

问题回答
这一步能下什么结论工程性结论。唯一影响科学结果的是 §3.4 三分法与 §3.5 输入集,两者都会在第 4 步产生新的漏斗数字
进正文还是补充代码改造本身都不进
补充方法§3.4 的三分法定义(尤其"不可评估 ≠ 阴性"这条判定规则必须写清楚)
LimitationsMaculopathy 无任何外部复制队列(19 对全部不可评估)
后续依赖§4 的预演数字就是第 4 步的跑前预期,跑完要逐项核对

七、⚠️ 两处待决(需拍板后才能继续)

7.1 tier(A/B/C/D)的去留

v3 的报告方式是漏斗,计划写「取消 A/B/C/D 标签」。但实测 tier 被 10 个文件、47 处引用06 / 07 / 08 / 10 / 21 / 32 / 33 / 37 / 41 / _singlecell_io), 且下游按字面字符串匹配(如 "C-MHC区(LD混杂风险)")。

「取消」在计划里是一行字,落到代码是一次十文件重构。三个选项:

选项代价
① 彻底删除,改为漏斗阶段列10 文件重构,风险最高,但口径最干净
② 保留 tier程序内部分组(决定谁进主图),论文只用漏斗改动最小;但 tier 的定义仍隐含"共定位是否决、MHC 要降级",与 v3 相悖
③ 保留列名、按 v3 重新定义取值下游字面匹配会全部失效,等同于①但更隐蔽

本轮已按最小侵入处理tier 原样保留并标注「待重构」,v3 的新列全部增量添加, 不破坏任何下游。等拍板。

7.2 第 4 步「外部复制」到底指哪条轴

我们其实有两条互相独立的外部复制线

脚本复制的是什么现状
结局侧17_replicate_external.R换独立结局队列(MVP 糖网/神经、Salem 糖肾)✅ 已改三分法
暴露侧·跨平台28_decode_reselect.R + tools/decode_platform_check_r9dm.R换蛋白测定平台(deCODE / SomaScan, N=35,559)⚠️ 无任何代码层判定规则

★ 关键:C1R 预印本对应的是暴露侧跨平台那条 ("evaluated in an independent Icelandic cohort (N=35,559, SomaLogic platform)"), 而 v3 流程图第 4 步的措辞正是照 C1R 定的。

★ 且实测:decode_reselect_mr.csv 没有任何脚本消费—— "三平台同向"那些结论是手工比对得出的,从未固化成规则。

需要决定:第 4 步取结局侧、暴露侧、还是两条都要(若都要,如何组合成一个存活判定)。 这决定漏斗长什么样,不宜由我代拍。


八、本步的 git 记录

新仓 F:\project\mr-pipeline-r9v3,4 个提交:

9a0edc3 feat: v3 第4步外部复制三分法 + 富集改用第4步存活集
6228626 refactor: MHC 判定单一真值源 + 根目录不再硬编码 + 缓存指纹 v3 + resume=false
9bf8e42 fix: 按 Sun 2023 原文重写 instrument_definition;统一 4 个文件行尾为 LF
c00451a chore: 关闭行尾转换(core.autocrlf=false + .gitattributes '* -text')
aeab838 chore: 从 mr-pipeline-r9 备份解出的 v3 重跑基线

个人科研与运维文档 · 内容持续修订