我来帮您生成《数据质控:FASTQ预处理可视化技巧》第2章的HTML正文片段,内容聚焦FASTQ格式详解与质量分数编码。

2. FASTQ格式详解:每一行含义,质量分数编码

FASTQ 是目前测序数据存储的"通用语言"。我刚开始处理测序数据时,第一反应是——这格式怎么这么简单?就四行重复,但每一行都有门道。今天我把这四行拆开讲透,顺便把 Phred 质量编码那点事说清楚。

2.1 四行结构:从 @ 到 + 再到质量串

一个完整的 FASTQ record 固定包含 四行,顺序不能乱,缺一行都不行。看个实际例子:

@EAS139:136:FC706VJ:2:2104:15343:197392 1:N:18:1
GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGTGCATCACAA
+
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

我来逐行拆解:

  • 第1行(标识符行):以 @ 开头,后面跟着测序仪器、flowcell 坐标、cluster 位置、read 方向、filter 标志等信息。不同测序平台的标识符格式略有差异,但 @ 是铁打的开头。
  • 第2行(碱基序列行):原始测序碱基,A/T/C/G 加上 N(无法判定的碱基)。长度就是测序读长,比如 150 bp 就是 150 个字符。这一行不换行,直接连着写。
  • 第3行(分隔行):以 + 开头,后面可以重复第1行的标识符(可选),也可以什么都不写。我见过很多数据直接只写一个 +,完全没问题。
  • 第4行(质量分数行):与第2行等长的 ASCII 字符,每个字符对应一个碱基的测序质量。字符的 ASCII 值经过偏移编码后,就是 Phred 质量分数。

关键点:第4行和第2行必须等长,一个字符对一个碱基。我曾经在处理一批老数据时发现长度对不上,结果是测序仪在读取时发生了跳号——这种 record 直接丢弃就好。

2.2 质量分数编码:Phred 公式 + ASCII 偏移

质量分数到底怎么来的?其实就是一个简单的对数转换:

Q = -10 × log₁₀(P)

其中 P 是该碱基的测序错误概率。Q = 30 意味着错误概率是 0.001(千分之一),Q = 20 是 0.01(百分之一)。Q 值越高,这个碱基越可信。

但问题来了——Q 值范围通常能到 40 甚至 60,直接用数字存很浪费空间。于是设计者用 单个 ASCII 字符 来编码 Q 值,做法就是:

ASCII = Q + offset

历史上出现过两种偏移量,也就是大家常说的 Phred+33 和 Phred+64。我把它们的核心差异整理成了一张表:

对比维度 Phred+33(Sanger / Illumina 1.8+) Phred+64(Illumina 1.3–1.7)
偏移量 33 64
最小 ASCII 33(!) 64(@)
最大 ASCII 126(~) 126(~)
Q 值范围 0 – 93 0 – 62
常见平台 Sanger、Illumina 1.8+、PacBio、ONT 早期 Illumina(GA II, HiSeq 2000 早期)
现状 绝对主流,几乎所有公共数据都用它 基本淘汰,遇到时需转换

我的习惯:拿到一个新数据集,第一件事就是用 head -n 4 看一眼质量行的第一个字符。如果看到 !(ASCII 33)或 #(ASCII 35),那基本就是 Phred+33。如果第一个字符是 @(ASCII 64)或 B(ASCII 66),很可能是 Phred+64。这招我用了快十年,从来没失手过。

避坑指南:我曾经从一家合作机构拿到一批 HiSeq 2000 的早期数据,质量行开头全是 B(ASCII 66)。我按 Phred+33 处理,结果所有碱基质量都飙到 33 以上,明显不对。后来一查,这居然是 Phred+64 编码!如果用错了偏移,下游变异检测会全面偏倚,数据基本报废。所以 拿到数据先确认编码类型,别想当然。

2.3 实战:如何快速判断一个 FASTQ 的编码类型

写一个简单的 Python 脚本就能搞定:

def guess_encoding(fastq_path, n_records=1000):
    min_ord, max_ord = 127, 0
    with open(fastq_path) as f:
        for i, line in enumerate(f):
            if i % 4 == 3:          # 质量行
                for ch in line.strip():
                    o = ord(ch)
                    if o < min_ord: min_ord = o
                    if o > max_ord: max_ord = o
            if i // 4 >= n_records:
                break

    print(f"ASCII range: {min_ord} – {max_ord}")
    if min_ord >= 64:
        return "Phred+64"
    elif min_ord >= 33 and max_ord <= 126:
        return "Phred+33"
    elif min_ord >= 59:
        return "Phred+64 (likely)"
    else:
        return "Phred+33 (likely)"

逻辑其实很简单:取 1000 条 record 的质量行,算最小和最大 ASCII 值。如果最小值 ≥ 64,绝不可能是 Phred+33(因为 Phred+33 的最小值是 33,对应 !)。反之,如果最小值在 33–63 之间,就是 Phred+33。

实用原则:目前 99% 的公开数据集(SRA、ENA、DDBJ)都使用 Phred+33。如果你下载的是 2015 年之后的 Illumina 数据,基本不用怀疑。但老数据(2011–2013 的 Illumina 双端)偶尔还会碰到 Phred+64,多留个心眼总没错。

下面这张图把整个 FASTQ 的结构和两种编码的对应关系串在了一起,你可以对照着理解:

FASTQ 格式结构与质量编码体系 @ 标识符行 碱基序列行 + 分隔行 质量分数行 Phred+33 ASCII 33 – 126 Phred+64 ASCII 64 – 126 质量分数 Q = -10 × log₁₀(P) P 为碱基测序错误概率 | Q 值越高 → 测序越可靠 Phred+33 是目前绝对主流 | Phred+64 仅见于早期 Illumina 数据

看懂这张图,FASTQ 的结构就没什么秘密了。四行从左到右、从上到下,质量分数行再分支到两种编码方式,逻辑非常清楚。

2.4 一个小技巧:从 ASCII 直观看编码

其实不用每次都跑脚本。我个人的经验是:直接用 head -n 40 看一下质量行的字符分布——

  • 如果看到大量 !、"、# 这些低 ASCII 字符(33–40),绝对是 Phred+33。
  • 如果看到的字符全都在 @(64)以上,没有低于 64 的,那就可能是 Phred+64。
  • 如果看到 I(ASCII 73)特别多,说明 Q 值集中在 40 左右,测序质量很好——无论哪种编码都是好事。

嗯,这里要注意:Phred+64 的 Q 值范围只有 0–62,对应 ASCII