1. 为什么我最终把质控流程全压在了Fastp上
测序数据下机之后,第一件事永远是质控。这个环节做得好不好,直接决定了后续比对率、变异检出灵敏度、甚至整个项目的成败。我见过太多人拿到fastq文件之后,随便跑个FastQC看一眼,觉得"差不多还行"就直接进入比对流程,结果后面各种报错、各种异常,回头排查半天,发现根子还是在原始数据上。
Fastp这个工具,我从它刚出来没多久就开始用,到现在基本上已经成了我处理fastq数据的首选质控方案。原因很简单:它把过去需要FastQC + Trimmomatic + cutadapt + 自写脚本才能搞定的事情,全部集成到了一个命令里,而且速度极快,多线程支持好,输出报告直观,还能自动检测adapter序列。对于日常的WGS、WES、RNA-seq数据,Fastp基本能覆盖90%以上的质控需求。
这篇文章我打算把自己这些年用Fastp的实战经验完整梳理一遍,从最基础的单端/双端处理,到UMI处理、polyG修剪、去重、与Shell脚本的配合使用,再到那些官方文档里不会写的坑和技巧。适合刚接触fastq质控的新手,也适合已经用过Fastp但想进一步榨干它性能的老手。读完你至少能做到:拿到一批fastq文件,知道该怎么设计质控方案,参数怎么调,出了问题怎么排查。
2. Fastp到底解决了什么问题:核心设计思路拆解
2.1 传统质控流程的痛点在哪里
早些年做质控,标准流程是这样的:先用FastQC跑一遍原始数据,看质量分布、GC含量、adapter污染情况;然后根据FastQC的报告,手动决定用Trimmomatic还是cutadapt做修剪;修剪完再用FastQC跑一遍确认效果;如果要做UMI处理,还得额外写脚本或者用fgbio、umi_tools这些工具。整个流程下来,光是工具之间的格式转换、参数协调就够折腾的。
更麻烦的是,Trimmomatic的adapter序列需要你自己提供,不同建库试剂盒的adapter序列还不一样,万一填错了,修剪效果大打折扣。cutadapt虽然支持自动检测adapter,但速度偏慢,而且它只管修剪,不管质量过滤和报告生成。
Fastp的设计思路就是把这些零散的需求整合起来。它内置了常见的adapter序列库,支持自动检测adapter,同时完成质量过滤、长度过滤、polyG/polyX修剪、duplication评估、overrepresentation分析,最后生成一份HTML报告和JSON格式的统计文件。一个命令搞定全流程,这就是它的核心价值。
2.2 Fastp的核心功能模块
Fastp的功能可以分成几个大块来理解:
adapter自动检测与修剪。这是Fastp最实用的功能之一。它通过对reads两端进行k-mer分析,自动识别adapter序列,不需要你手动指定。对于双端数据,它还能利用pair overlap信息来辅助判断。实测下来,对于标准建库的Illumina数据,自动检测的准确率非常高。
质量过滤与修剪。Fastp支持按碱基质量进行滑窗修剪(类似Trimmomatic的SLIDINGWINDOW),也支持按平均质量过滤整条read。默认情况下,它会从read两端向中间扫描,遇到质量低于阈值的碱基就切掉。这个逻辑比固定长度截断要合理得多。
polyG/polyX修剪。这个问题在NextSeq/NovaSeq平台上特别常见,因为双色荧光检测的原因,信号缺失时会被读成G。Fastp默认开启polyG修剪,对这类数据非常友好。polyX修剪则针对其他类型的均聚物污染。
UMI处理。Fastp支持在质控阶段直接处理UMI,包括UMI的提取、移动到read名称中、以及基于UMI的去重。这个功能对于低起始量建库、ctDNA检测等场景非常关键。
重复序列评估。Fastp会统计duplication rate,虽然它不做实际的去重操作(那是MarkDuplicates或UMI去重的事),但这个指标对于判断文库复杂度很有参考价值。
过表达序列分析。Fastp会自动检测过表达的序列,这对于发现污染、接头二聚体、rRNA残留等问题很有帮助。
2.3 为什么选择Fastp而不是其他工具
我对比过几个主流方案:
| 工具组合 | 速度 | 功能覆盖 | 易用性 | 报告质量 |
|---|---|---|---|---|
| FastQC + Trimmomatic | 中等 | 需手动配置adapter | 一般 | 分开两份报告 |
| FastQC + cutadapt | 较慢 | adapter检测好 | 一般 | 分开两份报告 |
| Fastp | 快 | 全集成 | 一条命令 | 单份HTML报告 |
| 自写Python脚本 | 慢 | 完全自定义 | 差 | 需自己写 |
Fastp在速度上的优势特别明显。它用C++编写,多线程效率高,处理一个30X的WGS样本(约60GB fastq),在16线程下大概20-30分钟就能跑完。同样的数据用Trimmomatic,时间大概要翻倍。
当然Fastp也不是万能的。它不做比对,所以不能做基于比对的污染检测;它的去重是基于UMI或序列本身的,不如Picard MarkDuplicates那么精细。但对于质控这个环节来说,Fastp已经足够好了。
3. 核心参数详解与实操配置
3.1 输入输出参数:最基础但最容易出错
Fastp的输入输出参数看起来简单,但有几个细节不注意就会踩坑。
单端数据:
fastp -i input.fq -o output.fq双端数据:
fastp -i read1.fq -I read2.fq -o clean1.fq -O clean2.fq注意双端模式下,read1和read2的输入输出参数大小写不同:-i和-I,-o和-O。这个设计一开始让我很不习惯,但用多了也就记住了。
输出报告:
fastp -i input.fq -o output.fq -h report.html -j report.json-h指定HTML报告路径,-j指定JSON报告路径。JSON报告特别有用,方便后续用脚本提取统计指标做批量汇总。
注意:Fastp默认会输出到标准输出(stdout),如果你不指定
-o,结果会直接打印到终端。我第一次用的时候没注意,屏幕上刷了一大堆序列,吓了一跳。所以务必指定输出文件。
还有一个实用参数是--stdout,当你需要把Fastp接入管道时很有用:
fastp -i input.fq --stdout | other_tool3.2 质量过滤参数:怎么设才合理
Fastp的质量过滤参数主要有这几个:
-q/--qualified_quality_phred:碱基质量阈值,默认15。意思是质量值低于15的碱基会被认为是"不合格"。-u/--unqualified_percent_limit:一条read中不合格碱基的比例上限,默认40%。超过这个比例,整条read被丢弃。-n/--n_base_limit:一条read中N碱基的数量上限,默认5。-l/--length_required:修剪后read的最小长度,默认15。--cut_front/--cut_tail/--cut_right:从不同方向进行质量修剪。
关于-q的设置,我的经验是:对于Illumina数据,Q15作为过滤阈值偏宽松,Q20更严格一些。但也不要设太高,Q30会导致大量数据被丢弃,尤其是read末端。一般WGS用Q15,RNA-seq用Q20,具体要看数据质量。
--cut_right这个参数值得单独说一下。它开启滑窗修剪模式,窗口大小由--cut_window_size控制(默认4),滑窗平均质量低于--cut_mean_quality(默认20)时,从窗口开始位置截断。这个逻辑和Trimmomatic的SLIDINGWINDOW很像,但Fastp的实现更快。
fastp -i input.fq -o output.fq \ -q 20 -u 30 -n 3 -l 36 \ --cut_right --cut_window_size 4 --cut_mean_quality 20这套参数是我常用的"严格模式",适合对数据质量要求高的场景。
3.3 Adapter修剪参数:自动还是手动
Fastp默认开启adapter自动检测和修剪。相关参数:
-a/--adapter_sequence:指定read1的adapter序列。--adapter_sequence_r2:指定read2的adapter序列。--detect_adapter_for_pe:双端模式下开启adapter自动检测。
这里有个坑:双端模式下,adapter自动检测默认是关闭的!你需要显式加上--detect_adapter_for_pe。这个设计是因为双端adapter检测需要额外的计算,Fastp为了速度默认关掉了。但实际使用中,我强烈建议双端数据都加上这个参数。
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ --detect_adapter_for_pe如果你明确知道adapter序列,手动指定会更快更准:
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -a AGATCGGAAGAGCACACGTCTGAACTCCAGTCA \ --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT这是标准Illumina TruSeq的adapter序列。不同试剂盒的adapter可能不同,用之前最好确认一下。
还有一个参数--adapter_fasta,可以提供一个FASTA文件包含多个adapter序列,Fastp会逐一比对。对于可能混有多种adapter的样本很有用。
3.4 polyG和polyX修剪:平台特异性处理
-g/--trim_poly_g:开启polyG修剪,默认开启。--poly_g_min_len控制最小长度,默认10。
-x/--trim_poly_x:开启polyX修剪,默认关闭。--poly_x_min_len默认10。
NextSeq和NovaSeq的数据一定要保持polyG修剪开启。我遇到过好几次,用户拿着NovaSeq数据来找我,说比对率特别低,一看fastq,read末端全是G,明显是polyG问题。开启修剪后,比对率直接上去了。
对于其他平台的数据,polyX修剪可以按需开启。但要注意,polyX修剪可能会误伤真实的polyA尾巴(比如RNA-seq中),所以RNA-seq数据要谨慎使用。
3.5 UMI处理参数:低起始量建库的救星
UMI(Unique Molecular Identifier)是一段随机序列,在建库时加到每个原始DNA分子上。它的作用是标记每个原始分子,这样在后续分析中可以区分真正的生物学重复和PCR扩增产生的重复。
Fastp处理UMI的方式很灵活,支持多种UMI位置:
-U/--umi:开启UMI处理。--umi_loc:UMI的位置,可选index1、index2、read1、read2、per_index、per_read。--umi_len:UMI长度。--umi_prefix:UMI前缀,添加到read名称中。--umi_skip:UMI后面需要跳过的碱基数。
最常见的场景是UMI在read1的前几个碱基:
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI这样处理后,UMI序列会被移到read名称中,格式类似@UMI_ACGTACGT:...。后续用umi_tools或fgbio做去重时,就能直接从这个名称中提取UMI。
如果UMI在index中:
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -U --umi_loc=index1 --umi_len=8注意:Fastp的UMI处理只是把UMI提取出来放到read名称中,它本身不做基于UMI的去重。去重需要后续用专门工具完成。这一点很多人会误解。
3.6 去重参数:什么时候该开
Fastp提供了-D/--dedup参数,开启基于序列的去重。它的逻辑是:如果两条read的序列完全相同(或者UMI相同),只保留一条。
这个功能对于去除PCR重复有一定效果,但要注意:
- 它不基于比对位置,所以不同基因组位置但序列相同的read会被误判为重复。
- 对于高覆盖度数据,去重会显著减少数据量。
- 如果后续还要用MarkDuplicates,这里去重就重复了。
我的建议是:如果做了UMI,用--dedup基于UMI去重是合理的;如果没有UMI,一般不在Fastp阶段去重,留给后续的MarkDuplicates处理。
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -U --umi_loc=read1 --umi_len=8 --dedup3.7 其他实用参数
-w/--thread:线程数,默认3。建议设为CPU核心数的1-1.5倍。我一般用16或32。
-z/--compression:输出压缩级别,1-9,默认4。级别越高压缩率越好但越慢。4是个不错的平衡点。
--overrepresentation_analysis:开启过表达序列分析。对于发现污染很有用,但会增加运行时间。
--correction:开启碱基校正。利用双端overlap信息校正错误碱基。对于低质量数据有帮助,但也会增加时间。
-R/--report_title:自定义报告标题。批量处理时很有用,方便区分不同样本。
4. 完整实操流程:从原始数据到干净fastq
4.1 场景设定与数据准备
假设我们有一批双端测序数据,来自NovaSeq平台,建库时加了8bp的UMI在read1前端,需要做完整的质控处理。数据存放在raw_data/目录下,样本名为sample1到sample10。
先看一下数据结构:
ls raw_data/ # sample1_R1.fastq.gz sample1_R2.fastq.gz # sample2_R1.fastq.gz sample2_R2.fastq.gz # ...4.2 单样本Fastp命令设计
针对这个场景,我设计的Fastp命令如下:
fastp \ -i raw_data/sample1_R1.fastq.gz \ -I raw_data/sample1_R2.fastq.gz \ -o clean_data/sample1_R1.clean.fastq.gz \ -O clean_data/sample1_R2.clean.fastq.gz \ -w 16 \ -q 20 \ -u 30 \ -n 3 \ -l 36 \ --detect_adapter_for_pe \ --cut_right \ --cut_window_size 4 \ --cut_mean_quality 20 \ -g \ --poly_g_min_len 10 \ -U \ --umi_loc=read1 \ --umi_len=8 \ --umi_prefix=UMI \ --dedup \ -h reports/sample1.html \ -j reports/sample1.json \ -R "sample1 QC Report" \ -z 4逐段解释这个命令的设计逻辑:
输入输出:用-i/-I指定双端输入,-o/-O指定双端输出。输出用.gz压缩,节省磁盘空间。
线程:-w 16,根据服务器配置调整。Fastp的多线程效率很高,16线程基本能跑满。
质量过滤:-q 20 -u 30 -n 3 -l 36。Q20阈值,不合格碱基比例上限30%,N碱基上限3,最小长度36。这套参数比默认值严格,适合对质量要求高的项目。
adapter检测:--detect_adapter_for_pe,双端模式必须显式开启。
滑窗修剪:--cut_right配合窗口大小4和平均质量20。从5'到3'扫描,遇到低质量窗口就截断。
polyG修剪:-g --poly_g_min_len 10,NovaSeq数据必开。
UMI处理:-U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI,从read1前端提取8bp UMI。
去重:--dedup,基于UMI去重。
报告:-h和-j分别输出HTML和JSON报告,-R设置报告标题。
压缩:-z 4,平衡压缩率和速度。
4.3 批量处理的Shell脚本
单个样本跑通了,接下来要批量处理。这里用Shell脚本的for循环来实现:
#!/bin/bash # 配置 RAW_DIR="raw_data" CLEAN_DIR="clean_data" REPORT_DIR="reports" THREADS=16 # 创建输出目录 mkdir -p ${CLEAN_DIR} ${REPORT_DIR} # 获取样本列表 for R1 in ${RAW_DIR}/*_R1.fastq.gz; do # 提取样本名 sample=$(basename ${R1} _R1.fastq.gz) R2="${RAW_DIR}/${sample}_R2.fastq.gz" # 检查R2是否存在 if [ ! -f "${R2}" ]; then echo "Warning: ${R2} not found, skipping ${sample}" continue fi echo "Processing ${sample}..." fastp \ -i ${R1} \ -I ${R2} \ -o ${CLEAN_DIR}/${sample}_R1.clean.fastq.gz \ -O ${CLEAN_DIR}/${sample}_R2.clean.fastq.gz \ -w ${THREADS} \ -q 20 -u 30 -n 3 -l 36 \ --detect_adapter_for_pe \ --cut_right --cut_window_size 4 --cut_mean_quality 20 \ -g --poly_g_min_len 10 \ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI \ --dedup \ -h ${REPORT_DIR}/${sample}.html \ -j ${REPORT_DIR}/${sample}.json \ -R "${sample} QC Report" \ -z 4 if [ $? -eq 0 ]; then echo "${sample} done." else echo "Error: ${sample} failed!" fi done echo "All samples processed."这个脚本有几个细节值得说明:
basename ${R1} _R1.fastq.gz用来提取样本名。basename的第二个参数是后缀,它会去掉这个后缀。比如raw_data/sample1_R1.fastq.gz会变成sample1。
if [ ! -f "${R2}" ]检查R2文件是否存在,避免因为缺失文件导致脚本中断。
if [ $? -eq 0 ]检查上一条命令的退出状态。Fastp成功返回0,失败返回非0。这样可以及时发现失败的样本。
4.4 并行加速:让批量处理快起来
上面的脚本是串行执行的,10个样本要跑10次。如果服务器核心多,可以并行处理。有两种方式:
方式一:用GNU parallel
ls raw_data/*_R1.fastq.gz | \ sed 's/_R1.fastq.gz//' | \ parallel -j 4 ' sample={} fastp -i raw_data/${sample}_R1.fastq.gz \ -I raw_data/${sample}_R2.fastq.gz \ -o clean_data/${sample}_R1.clean.fastq.gz \ -O clean_data/${sample}_R2.clean.fastq.gz \ -w 8 -q 20 -u 30 -n 3 -l 36 \ --detect_adapter_for_pe \ --cut_right --cut_window_size 4 --cut_mean_quality 20 \ -g --poly_g_min_len 10 \ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI \ --dedup \ -h reports/${sample}.html \ -j reports/${sample}.json \ -R "${sample} QC Report" -z 4 '-j 4表示同时跑4个样本,每个样本用8线程。总共32线程。这样比串行快很多。
方式二:用Shell的后台任务
for R1 in raw_data/*_R1.fastq.gz; do sample=$(basename ${R1} _R1.fastq.gz) R2="raw_data/${sample}_R2.fastq.gz" fastp -i ${R1} -I ${R2} \ -o clean_data/${sample}_R1.clean.fastq.gz \ -O clean_data/${sample}_R2.clean.fastq.gz \ -w 8 -q 20 -u 30 -n 3 -l 36 \ --detect_adapter_for_pe \ --cut_right --cut_window_size 4 --cut_mean_quality 20 \ -g --poly_g_min_len 10 \ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI \ --dedup \ -h reports/${sample}.html \ -j reports/${sample}.json \ -R "${sample} QC Report" -z 4 & # 控制并发数 while [ $(jobs -r | wc -l) -ge 4 ]; do sleep 1 done done wait echo "All done."&把命令放到后台执行,jobs -r | wc -l统计正在运行的后台任务数,超过4个就等待。wait等待所有后台任务完成。
注意:并行处理时要注意内存和磁盘I/O。Fastp本身内存占用不大,但多个实例同时读写磁盘可能会成为瓶颈。如果发现速度上不去,可能是磁盘I/O限制了。
4.5 结果验证与报告解读
跑完之后,先看JSON报告里的关键指标:
# 提取关键统计 cat reports/sample1.json | python3 -c " import json, sys data = json.load(sys.stdin) summary = data['summary'] before = summary['before_filtering'] after = summary['after_filtering'] print(f\"Total reads before: {before['total_reads']}\") print(f\"Total reads after: {after['total_reads']}\") print(f\"Pass rate: {after['total_reads']/before['total_reads']*100:.2f}%\") print(f\"Q30 before: {before['q30_rate']*100:.2f}%\") print(f\"Q30 after: {after['q30_rate']*100:.2f}%\") print(f\"GC content: {after['gc_content']*100:.2f}%\") "一般来说,合格的质控结果应该满足:
- Pass rate在80%以上(严格模式可能70%左右)
- Q30 after在90%以上
- GC content与物种预期相符
- Duplication rate在合理范围(WGS一般<20%,RNA-seq可能更高)
如果Pass rate过低,要检查是不是质量阈值设得太严,或者原始数据本身质量差。如果Q30 after没有明显提升,说明修剪效果不好,可能需要调整参数。
HTML报告更直观,用浏览器打开就能看到各种图表:质量分布、碱基组成、adapter含量、duplication等。我一般会重点看几个图:
- Quality curve:修剪前后的质量分布对比
- Base composition:碱基组成是否正常,有没有异常偏移
- Adapter content:adapter是否被有效去除
- Duplication:重复率是否正常
5. 常见问题与排查技巧实录
5.1 Fastp运行报错怎么办
报错一:Failed to open file
最常见的原因是路径写错了,或者文件不存在。检查:
ls -lh raw_data/sample1_R1.fastq.gz如果文件存在但还是报错,可能是权限问题:
chmod +r raw_data/sample1_R1.fastq.gz报错二:std::bad_alloc
内存不足。Fastp处理大文件时需要一定内存,尤其是开启overrepresentation分析时。解决方法:
- 减少线程数(每个线程会占用一定内存)
- 关闭overrepresentation分析
- 增加服务器内存
报错三:Segmentation fault
段错误,通常是输入文件损坏。检查fastq文件完整性:
gzip -t raw_data/sample1_R1.fastq.gz如果gzip测试报错,说明文件损坏,需要重新下载或从备份恢复。
5.2 质控效果不理想的排查思路
问题:adapter去除不干净
先确认adapter序列是否正确。如果自动检测效果不好,手动指定:
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -a AGATCGGAAGAGCACACGTCTGAACTCCAGTCA \ --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT如果还是不行,可能是adapter发生了突变或者有多个adapter。用--adapter_fasta提供多个候选序列。
问题:polyG修剪过度
如果发现read被切得太短,可能是polyG阈值设得太低。默认--poly_g_min_len 10,可以提高到15或20:
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -g --poly_g_min_len 15问题:UMI提取不正确
检查UMI位置和长度是否与建库方案一致。如果不确定,可以先不开启UMI处理,跑一遍Fastp,看看read前几个碱基的组成。如果前8bp的碱基组成是随机的(每种碱基约25%),那很可能就是UMI。
# 查看前10bp的碱基组成 zcat raw_data/sample1_R1.fastq.gz | head -1000 | \ awk 'NR%4==2 {print substr($0,1,10)}' | \ sort | uniq -c | sort -rn | head -205.3 性能优化技巧
技巧一:用管道避免中间文件
如果Fastp后面还有其他处理步骤,可以用管道直接传递:
fastp -i input.fq --stdout -w 8 | \ bwa mem -t 8 reference.fa - | \ samtools sort -@ 8 -o output.bam这样避免了写中间fastq文件,节省磁盘I/O。
技巧二:合理设置压缩级别
-z 4是默认值,平衡了速度和压缩率。如果磁盘空间紧张,可以设-z 6或-z 9,但会慢一些。如果追求速度,设-z 1或-z 2。
技巧三:用--stdin从管道读取
Fastp支持从标准输入读取:
zcat input.fq.gz | fastp --stdin -o output.fq这在处理流式数据时很有用。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| Pass rate过低 | 质量阈值太严 | 降低-q或-u |
| Q30 after无提升 | 修剪参数不当 | 调整--cut_right参数 |
| adapter残留 | 检测未开启或序列不对 | 加--detect_adapter_for_pe或手动指定 |
| read过短 | polyG/polyX修剪过度 | 提高--poly_g_min_len |
| UMI未提取 | 位置或长度不对 | 检查--umi_loc和--umi_len |
| 运行速度慢 | 线程数不足或I/O瓶颈 | 增加-w,检查磁盘 |
| 内存不足 | 线程过多或开启overrepresentation | 减少线程,关闭overrepresentation |
| 输出文件为空 | 输入文件损坏或参数错误 | 检查输入文件,查看日志 |
5.5 几个我踩过的坑
坑一:双端adapter检测默认关闭
这个前面提过,但值得再强调。我第一次用Fastp处理双端数据时,没加--detect_adapter_for_pe,结果adapter残留严重。后来看文档才发现这个默认行为。现在我的脚本里这个参数是必加的。
坑二:UMI处理不等于UMI去重
Fastp的-U只是把UMI提取到read名称中,--dedup才是去重。而且--dedup在没有UMI时是基于序列去重,有UMI时是基于UMI去重。这两个参数要配合使用。
坑三:polyG修剪对非NextSeq数据的影响
有一次处理MiSeq数据,习惯性开了polyG修剪,结果发现一些真实的polyG区域被误切了。后来查资料才知道,polyG问题主要是双色荧光平台的特性,MiSeq是四色荧光,不存在这个问题。所以polyG修剪要按平台决定是否开启。
坑四:JSON报告中的百分比是小数
Fastp的JSON报告中,q30_rate、gc_content这些字段是0-1之间的小数,不是百分比。我第一次用脚本提取时忘了乘100,结果报告里Q30是0.95%,闹了笑话。
坑五:输出文件名不要和输入相同
Fastp会先读输入再写输出,但如果输入输出文件名相同,可能会出问题。虽然Fastp有保护机制,但最好还是用不同的文件名。
6. 进阶技巧:让Fastp发挥更大价值
6.1 与umi_tools的配合使用
Fastp处理完UMI后,read名称中会带有UMI信息。接下来可以用umi_tools做基于UMI的去重和定量:
# Fastp处理 fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI # 比对 bwa mem -t 16 reference.fa c1.fq c2.fq | \ samtools sort -@ 16 -o aligned.bam # 提取UMI并去重 umi_tools extract --stdin=c1.fq --stdout=c1.umi.fq \ --read2-in=c2.fq --read2-out=c2.umi.fq \ --bc-pattern=NNNNNNNN # 或者从BAM中提取 umi_tools dedup -I aligned.bam -S dedup.bam \ --extract-umi-method=read_name \ --umi-separator=:注意Fastp的UMI前缀格式是UMI_,umi_tools默认的分隔符是_,可能需要调整--umi-separator参数。
6.2 用Shell脚本做质控报告汇总
批量处理完后,通常需要把所有样本的统计指标汇总成一张表。用Shell + Python可以轻松搞定:
#!/bin/bash echo -e "Sample\tTotal_Reads\tPass_Rate\tQ30_Before\tQ30_After\tGC\tDup_Rate" > qc_summary.tsv for json in reports/*.json; do sample=$(basename ${json} .json) python3 -c " import json with open('${json}') as f: data = json.load(f) s = data['summary'] b = s['before_filtering'] a = s['after_filtering'] d = data.get('duplication', {}) print(f\"${sample}\t{b['total_reads']}\t{a['total_reads']/b['total_reads']*100:.2f}%\t{b['q30_rate']*100:.2f}%\t{a['q30_rate']*100:.2f}%\t{a['gc_content']*100:.2f}%\t{d.get('rate', 0)*100:.2f}%\") " >> qc_summary.tsv done echo "Summary written to qc_summary.tsv"这个脚本会生成一个TSV文件,可以直接用Excel打开或者导入R做可视化。
6.3 处理特殊类型数据的参数调整
RNA-seq数据:
- 不要开polyX修剪,会误伤polyA尾巴
--cut_right可以保留,但窗口质量阈值可以放宽到15- 考虑开启
--correction,利用双端overlap校正错误
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ --detect_adapter_for_pe \ --cut_right --cut_window_size 4 --cut_mean_quality 15 \ -g --poly_g_min_len 10 \ --correction \ -q 15 -u 40 -l 36扩增子数据:
- 长度过滤要严格,因为扩增子长度是已知的
- adapter修剪要彻底,因为扩增子数据adapter污染通常较严重
- 考虑开启
--dedup,扩增子数据重复率通常很高
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ --detect_adapter_for_pe \ -l 200 -q 20 -u 20 \ --dedup \ -g低起始量数据:
- UMI处理必开
- 去重必开
- 质量阈值可以适当放宽,因为低起始量数据本身质量可能就一般
fastp -i r1.fq -I r2.fq -o c1.fq -O c2.fq \ --detect_adapter_for_pe \ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI \ --dedup \ -q 15 -u 40 -l 30 \ -g6.4 与Nextflow/Snakemake的集成
如果项目规模大,建议把Fastp集成到工作流管理工具中。以Nextflow为例:
process fastp { tag "$sample" publishDir "results/clean", mode: 'copy' input: tuple val(sample), path(r1), path(r2) output: tuple val(sample), path("${sample}_R1.clean.fastq.gz"), path("${sample}_R2.clean.fastq.gz") path("${sample}.html") path("${sample}.json") script: """ fastp \\ -i ${r1} -I ${r2} \\ -o ${sample}_R1.clean.fastq.gz \\ -O ${sample}_R2.clean.fastq.gz \\ -w ${task.cpus} \\ -q 20 -u 30 -n 3 -l 36 \\ --detect_adapter_for_pe \\ --cut_right --cut_window_size 4 --cut_mean_quality 20 \\ -g --poly_g_min_len 10 \\ -U --umi_loc=read1 --umi_len=8 --umi_prefix=UMI \\ --dedup \\ -h ${sample}.html -j ${sample}.json \\ -R "${sample} QC Report" -z 4 """ }这样可以利用Nextflow的并行调度、错误重试、断点续跑等功能,比裸写Shell脚本更可靠。
6.5 质控后的数据管理建议
质控完成后,建议保留以下文件:
- 干净的fastq文件(压缩)
- Fastp的HTML报告
- Fastp的JSON报告
- 运行日志(如果有)
- 使用的命令或脚本
目录结构建议:
project/ ├── raw_data/ # 原始数据(只读) ├── clean_data/ # 质控后数据 ├── reports/ # 质控报告 │ ├── html/ │ └── json/ ├── scripts/ # 处理脚本 ├── logs/ # 运行日志 └── qc_summary.tsv # 汇总统计原始数据一定要保留,不要为了省空间删掉。万一后续发现质控参数需要调整,还能重新跑。我一般会在项目结束后把原始数据归档到冷存储,但至少保留到项目发表。
7. 一些个人体会
Fastp这个工具我用了快五年,从最早的0.20版本到现在,功能越来越完善。它最大的价值在于把质控流程标准化了——以前每个人写的质控脚本都不一样,现在一个Fastp命令就能覆盖大部分需求,结果可复现,报告可比较。
但工具再好,也只是工具。质控参数怎么设,还是要根据具体的数据类型、建库方案、下游分析需求来决定。我见过有人直接抄别人的参数,结果数据被切得太狠,后面分析灵敏度下降。也见过有人参数太宽松,adapter没去干净,导致假阳性。
我的建议是:新项目开始时,先拿一两个样本做参数测试,看看不同参数下的Pass rate、Q30、adapter去除效果,找到最适合这个项目的参数组合,再批量处理。这个前期投入是值得的,能避免后面返工。
另外,Fastp的报告一定要认真看。很多人跑完Fastp就直接进入下一步,报告看都不看。其实报告里有很多有价值的信息:过表达序列可能提示污染,duplication rate可能提示文库复杂度问题,adapter含量可能提示建库质量。这些信息对于判断数据是否可用非常关键。
最后说一个实际工作中的小习惯:我会把每次Fastp运行的完整命令记录在一个commands.log文件里,包括日期、样本名、参数。这样半年后回头看,还能知道当时是怎么处理的。这个习惯帮我省了很多次"当时到底怎么跑的"的困惑。