第4章 头信息格式标准与规范
FASTQ 的第一行,也就是 @行,看似简单,其实坑不少。我刚入行那会儿,以为每行 @ 后面随便写个 ID 就行,结果被下游工具狠狠教育了一回——有些程序只认特定格式,不匹配就报错。今天咱们就把这 @ 行的门道捋清楚。
4.1 @ 行通用结构
每条记录都以 @ 开头,紧接着是序列标识符(ID),后面可以跟一个或多个描述字段,用空格隔开。说白了:@ID 可选描述
举个最简单的例子:
@EAS139:136:FC706VJ:2:2104:15343:197393 1:Y:18:ATCACG
CTCTTTCCCTCCTCTTTTTCTCTCCCTCTCCTTTCCTTTCCTTTCTCCTTTTCTCCTT
+
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
这里面 @ 后面跟了一长串用冒号分开的字段,最后跟了一个空格和一个描述。你想想看,如果程序按照固定的分隔符解析,多一个少一个冒号都会出问题。我曾经在接手一批第三方数据时,发现所有 @ 行末尾都带了一个看不见的制表符,导致 split 出错了——那个排查过程,一言难尽。
4.2 字段分隔约定
各大测序平台使用的分隔符并不统一。我整理了一下常见的情况:
| 平台 / 工具 | 主要分隔符 | 典型字段内容 |
|---|---|---|
| Illumina CASAVA | 冒号 (:) + 空格 | instrument:flowcell:lane:tile:x:y #index/readnum |
| PacBio CCS | 斜杠 (/) | moleculeId/readnum |
| Oxford Nanopore | 下划线 (_) 或空格 | readId runId start_end |
| Sanger / 传统 FASTA | 空格 + 自由文本 | sequenceId free description |
你可能会问:为什么搞这么多花样?说白了,每个平台都有自己需要记录的元数据——Illumina 要记机器、芯片、坐标;PacBio 要记分子编号;Nanopore 要记通道和时间。这些字段挤在一行里,就得靠统一的分隔符来区分。
我个人建议,在写解析器时,不要死磕一种分隔符。最好先检测冒号、斜杠、下划线出现的频率,自动判断格式。我在一个项目中用 re.split(r'[:\s_/]+', header) 来做通用拆分,效果还不错,但要注意空字段的处理。
另外,Illumina 的 # 符号也很特殊。它后面跟着的是样本 index(多重索引时的 barcode 序列),再跟斜杠和读段序号。比如 #ATCACG/1。如果 index 不存在,可能会直接写成 #0/1 或干脆省略。嗯,这里要注意,有些工具遇到 #0 时直接报错——我遇到过,因为自认为“0 代表无”是常识,但工具不认。
4.3 常见头信息变体
别看只是一行字符串,不同厂家、不同版本之间的头信息变体多得让人头疼。我挑几个最常遇到的来说。
Illumina CASAVA 1.8+
这是目前最常见的格式。完整结构如下:
@EAS139:136:FC706VJ:2:2104:15343:197393 1:Y:18:ATCACG
各部分含义:
- EAS139 – 测序仪 ID
- 136 – flowcell 编号
- FC706VJ – flowcell 序列号
- 2 – lane 号
- 2104 – tile 号
- 15343 – x 坐标
- 197393 – y 坐标
空格后面是质量控制字段,冒号分隔:1:Y:18:ATCACG
- 1 – 读段序号(1 或 2)
- Y – 过滤器是否通过(Y/N)
- 18 – 对照文库索引值(control number)
- ATCACG – 样本 index 序列
#0/1 表示。后来才发现,不同版本的 CASAVA 对 index 缺失的处理不一样。建议先查看数据配套的 RunInfo.xml 或 SampleSheet.csv 来确认真实格式。
PacBio 的变体
PacBio 的 CCS (Circular Consensus Sequencing) 头信息简洁很多:
@m64012_210805_170248/3459256/ccs
斜杠分隔三个部分:仪器 run ID / 分子编号 ZMW hole number / 读段类型(ccs 表示一致性序列)。如果是 subreads,可能类似 /3459256/0_10000 记录起止位置。
我个人觉得 PacBio 的头信息最容易解析——因为字段少,分隔统一,基本不会出错。
Oxford Nanopore 的变体
ONT 的头信息看起来最“乱”,因为里面包含了很多冗余信息:
@f7dede3a-2b9f-4f6e-8e7a-0a5bcf5b3c5c runid=xxxx sampleid=yyy read=1 ch=123 start_time=2024-08-01T12:00:00Z
它实际上是一个 UUID 作为 read ID,后面跟 key=value 形式的键值对。这种格式的好处是扩展性极强,坏处是解析起来得用正则匹配键名。
有一次我拿着 ONT 的数据去跑一个只认 Illumina 格式的比对工具,结果工具直接把描述字段里的 runid= 当作 alignment 的一个 flag 给吞掉了——你说冤不冤?所以对接不同工具时,一定要先看文档。
Sanger / NCBI 传统格式
这种格式最简单:
@SRR001666.1 1858966 HWI-EAS339_3:3:1:1614:1095 length=36
空格前面是 run_accession.序列号,后面是自由文本说明。NCBI 的 SRA 数据大量使用这种格式。我建议用 .split(maxsplit=1) 来拿 ID,后面整个字符串当作注释存储,不要自作聪明去拆解。
知识结构总览
下面这张图整理了我上面讲的几种主要头信息变体及其解析路径。你可以把它当作一个快速参考:
这张图展示了 @ 行的三大核心维度:分隔符、ID+描述结构、以及具体的元数据变体。我画的时候特意把每个平台常用的分隔符标了出来——你可以快速看到,Illumina 是冒号的大户,PacBio 全靠斜杠,Nanopore 则是一套自成体系的 key-value。
写解析器的时候,我一般会按“先识别平台 → 再按对应规则拆分 → 最后提取关键字段”这个流程来。如果遇到未知格式,我会把整行存进一个 field 中,然后尝试匹配已知模式。嗯,说起来简单,真遇到那种混用冒号和空格的奇葩数据,还是得手动调。
最后说一句,头信息的标准化一直在演进。Illumina 在最新版 BCL2FASTQ 中已经开始用 @instrument:run:flowcell:lane:... #index,但许多旧数据还是老格式。我的建议是:永远留一手,做好格式兼容。