文章目录

最近完成了一个小案例:把一张临床试验 Swimmer Plot 拆开,用模拟数据分别写出 R 和 SAS 版本,再把关键计数和输出结果做成 QC。

这不是在复刻某个真实项目的 TFL。原始数据不可得,截图里的周期位置也不能直接当成数据。所以我做的事情更接近“图形结构复现”:保留能从图上看见的关系,所有受试者、时间和疾病状态都重新构造。

先看清楚这张图在表达什么

Swimmer Plot 通常按受试者画一行。主图里有一段表示治疗暴露时长的水平条,条上可以放疾病状态;治疗结束的位置有一个标记,仍在治疗的受试者则延伸出一支箭头。

这张测试图还包括左侧的剂量队列、del(5q)、既往治疗次数、受试者 ID,以及右侧的 EOT 原因。它看起来像一张图,实际包含了几类不同的数据:受试者属性、治疗区间、疾病状态事件和治疗结束信息。

如果一开始就把这些内容拼成一张宽表,后面很快会遇到问题:一个受试者有多次疾病评估时,究竟要预留多少列?不同图层的时间点如何保持一致?因此我把数据拆成两张表。

两张表比一张大宽表更容易检查

第一张是受试者表,一行一个虚构受试者,放置剂量队列、疾病特征、既往治疗次数、治疗开始和结束周期、是否持续治疗、EOT 原因等字段。

第二张是疾病状态事件表,一行一个展示用事件,放置受试者 ID、评估周期和 PDRefractory 标签。以后如果要展示多次 CR、PR、SD 或 PD,可以继续增加事件行,而不用给受试者表不断加新变量。

这两张表不是某个 CDISC 标准数据集的替代品。真实项目中,数据来源和变量名要按方案、SAP、分析数据规范和公司 SOP 确定。这里只是一个适合教学和绘图验证的最小结构。

测试数据到底有多大

案例使用 12 名完全虚构的受试者,编号从 SYN-001SYN-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 对齐左侧表格和主图,再组合 AXISTABLEHIGHLOWPLOTTEXTPLOTSCATTERPLOTVECTORPLOT

两种语言的思路可以这样对照:

图形任务SASR
读入 CSVPROC IMPORTread.csv()
按受试者合并MERGE ... BYmerge()
治疗暴露条HIGHLOW TYPE=BARgeom_segment()
EOT 标记SCATTERgeom_point()
持续治疗箭头VECTORarrowgeom_segment()
左侧受试者表YAXISTABLE负 x 坐标的 geom_text()
输出文件ODS LISTING / PDFggsave()

对我来说,映射表比“R 语法更现代”这种判断有用得多。它把已经熟悉的 SAS 图形思路拆成 R 里可以逐项验证的动作,也暴露出两套系统在布局和注释能力上的差异。

QC 先于好看

脚本在出图前先检查数据量、唯一受试者、持续治疗人数、del(5q) 计数、EOT 一致性和事件时间范围。R 版本生成 validation_report.txt,SAS GTL 版本生成脱敏后的验证报告和运行日志。

本地验证结果是:R 代码实跑通过;SAS 9.4M7 的 GTL 版本批处理退出码为 0,最终日志没有运行时 ERROR 或 WARNING。这里的“通过”只说明这组模拟数据和这份代码满足预先写下的检查,不等于真实项目 TFL 已经经过完整的统计或监管审查。

截图不能替我们决定的事情

仅凭一张图,无法确定 EAS 的正式人群定义,也无法判断 PDRefractory 是首次评估、最佳疗效、EOT 状态,还是别的分析时点。条长究竟按完整周期、实际给药日、治疗日还是名义周期计算,同样不能从像素位置推出。

这些问题必须回到方案、SAP、分析数据规范或原始 TFL 程序。正式图形还需要确定多次评估、确认疗效、死亡、移植、随访截尾和互斥展示规则。否则,图画得越像,解释风险反而越大。

这个案例对 R 转型有什么用

它没有试图覆盖 R 的全部内容,只完成一条小链路:模拟数据、结构化数据表、R 图形、SAS 对照、结果 QC、PNG/PDF 和 README。每一步都有明确输入和输出,出问题时知道该回到哪一层检查。

这也是我现在选择案例学习的原因。课程可以告诉我有哪些函数,案例会逼着我回答:数据该怎样组织,哪一部分是方法,哪一部分只是画图,结果怎样让另一个人核对,以及哪些内容不能公开。

案例代码暂时保存在私有工作区,后续如果整理成公开仓库,还要再次检查数据、路径和运行日志是否完全脱敏。当前文章只公开模拟数据设计和方法边界,不把它包装成真实临床研究结果。

DISCUSSION

评论与勘误

欢迎指出错误、补充资料或分享实践经验。评论由 GitHub Discussions 托管,登录 GitHub 后即可参与。