4. Per tile sequence quality 模块详解与阈值设定

这个模块,很多人一开始容易忽略。它看的是测序芯片上每个小格子(tile)的质量波动。说白了,就是检查你的测序反应在芯片上是不是均匀的。万一某个区域出了毛病,整条lane的数据都会被拖累。

我自己刚接触FastQC时,看到那个花花绿绿的热图,第一反应是“挺好看的”。直到有一次,一个样本的tile质量热图出现了一条明显的红色竖条,对应的文库测序深度就是偏低。从那以后,我每次质控都会盯着这个模块看几秒。

4.1 热图怎么看

FastQC会给每个flow cell生成一张热图。横轴是文库位置(通常从左到右),纵轴是tile编号(从上到下)。颜色代表每个tile的平均质量偏离全局平均值的程度。

  • 蓝色/绿色:该tile质量高于平均水平。
  • 黄色/橙色:略低于平均,通常可接受。
  • 红色:显著偏低,需要警惕。

你可能会问:为什么有的热图整体偏蓝?那是因为大部分tile质量都很好,少数差的被压下去了。我这里有个经验:如果整个热图颜色均匀,深浅差异小,基本没问题。但要是出现成片红色或一条红色竖线/横线,那就得仔细查原因了。

核心判断原则:不是看单个tile的颜色绝对值,而是看空间分布的规律性。随机散布的零星红点可能是正常的统计波动;但连续成片的红色块,一定是系统性偏差。

4.2 常见问题根因

我总结了几种常见场景,你对照一下就能快速定位。

热图表现 可能原因 严重程度
某一行tile整体偏红 该tile的孔道堵塞或表面污染 中
出现一条垂直红色带 液流不均匀,某个通道局部失效 高
边缘一圈偏红 边缘效应(即“边缘气泡”) 低(可容忍)
随机散布红点 单tile随机故障 低

记得有一次我处理一个RNA-seq项目,热图中间出现了一条很窄的红色竖线。我一开始以为是偶然,后来发现那条竖线正好对应着某个文库的index区域,导致该样本的barcode识别成功率下降。所以,别小看这种细微的线索。

4.3 默认阈值 vs. 我常用的调整值

FastQC官方的默认阈值是这样设定的:

# 默认参数(嵌入式,不可直接修改)
# fastqc 默认将“平均偏差超过2”标记为warning
# 将“平均偏差超过3”标记为failure
# 这里的“平均偏差”是指每个tile的平均质量得分与所有tile均值的差值

但在实际项目中,我发现这个阈值有点太松。尤其是在高深度测序(比如≥50X的WGS)中,单个tile的偏差即使只有1.5,也可能导致下游变异检测的假阳性。所以我建议:

  • 严谨项目:warning设为1.5,failure设为2.5。
  • 常规项目:保持默认的2/3,但禁止出现成片红色区域。
  • 单细胞RNA-seq:由于UMI矫正,公差可以放宽,warning 2.5,failure 3.5。
小技巧:如果你想批量调整这些阈值,不要只依赖FastQC自带的参数(它其实不对外开放)。更实用的做法是:用Python解析fastqc_data.txt中的“Per tile sequence quality”一节,自己计算偏差并打标。我习惯写一个自定义脚本,把阈值写进YAML配置文件中。

下面是一个示例YAML配置:

# tile_quality_config.yaml
thresholds:
  warning: 1.5
  failure: 2.5
flags:
  reject_if_red_cluster: true
  cluster_size: 3     # 连续3个tile以上才算cluster
output_dir: "qc_reports"

然后配合一个简单的Python脚本去解析:

import yaml
import re

with open("tile_quality_config.yaml") as f:
    config = yaml.safe_load(f)

# 读取fastqc_data.txt中的tile质量行
with open("sample_fastqc/fastqc_data.txt") as f:
    lines = f.readlines()

in_tile_section = False
tile_data = []
for line in lines:
    if line.startswith(">>Per tile sequence quality"):
        in_tile_section = True
        continue
    if in_tile_section and line.startswith(">>END_MODULE"):
        break
    if in_tile_section and line.strip() and not line.startswith("#"):
        parts = line.strip().split("\t")
        if len(parts) >= 3:
            tile_data.append({
                "flowcell": parts[0],
                "tile": parts[1],
                "mean_deviation": float(parts[2])
            })

warn_tiles = [t for t in tile_data if t["mean_deviation"] > config["thresholds"]["warning"]]
fail_tiles = [t for t in tile_data if t["mean_deviation"] > config["thresholds"]["failure"]]

print(f"Warning tiles: {len(warn_tiles)}")
print(f"Failure tiles: {len(fail_tiles)}")
注意:不要直接用这段代码去改fastqc.html,那只是可视化。真正的质量判断一定要基于原始数据文件。我曾经吃过亏,以为热图没有红色就是好的,结果一读数值发现很多tile的偏差在2.1-2.3之间,只是颜色映射没显示出来。从那以后,我坚持用数值说话。

4.4 阈值设定决策流程图

下面这张图是我自己总结的决策逻辑,帮助你快速判断该tile模块是pass还是需要干预。

Per Tile Quality 阈值判定流程 FastQC原始热图数据 计算每个tile平均偏差 阈值比较 偏差 > 1.5? 是 标记为Warning 偏差>2.5? 是 Failure 否 Pass 快速决策建议 • 偏差>2.0 必须排查原因 • 连续红色块直接Fail • 边缘效应可酌情放行

4.5 避坑指南与我的个人习惯

最后,说几个容易踩的坑。

  • 别把热图当唯一依据。颜色映射范围是自动的,如果整体质量都很高,微小的偏差也会显示为红色。所以一定要结合数值看。
  • 不同测序平台宽容度不同。Illumina的tile质量通常很稳定,红色很少见;而华大(BGI)的芯片偶尔会有区域偏差,阈值可以放宽0.5。
  • 复测样本慎重处理。如果同一个样本在两个批次中tile质量差异很大,那大概率是实验操作问题,不是文库本身问题。
我曾经犯过的错:有次我在跑一个16S扩增子项目,看到Per tile的热图有一小块红色,就直接判定该样本不合格,要求重新建库。结果重新做了一遍还是那样,后来才发现那个区域对应的恰好是引物二聚体位置,属于正常现象。从那以后,我学会了先查看其他模块(如GC含量、序列重复)做交叉验证,不再单凭一个热图下结论。

嗯,模块讲完了。总结一下:Per tile sequence quality这个模块,核心是看空间均匀性。阈值要根据项目目的、测序平台、文库类型灵活调整。别死守默认值,也别过度解读。用一个简单的脚本把数值提出来,画个分布图,比盯着热图靠谱得多。