文章目录
最近完成了一个小案例:把一张临床试验 Swimmer Plot 拆开,用模拟数据分别写出 R 和 SAS 版本,再把关键计数和输出结果做成 QC。
这不是在复刻某个真实项目的 TFL。原始数据不可得,截图里的周期位置也不能直接当成数据。所以我做的事情更接近“图形结构复现”:保留能从图上看见的关系,所有受试者、时间和疾病状态都重新构造。
先看清楚这张图在表达什么
Swimmer Plot 通常按受试者画一行。主图里有一段表示治疗暴露时长的水平条,条上可以放疾病状态;治疗结束的位置有一个标记,仍在治疗的受试者则延伸出一支箭头。
这张测试图还包括左侧的剂量队列、del(5q)、既往治疗次数、受试者 ID,以及右侧的 EOT 原因。它看起来像一张图,实际包含了几类不同的数据:受试者属性、治疗区间、疾病状态事件和治疗结束信息。
如果一开始就把这些内容拼成一张宽表,后面很快会遇到问题:一个受试者有多次疾病评估时,究竟要预留多少列?不同图层的时间点如何保持一致?因此我把数据拆成两张表。
两张表比一张大宽表更容易检查
第一张是受试者表,一行一个虚构受试者,放置剂量队列、疾病特征、既往治疗次数、治疗开始和结束周期、是否持续治疗、EOT 原因等字段。
第二张是疾病状态事件表,一行一个展示用事件,放置受试者 ID、评估周期和 PD 或 Refractory 标签。以后如果要展示多次 CR、PR、SD 或 PD,可以继续增加事件行,而不用给受试者表不断加新变量。
这两张表不是某个 CDISC 标准数据集的替代品。真实项目中,数据来源和变量名要按方案、SAP、分析数据规范和公司 SOP 确定。这里只是一个适合教学和绘图验证的最小结构。
测试数据到底有多大
案例使用 12 名完全虚构的受试者,编号从 SYN-001 到 SYN-012。他们分在 5 个剂量队列中,每个队列分别有 1、3、2、4、2 人。
数据里有 2 名 del(5q)=Yes,2 名在数据截止时仍在治疗;疾病状态事件共 8 条,其中 PD 5 条、Refractory 3 条;另有 3 名受试者标记为疾病疗效评估不可得。数据截止日 2025-08-21 只是为了匹配示例图的布局,不代表研究时间点。
这些数字的作用是让程序有东西可检查。它们不是疗效结果,也不支持任何医学结论。
R 和 SAS 各自负责什么
R 版本使用 ggplot2:用 geom_segment() 画治疗条,用 geom_point() 标记 EOT,用带箭头的线段表示持续治疗,再用 geom_text() 放置受试者表和事件文字。图形输出为 300 DPI PNG 和矢量 PDF。
SAS 版本保留了两种写法。较短的对照版本使用 PROC SGPLOT,完整版本使用 PROC TEMPLATE + SGRENDER。GTL 版本用 LAYOUT LATTICE 对齐左侧表格和主图,再组合 AXISTABLE、HIGHLOWPLOT、TEXTPLOT、SCATTERPLOT 和 VECTORPLOT。
两种语言的思路可以这样对照:
| 图形任务 | SAS | R |
|---|---|---|
| 读入 CSV | PROC IMPORT | read.csv() |
| 按受试者合并 | MERGE ... BY | merge() |
| 治疗暴露条 | HIGHLOW TYPE=BAR | geom_segment() |
| EOT 标记 | SCATTER | geom_point() |
| 持续治疗箭头 | VECTOR | 带 arrow 的 geom_segment() |
| 左侧受试者表 | YAXISTABLE | 负 x 坐标的 geom_text() |
| 输出文件 | ODS LISTING / PDF | ggsave() |
对我来说,映射表比“R 语法更现代”这种判断有用得多。它把已经熟悉的 SAS 图形思路拆成 R 里可以逐项验证的动作,也暴露出两套系统在布局和注释能力上的差异。
QC 先于好看
脚本在出图前先检查数据量、唯一受试者、持续治疗人数、del(5q) 计数、EOT 一致性和事件时间范围。R 版本生成 validation_report.txt,SAS GTL 版本生成脱敏后的验证报告和运行日志。
本地验证结果是:R 代码实跑通过;SAS 9.4M7 的 GTL 版本批处理退出码为 0,最终日志没有运行时 ERROR 或 WARNING。这里的“通过”只说明这组模拟数据和这份代码满足预先写下的检查,不等于真实项目 TFL 已经经过完整的统计或监管审查。
截图不能替我们决定的事情
仅凭一张图,无法确定 EAS 的正式人群定义,也无法判断 PD 和 Refractory 是首次评估、最佳疗效、EOT 状态,还是别的分析时点。条长究竟按完整周期、实际给药日、治疗日还是名义周期计算,同样不能从像素位置推出。
这些问题必须回到方案、SAP、分析数据规范或原始 TFL 程序。正式图形还需要确定多次评估、确认疗效、死亡、移植、随访截尾和互斥展示规则。否则,图画得越像,解释风险反而越大。
这个案例对 R 转型有什么用
它没有试图覆盖 R 的全部内容,只完成一条小链路:模拟数据、结构化数据表、R 图形、SAS 对照、结果 QC、PNG/PDF 和 README。每一步都有明确输入和输出,出问题时知道该回到哪一层检查。
这也是我现在选择案例学习的原因。课程可以告诉我有哪些函数,案例会逼着我回答:数据该怎样组织,哪一部分是方法,哪一部分只是画图,结果怎样让另一个人核对,以及哪些内容不能公开。
案例代码暂时保存在私有工作区,后续如果整理成公开仓库,还要再次检查数据、路径和运行日志是否完全脱敏。当前文章只公开模拟数据设计和方法边界,不把它包装成真实临床研究结果。
DISCUSSION
评论与勘误
欢迎指出错误、补充资料或分享实践经验。评论由 GitHub Discussions 托管,登录 GitHub 后即可参与。