4. 数据接收与原始数据层构建
临床研究的数据流水线从“接收”开始。无论上游是EDC系统导出的CSV、统计软件生成的SAS数据集、CDISC标准下的XPT传输文件,还是实验室信息系统推送的XML,进入流水线的第一站都应当是原始数据层(Raw Data Layer)。这一层的核心原则是:原样保留、不可变、可追溯。任何解析、清洗、类型转换都发生在原始层之后,原始层本身只负责“接住”数据并证明它没有被篡改。
原始数据导入与文件格式解析
导入的第一步不是写代码,而是建立接收清单。每批数据到达时,应记录:来源系统、传输方式(SFTP/API/人工上传)、到达时间、文件清单、文件大小、校验值(MD5或SHA-256)、对应的传输批次号。这些元数据本身也是原始层的一部分,通常写入一张ingest_manifest表或同目录下的manifest.json。
不同格式的解析策略差异很大,需要分别处理:
- CSV:看似简单,实则陷阱最多。必须显式指定分隔符、引号规则、编码(UTF-8/GBK)、是否含表头、缺失值表示(空串、
NA、.、-9)。建议解析时全部按字符串读入,不做自动类型推断,避免00123被吞掉前导零、日期被误判。 - SAS7BDAT:需借助
pyreadstat或pandas.read_sas读取,注意SAS的缺失值有.、.A~.Z、._等多种特殊缺失码,不能简单当作NaN。 - XPT(SAS Transport):CDISC SDTM/ADaM的标准传输格式,固定80字节记录结构,变量名大写且不超过8字符,需用
pyreadstat.read_xport解析,并保留其变量标签与格式信息。 - XML:常见于CDISC ODM和Lab数据。应使用
lxml按XPath提取,同时保留原始XML文件本身,因为ODM中的ItemGroupDef、CodeList等元数据对后续校验至关重要。
解析后的数据应写入原始层的落地表,字段全部为字符串类型,并附加_ingest_batch_id、_source_file、_row_num三列溯源信息。这样即使后续发现解析错误,也能定位到具体文件的具体行。
数据校验与接收报告
接收校验分三个层次,由浅入深:
- 文件级校验:文件是否存在、大小是否非零、MD5是否与发送方一致、文件数量是否与清单匹配。
- 结构级校验:列名是否与预期Schema一致、列数是否正确、编码是否可解析、行数是否在合理范围(如与上一批次偏差不超过20%)。
- 内容级校验:关键字段非空率、受试者ID格式、日期格式、枚举值是否在允许集合内、重复主键检测。
校验结果应生成一份接收报告(Ingest Report),包含:批次号、文件清单及校验值、各文件行数与列数、结构校验通过/失败项、内容校验的异常计数与示例行、总体状态(PASS/WARN/FAIL)。WARN表示可继续但需人工确认,FAIL表示阻断,数据不得进入下游。
原始数据存储策略
原始层推荐采用不可变分区存储:按来源系统/数据集/接收日期/批次号组织目录,文件写入后设为只读,任何修正都以新批次追加,不覆盖旧文件。这样天然满足ALCOA+中的“原始性、准确性、完整性、一致性、持久性”。
存储介质上,小规模研究可直接用文件系统加Parquet落地;中大型研究建议对象存储(如S3/MinIO)加元数据库(如PostgreSQL记录manifest和报告)。无论哪种,都应保证:原始文件与解析后的落地表同时保留,前者是法律意义上的原始记录,后者是工程意义上的可查询副本。
一个常见的误区是把“解析成功”等同于“接收成功”。解析成功只说明文件能被程序读取,不代表数据符合研究预期。例如某批次CSV中SEX列出现了M、F、1、2四种写法,解析完全正常,但内容校验必须将其标记为WARN并输出示例行,由数据管理员确认映射规则后再进入下游。原始层不做修正,只做记录和报告。