Ubuntu 下 FastQC 与 MultiQC 测序数据质控批量实战

发布时间:2026/10/1 13:47:00
Ubuntu 下 FastQC 与 MultiQC 测序数据质控批量实战 测序数据从公司拷回来或者自己用 bcl2fastq 跑完下机第一个动作往往是 vim 打开 fastq 瞄两眼然后被满屏的 ACGT 劝退。真正靠谱的做法是在 LinuxUbuntu上先跑一遍 FastQC把每个样本的碱基质量、GC 分布、接头污染、重复序列这些指标摸清楚再用 MultiQC 把几十甚至上百份报告合并成一张总览页面。这一套组合拳几乎是我见过的所有测序项目里最标准的“入场检查”成本低、上手快但对后续是不是要 trim、要不要重测、要不要换建库方式影响极大。这篇内容面向的是刚接触生信的实验人员、刚转行做 NGS 分析的同学以及需要在服务器上批量处理数据的运维或平台同事。我会把 FastQC 与 MultiQC 在 Ubuntu 上的安装路线、参数含义、批量化脚本、报告读法、常见报错排查都过一遍尽量做到你照着敲就能跑起来跑起来之后也知道自己在看什么。1. 先弄明白 FastQC 和 MultiQC 各自管什么1.1 FastQC 的定位单样本的“体检报告”FastQC 是 Babraham Institute 出的一个 Java 写的测序质控工具输入是 fastq也支持 sam/bam甚至纳米孔的长读长数据输出是每个样本一份 HTML 报告加一个 zip 压缩包。它的核心价值在于把一堆肉眼根本看不出来的统计规律用图表的方式摊在你面前。它默认会跑十来个模块Basic Statistics、Per base sequence quality、Per tile sequence quality、Per sequence quality scores、Per base sequence content、Per sequence GC content、Per base N content、Sequence Length Distribution、Sequence Duplication Levels、Overrepresented sequences、Adapter Content、Kmer Content。每个模块给出 PASS / WARN / FAIL 三档判定遇到 FAIL 的模块就得认真看看到底是数据本身的问题还是建库环节留下的痕迹。这里有个很多人忽略的点FastQC 是“单样本视角”它一次只能处理一个文件并生成一份报告。你手里有 96 个样本它就生成 96 份报告。它本身不带跨样本比较能力也不做数据清洗仅仅告诉你“这份数据现在长什么样”。1.2 MultiQC 的定位把 N 份报告拧成一张总览MultiQC 解决的问题正是 FastQC 留下的空白。它由 Seqera 团队维护是个 Python 工具能扫描一个目录树自动识别其中几十种生信工具的输出文件FastQC、fastp、Trim Galore、Salmon、STAR、Qualimap、Picard、Samtools 等等然后把它们汇总成一个 HTML 报告。我最看重 MultiQC 的三点第一它会把样本名、总 reads 数、Q30 比例、GC 含量这些关键指标抽成一张 General Statistics 表你可以直接按 Q30 排序一眼看出哪些样本拖后腿第二它会按模块维度把所有样本叠在一起画图比如 Per base sequence quality 会画成一张包含全部样本的曲线图比一张张翻 HTML 高效太多第三它会顺手输出一份multiqc_data目录里面的 txt 和 json 可以直接喂给 pandas 或者导入 Excel方便做后续的筛选和报表。1.3 为什么这两个工具非得搭配着用单独用 FastQC在样本少的时候还行样本一多就变成了“手动翻报告”的体力活而且人眼容易疲劳看漏几个异常样本很常见。单独用 MultiQC 也不行它本身不做质控计算所有数据都来自其他工具的输出没有 FastQC 就没东西可汇总。所以标准的流程是FastQC 生成原始报告 → MultiQC 扫描这些报告 → 生成总览。我在实际项目里还会把 MultiQC 的产物作为“门禁”比如规定 Q30 低于 80% 的样本必须先做 trim 再重新质控达到门槛才允许进入比对环节。这个门禁放在流程里比事后靠人盯要可靠得多。2. Ubuntu 上装 FastQC 和 MultiQC 的三条路线2.1 三条安装路线对比与选型建议在 Ubuntu 上装这两个工具主流有三条路系统包管理器 apt、Conda 生态、官方二进制或 pip。它们各有取舍我列个表你按自己环境挑。方式优点缺点适用场景apt一条命令系统级可用仓库版本通常偏旧MultiQC 不一定有临时试用、教学演示Conda推荐版本可控依赖自动解决能锁环境首次装 Miniforge 稍慢磁盘占用大生产分析、多人协作服务器官方包 / pip拿到最新版轻量需要自己管 Java 和 Python 依赖单机、有经验的用户如果你的服务器是多个人共用我强烈建议走 Conda 路线并且用环境隔离。原因很实际FastQC 依赖 JavaMultiQC 依赖 Python而服务器上往往还跑着别的老流程系统级的 Java 和 Python 版本一旦被你改了可能把别人的流程搞崩。环境隔离是成本最低的“防背锅”手段。2.2 Conda 路线完整操作与 Java 依赖处理先说 Java。FastQC 从 0.12.0 开始要求 Java 11 及以上0.11.x 系列 Java 8 就能跑。如果你走 Conda 安装bioconda 会自动把 openjdk 作为依赖装进同一个环境你完全不用操心。但如果走官方二进制包就得自己装sudo apt update sudo apt install -y openjdk-17-jre-headless java -version注意这里装的是-headless版本不带图形界面。原因后面排查章节会讲FastQC 在纯命令行服务器上偶尔会因为没有图形环境报HeadlessException装 headless 版本能规避一大类问题。接下来装 Miniforge它默认使用 conda-forge 渠道比 Miniconda 少很多许可证上的烦心事curl -L -O https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh bash Miniforge3-Linux-x86_64.sh -b -p $HOME/miniforge3 source $HOME/miniforge3/etc/profile.d/conda.sh-b是静默安装-p指定安装路径。装完之后把初始化脚本写进.bashrc否则每次开新终端都得手动 source 一遍。然后就到了关键的一步创建环境mamba create -n qc -c conda-forge -c bioconda fastqc multiqc -y mamba activate qc fastqc --version multiqc --version渠道顺序这里值得多说一句。现在的推荐写法是把conda-forge放在bioconda前面因为 bioconda 里的大量包依赖 conda-forge 的基础库顺序反了容易触发依赖求解器反复回溯装个 FastQC 能耗上好几分钟。用 mamba 代替 conda 也是同理求解速度快一个量级。2.3 pip 与官方包的补充方案如果你只想用 MultiQC其实不一定需要 Conda。它是个纯 Python 包用 venv 隔离就够了python3 -m venv ~/venvs/qc source ~/venvs/qc/bin/activate pip install -U pip pip install multiqc这种方式的好处是轻坏处是 FastQC 你得单独解决。而新版本的 MultiQC 对 Python 版本有要求越新的版本门槛越高所以如果你的系统 Python 是 3.6 这种老古董先升 Python 或者干脆回到 Conda 方案。FastQC 官方包的做法是去 Babraham 官网下载 zip解压后把目录加到 PATHunzip fastqc_v0.12.1.zip -d /opt chmod x /opt/FastQC/fastqc export PATH/opt/FastQC:$PATHchmod x这一步千万别忘官方包解压出来默认没有执行权限很多人卡在Permission denied就是这个原因。提示不管走哪条路装完之后一定用fastqc --version和multiqc --version各验证一次。文件存在不等于能跑Java 版本不匹配的问题只有真正执行时才会暴露。3. FastQC 实操参数、批量运行与报告逐项解读3.1 常用参数逐个拆解与容易踩的坑FastQC 的参数不多但有几个是必须理解的。-o/--outdir指定输出目录不指定就写在输入文件旁边。批量跑的时候一定要指定否则你的原始数据目录会被 HTML 和 zip 淹掉。-t/--threads是最容易误解的参数。很多人以为它能让单个大文件跑得更快事实并非如此。FastQC 的线程是“同时处理多少个文件”每个文件仍然由单线程处理。官方文档还提到每个线程会占用约 250MB 内存所以-t开太大不仅不会更快还会把内存吃满。32 位系统上官方建议不超过 6 个线程。--memory控制每个线程的内存上限默认就是 250MB。处理长读长数据或者压缩比特别高的 fastq 时这个值要往上调。--extract和--noextract决定要不要把 zip 里的内容解压出来。默认是解压的会生成一个xxx_fastqc/目录里面有fastqc_data.txt、summary.txt、图片等等。样本多的时候这些目录会占用大量 inode 和磁盘空间我一般直接用--noextract因为 MultiQC 完全可以直接读 zip。--nogroup会让 Per base sequence quality 这类图表不再按碱基位置分组而是每个位置单独画。读长 150bp 的时候图会变得很密一般不推荐开但在排查特定位置的异常时有用。--adapters和--contaminants允许你替换默认的接头和污染序列文件。做特殊文库比如自定义接头的时候需要自己写一份。还有一个隐藏的坑命令行参数长度限制。fastqc *.fastq.gz在样本数量多、文件名长的时候可能直接报Argument list too long。解决办法是配合find和xargs。3.2 一个可以直接抄的批量运行脚本这是我日常用的脚本骨架稍微改改就能用#!/usr/bin/env bash set -euo pipefail RAW_DIRraw_data OUT_DIRqc_reports THREADS8 mkdir -p $OUT_DIR find $RAW_DIR -maxdepth 1 -name *.fastq.gz -print0 \ | xargs -0 -n 1 -P $THREADS \ fastqc --noextract --quiet --outdir $OUT_DIR几个细节解释一下。-print0和xargs -0是为了处理文件名里有空格的情况测序文件里这种命名不算罕见。-n 1表示每次只传一个文件给 fastqc-P 8表示最多 8 个进程并行。这里我用的是进程级并行没有再用fastqc -t因为两层并行叠加会导致内存占用失控。如果你想用fastqc -t那就把-P设成 1二选一。set -euo pipefail也是经验之谈。不加的话中间某个文件跑失败脚本还会继续往下走最后你以为全跑完了其实漏了几个样本。3.3 十二个模块里真正需要盯的几个FastQC 报告模块挺多但实际工作中真正决定后续动作的其实就那么几个。Per base sequence quality是第一优先级。横轴是碱基位置纵轴是 Phred 质量值。Phred 值和质量概率的关系是 Q -10 × log10(P)Q20 对应 1% 的错误率Q30 对应 0.1%。新版本 FastQC 把绿色区间下沿定在 27 附近黄色是 20 到 27红色低于 20。如果曲线在 3 端明显下滑进入黄区甚至红区说明测序后期的质量衰减需要考虑按质量裁剪。注意Illumina 双端测序里 read2 的质量普遍低于 read1这是化学原理决定的看到 read2 整体偏低不用惊慌看趋势就行。Per base sequence content看的是 A/T/C/G 四条线是否平行。理论上四条线各占 25%。实际数据里开头的 10 到 15 个碱基经常出现剧烈波动这通常是随机引物带来的偏好属于正常现象不用管。但如果整条 read 上四条线都明显分离A 和 T 不相等、G 和 C 不相等那就要怀疑污染或者物种混杂了。Per sequence GC content是判断污染最直观的图。正常情况下实际 GC 分布应该拟合理论分布形成一条比较对称的钟形曲线。如果出现明显的双峰或者实际曲线和理论曲线严重错位往往意味着样本里混进了别的物种或者存在大量接头二聚体。Adapter Content的累积曲线如果在一端持续上升说明接头没去干净必须 trim。常见的 Illumina 通用接头是AGATCGGAAGAGC开头的那一段Nextera 的转座酶序列是CTGTCTCTTATA。FastQC 内置了这些接头库一般不用自己配。Sequence Duplication Levels要结合文库类型判断。全基因组测序里一定程度的重复是正常的因为基因组本身有重复序列。但如果是 RNA-seq高重复可能来自高表达基因也可能是低复杂度序列或 PCR 过度扩增需要结合 Overrepresented sequences 一起看。Overrepresented sequences列出的是占总 reads 超过 0.1% 的序列。如果这些序列能比对到接头或者已知的污染基因组基本就实锤了。3.4 不想翻 HTML用 summary.txt 快速筛异常样本多的时候一张张点开 HTML 不现实。FastQC 的 zip 里有个summary.txt格式是判定\t模块名\t文件名用一行 shell 就能把所有有 FAIL 的样本列出来for z in qc_reports/*_fastqc.zip; do s$(basename $z _fastqc.zip) if unzip -p $z ${s}_fastqc/summary.txt | grep -q ^FAIL; then echo FAIL: $s fi done这个脚本在样本量上百的时候特别好用先跑一遍圈出可疑名单再针对这几十个样本细看 HTML效率提升非常明显。顺便说一句PASS/WARN/FAIL 只是启发式判定不是绝对标准。低覆盖度的数据、特殊的建库方式都可能触发 WARN最终还是要结合实验背景判断。4. MultiQC 实操目录组织、配置定制与自动化4.1 基本用法与目录组织建议MultiQC 最简单的用法就是在报告所在目录下敲multiqc .它会递归扫描当前目录找到所有能识别的文件生成multiqc_report.html和一个multiqc_data目录。几个常用参数-o/--outdir指定输出目录-n/--filename指定报告文件名-f/--force覆盖已有报告不加的话如果报告已存在会直接退出-m/--module强制只跑某个模块比如-m fastqc--ignore排除路径比如--ignore old_data/*--ignore-samples按样本名排除-d/--dirs在样本名里保留目录层级跨批次同名样本的时候非常有用。目录组织我建议按“原始数据 → 质控报告 → 汇总”三级分project/ ├── raw_data/ 原始 fastq ├── qc_reports/ FastQC 输出的 html zip └── multiqc_out/ MultiQC 汇总报告然后命令行写成multiqc qc_reports \ --outdir multiqc_out \ --filename sequencing_qc \ --title RNA-seq Batch1 QC \ --force这样做的好处是 MultiQC 的扫描范围被限定在qc_reports里不会误扫到其他目录的无关文件。我见过有人直接在项目根目录敲multiqc .结果把几年前的历史报告一起扫进来样本数直接翻倍排查了半天才发现问题。4.2 用配置文件把报告做成“能交付”的样子默认生成的 MultiQC 报告能用但不够“能交付”。加一个 YAML 配置文件能让报告更像正规的交付物title: Project ABC 测序质控总览 subtitle: Batch 2024-05 intro_text: 本报告汇总本批次全部样本的 FastQC 结果Q30 低于 80% 的样本需执行 trim 后重跑。 report_header_info: - Contact: bioinfolab.org - Pipeline: RNA-seq v1.3 - Date: 2024-05-20 fastqc_config: top_overrepresented_sequences: 10 max_table_rows: 500top_overrepresented_sequences控制报告里展示多少条过表达序列默认值偏少样本存在接头污染时会看不够。max_table_rows在样本量极大时很有用避免浏览器打开表格直接卡死。调用方式multiqc qc_reports -c multiqc_config.yaml -o multiqc_out -f如果同批次有重复样本名用--replace-names配合一个两列的 TSV 重命名表可以一次性把所有样本名换成实验室内部的规范命名。4.3 把 MultiQC 的产物接进自动化流程MultiQC 真正的价值在自动化上。跑完之后multiqc_data目录里有一堆纯文本文件最常用的是multiqc_general_stats.txt和multiqc_fastqc.txt。前者是每个样本一行、每个指标一列的总表格式规整直接就能被 pandas 读import pandas as pd df pd.read_csv(multiqc_out/multiqc_data/multiqc_general_stats.txt, sep\t) df df.set_index(Sample) # 找出 Q30 低于 80% 的样本 low_q30 df[df[FastQC_mqc-generalstats-fastqc-percent_duplicates] 50] print(low_q30.index.tolist())这段代码完全可以塞进 CI 或者流程管理的某个检查节点里不达标就直接中断下游。我通常会再加一条规则如果某个样本的% Dups超过 50% 且总 reads 数低于某个阈值就标记为“建议重测”。这种基于数据而不是基于感觉的判断能省下大量扯皮时间。还有一个容易被忽略的用法--pdf。MultiQC 支持直接导出 PDF但它依赖 pandoc 和 LaTeX装起来比较重。如果你的交付对象需要 PDF可以在 CI 里单独跑一个装了这些依赖的镜像而不是把依赖压到主环境里。5. 常见问题排查与踩坑实录5.1 安装与运行阶段的典型报错速查下面这张表是我这些年亲身遇到过、也被别人问过最多的报错基本能覆盖八成的情况。现象根本原因处理方式Unrecognized VM option MaxPermSizeJava 8 之后移除了 PermGen 空间编辑 fastqc 启动脚本删掉该参数或升级到 0.12.xjava.awt.HeadlessException服务器无图形显示环境装openjdk-17-jre-headless或设export DISPLAYconda: command not found新终端未初始化 conda在.bashrc里 sourceconda.shfastqc: command not found环境没激活或 PATH 未生效mamba activate qc后重试Permission denied官方包解压后无执行权限chmod x FastQC/fastqcArgument list too long通配符展开后参数超长改用find xargsMultiQC 报告里没有 FastQC 模块文件名不符合识别规则报告文件名需含_fastqc且未被--ignore排除Duplicate sample name警告跨批次同名样本被合并加--dirs保留路径或用--replace-names内存被吃满 OOMfastqc -t与xargs -P叠加并行只保留一层并行磁盘 inode 耗尽用了--extract生成大量小文件改用--noextract关于MaxPermSize这个坑补充一句背景。FastQC 的启动脚本在 0.11.x 时代写死了-XX:MaxPermSize250m这个参数在 JDK 7 及以前用来控制永久代大小JDK 8 开始被 Metaspace 取代参数被标记为 unrecognized。虽然它通常只是打一行警告不影响运行但在某些严格模式下会导致启动失败。进去改脚本是最直接的解法。5.2 报告解读阶段最容易误判的三种情况第一种把正常的 read2 质量下降当成问题。Illumina 双端测序的 read2 质量天然低于 read1因为它的测序化学路径不同。只要 read2 的曲线没有掉到红区通常不需要特殊处理。真正的红线是曲线中段就出现断崖式下滑。第二种把前几个碱基的 GC 波动当成污染。RNA-seq 因为随机引物的存在开头几个碱基的碱基组成大幅偏离 25% 是普遍现象。判断污染要看的是全长的整体偏离而不是开头那一小段。第三种把过表达序列直接等同于污染。Overrepresented sequences 里出现一条序列可能来自接头、可能来自 rRNA 残留、也可能来自某个表达量极高的基因。正确做法是取这条序列去比对可以用 blast 或者它的接头比对结果确认来源之后再决定要不要 trim。5.3 几条我自己踩出来的实操心得第一永远先在小样本上试。别一上来就把 96 个样本全丢进去跑先用两三个样本验证流程、参数、输出格式都对再批量铺开。我早期吃过亏脚本里一个路径写错跑到一半发现输出全跑到错误目录去了白等一个多小时。第二把qc_reports目录当成只读。FastQC 输出之后不要再往里塞别的东西比如自己整理的 Excel、备注文件。MultiQC 的扫描逻辑是基于文件特征的目录越干净误识别的概率越低。第三MultiQC 版本用 conda 锁住。MultiQC 更新很频繁模块解析逻辑和报告模板偶尔会有变化。生产流程里最好在环境里锁一个确定版本比如multiqc1.21别让它自动升到最新。不然某天早上你发现报告长得不一样了还以为数据出了问题。第四样本命名从一开始就规范。FastQC 和 MultiQC 都靠文件名区分样本命名里带批次、日期、样本编号会让后续所有的合并、重命名、报表工作都轻松很多。命名混乱带来的痛苦往往在项目后期才集中爆发那时候再改成本极高。第五跑长读长数据要另眼相看。纳米孔和 PacBio 的数据在 FastQC 里会触发大量 WARN因为默认阈值是给短读长设计的。处理这类数据时建议配合--nano参数新版支持并且不要拿短读长的标准去判断长读长数据的 PASS/FAIL。第六养成留存multiqc_data的习惯。HTML 报告给人看multiqc_data里的文本给机器看。把每批次的multiqc_general_stats.txt按批次归档半年之后你想回溯“这批数据当时的 Q30 是多少”翻文件比翻聊天记录快得多。上面这几条没什么高深理论但每一条背后都是实打实的时间成本。FastQC 加 MultiQC 这套组合本身并不复杂真正花时间的从来不是装和跑而是理解每个指标背后的含义并且在样本量上去之后依然能高效地把异常样本挑出来。