处理原始scRNA-seq数据
2.1 FastQC
当你获取到单细胞下机数据的时候,第一步需要做的就是检查数据质量。
本次教程使用的数据为(Kolodziejczyk et al. 2015)发表的文章内使用SMART-seq技术做的单细胞测序结果。
示例数据下载地址[1]
ERR522959_1.fastq
和 ERR522959_2.fastq
数据质量查看这里我们推荐使用fastQC。
fastQC是由Babraham Bioinformatics出品的一个软件,目前是二代测序领域最常用的一个质控软件,官网点击[2]
)
2.1.1 安装
使用conda即可
conda install -c bioconda fastqc
2.1.2 下载数据
wget ftp://ftp.sra.ebi.ac.uk/vol1/fastq/ERR522/ERR522959/ERR522959_1.fastq.gz
wget ftp://ftp.sra.ebi.ac.uk/vol1/fastq/ERR522/ERR522959/ERR522959_2.fastq.gz
#查看数据
less ERR522959_1.fastq.gz
2.1.3 生成质量报告
mkdir fastqc_results && fastqc -o fastqc_results ./ERR522959_1.fastq.gz ./ERR522959_2.fastq
# 运行结束后会生成一个html文件和一个gzip文件,打开html文件即可查看质量分析报告
sxj@node5~/scRNA-seq/class/fastqc_results$ll
total 1.6M
drwxr-xr-x 2 sxj users 124 Jul 7 2018 .
drwxr-xr-x 3 sxj users 81 Jul 7 2018 ..
-rw-r--r-- 1 sxj users 340K Jul 7 2018 ERR522959_1_fastqc.html
-rw-r--r-- 1 sxj users 461K Jul 7 2018 ERR522959_1_fastqc.zip
-rw-r--r-- 1 sxj users 337K Jul 7 2018 ERR522959_2_fastqc.html
-rw-r--r-- 1 sxj users 456K Jul 7 2018 ERR522959_2_fastqc.zip
# zip文件打开后和html内容一致的,不再展示
fastqc 报告的具体参数这里不加赘述,请自行理解
2.2 Trimming Reads 清洗数据
本课程使用的处理软件为Trim Galore!
因为在上一步的报告中我们发现有部分接头序列,所以这里需要进行数据修剪。
修剪结束以后需要再次使用fastQC生成质量报告查看处理结果。
$mkdir fastqc_trimmed_results && trim_galore --nextera -o fastqc_trimmed_results ./ERR522959_1.fastq.gz ./ERR522959_2.fastq.gz
2.3 File formats 数据格式
FastQ 是大部分最原始scRNASeq的数据,所有的单细胞RNA-seq测序数据都是双端paired-end测序,barcode序列可能会出现在一个read或者两个reads内(依据使用protocol的不同),但是使用UMI的方法产生的数据,一个read会包含UMI/barcode/adapters但是由于测序长度原因会不包含转录本序列,所以这个方法后续的处理是以单端测序的形式处理的。
FastQ 文件拥有以下的格式:
>ReadID
READ SEQUENCE
+
SEQUENCING QUALITY SCORES
2.3.2 BAM文件格式
BAM文件以一个标准化的高效率的形式存储了比对后的文件的,BAM文件是由可读式的SAM文件高效压缩来的,一般来说会包含一个header(包含样本准备,测序和比对信息)。
比对文件的标准格式如下
1. QNAME: read name(一般来说可以的话会包含UMI及Barcode信息)
2. FLAG:数值标签指明比对情况(包括比对到多个位置/单端read匹配等等)
3. RNAME: 比对到的参考基因组
4. POS: 比对到参考基因组的起始位点
5. MAPQ:比对质量
6. CIGAR:字符信息(表明对比的情况,是否有突变缺失插入等)
7. RNEXT:paired-end read 比对到的参考基因组染色体
8. PNEXT:paired-end read 比对到的参考基因组的起始位点
9. TLEN:比对到的长度
10. SEQ: read序列
11. QUAL:read质量
bam文件到sam文件以及sam文件到bam文件的转换可以通过samtools完成
samtools view -S -b file.sam > file.bam
samtools view -h file.bam > file.sam
很多时候还会有将bam/cram文件转化为fastq文件的需求,这个可以通过bedtools完成:
# sort reads by name
samtools sort -n original.bam -o sorted_by_name.bam
# remove secondary alignments
samtools view -b -F 256 sorted_by_name.bam -o primary_alignment_only.bam
# convert to fastq
bedtools bamtofastq -i primary_alignment_only.bam -fq read1.fq -fq2 read2.fq
2.3.3 CRAM 文件
CRAM类似于BAM文件,先对比较少见,包含的信息比bam文件少,一般Sanger/EBI的测序结果是这个形式,转化为fastq文件的时候需要参考基因组。
export REF_CACHE=/path_to/cache_directory_for_reference_genome
samtools view -b -h -T reference_genome.fasta file.cram -o file.bam
samtools view -C -h -T reference_genome.fasta file.bam -o file.cram
2.3.4 手动检视文件
使用less & more 以及samtools view可以完成对的文件的查看
less file.txt
more file.txt
# counts the number of lines in file.txt
wc -l file.txt
samtools view -h file.[cram/bam] | more
# counts the number of lines in the samtools output
samtools view -h file.[cram/bam] | wc -l
2.3.5 基因组文件的(FASTA, GTF)
为了比对read,我们需要下载参考基因组以及对应的注释文件。一般来说有以下三个下载来源:
Ensembl
NCBI
UCSC Genome Browser
有关差别我在高通量测序数据处理学习记录(零):NGS分析如何选择合适的参考基因组和注释文件这篇文章中有详细的论述。
另外存有的一个问题就是如果需要往fasta文件和gtf文件内添加内容,我们需要如何操作。(常见的就是ERCC的问题,包括转入的质粒或者CRISPR相关序列)目前来说没有标准化的一个方法,我们这里提供了一个Perl脚本完成这个任务。
# Converts the Annotation file from
# https://www.thermofisher.com/order/catalog/product/4456740 to
# gtf and fasta files that can be added to existing genome fasta & gtf files.
my @FASTAlines = ();
my @GTFlines = ();
open (my $ifh, "ERCC_Controls_Annotation.txt") or die $!;
<$ifh>; #header
while (<$ifh>) {
# Do all the important stuff
chomp;
my @record = split(/\t/);
my $sequence = $record[4];
$sequence =~ s/\s+//g; # get rid of any preceeding/tailing white space
$sequence = $sequence."NNNN";
my $name = $record[0];
my $genbank = $record[1];
push(@FASTAlines, ">$name\n$sequence\n");
# is GTF 1 indexed or 0 indexed? -> it is 1 indexed
# + or - strand?
push(@GTFlines, "$name\tERCC\tgene\t1\t".(length($sequence)-2)."\t.\t+\t.\tgene_id \"$name-$genbank\"; transcript_id \"$name-$genbank\"; exon_number \"1\"; gene_name \"ERCC $name-$genbank\"\n");
push(@GTFlines, "$name\tERCC\ttranscript\t1\t".(length($sequence)-2)."\t.\t+\t.\tgene_id \"$name-$genbank\"; transcript_id \"$name-$genbank\"; exon_number \"1\"; gene_name \"ERCC $name-$genbank\"\n");
push(@GTFlines, "$name\tERCC\texon\t1\t".(length($sequence)-2)."\t.\t+\t.\tgene_id \"$name-$genbank\"; transcript_id \"$name-$genbank\"; exon_number \"1\"; gene_name \"ERCC $name-$genbank\"\n");
} close($ifh);
# Write output
open(my $ofh, ">", "ERCC_Controls.fa") or die $!;
foreach my $line (@FASTAlines) {
print $ofh $line;
} close ($ofh);
open($ofh, ">", "ERCC_Controls.gtf") or die $!;
foreach my $line (@GTFlines) {
print $ofh $line;
} close ($ofh);
实际演示
# 脚本保存为ERCC_fasta_gtf_transform.pl
wget https://assets.thermofisher.com/TFS-Assets/LSG/manuals/cms_095047.txt
mv cms_095047.txt ERCC_Controls_Annotation.txt
perl ERCC_fasta_gtf_transform.pl
# 搞定
sxj@node5~/scRNA-seq/class/fastqc_trimmed_results/ERCC$ll
total 224K
drwxr-xr-x 2 sxj users 122 Jul 8 2018 .
drwxr-xr-x 4 sxj users 4.0K Jul 8 2018 ..
-rw-r--r-- 1 sxj users 86K Jul 21 2017 ERCC_Controls_Annotation.txt
-rw-r--r-- 1 sxj users 83K Jul 8 2018 ERCC_Controls.fa
-rw-r--r-- 1 sxj users 43K Jul 8 2018 ERCC_Controls.gtf
-rw-r--r-- 1 sxj users 1.6K Jul 8 02:24 ERCC_fasta_gtf_transform.pl
2.4 Demultiplexing
我不知道该如何翻译这个词汇,初步理解为细胞区分或者说是提取barcode/UMI信息,这个过程往往取决于你使用的方法。其中适用性最强的当属zUMIs,可以提取大部分现有平台产生数据的barcode/UMI信息。话说回来,现在大部分的数据一般都是整理好返还给你matrix,如果是自己建库产生的数据,就需要自己写一个script根据序列信息和位置信息提取对应所需。
对任何数据而言,demultiplexing包括在1-read/2-read确认和移除cell-barcode序列(包括UMI),本篇文章作者贴出了他们使用的perl代码脚本 publicly available进行一系列的处理。下面的是举例实战:
perl 1_Flexible_UMI_Demultiplexing.pl 10cells_read1.fq 10cells_read2.fq "C12U8" 10cells_barcodes.txt 2 Ex
## Doesn't match any cell: 0
## Ambiguous: 0
## Exact Matches: 400
## Contain mismatches: 0
## Input Reads: 400
## Output Reads: 400
## Barcode Structure: 12 bp CellID followed by 8 bp UMI
perl 1_Flexible_FullTranscript_Demultiplexing.pl 10cells_read1.fq 10cells_read2.fq "start" 12 10cells_barcodes.txt 2 Ex
##
## Doesn't match any cell: 0
## Ambiguous: 0
## Exact Matches: 400
## Contain Mismatches: 0
## Input Reads: 400
## Output Reads: 400
对于包含UMI的数据,demultiplexing这一步包括把UMI序列给添加到read name上,如果数据的产生基于droplet技术或者SeqWell技术,将barcode加在read named上也可以避免产生相对较大的数据。
2.4.1 确认包含有细胞的droplets/microwells
基于droplet的建库方法只有一小部分的droplets既包含bead和一个完成的细胞。实际中会出现RNA污染的问题,破碎的细胞释放出RNA进入其他的droplet中,和那些正常的droplet一起建库测序从而导致背景噪音。而droplet的大小,扩增效率,测序的因素等会导致无论是背景噪音还是真实的细胞都会出现建库大小横跨较大范围。目前有不同的方法去鉴别对阵真实细胞的barcode。
大部分的方法都是利用每个barcode含有的转录本数量的比例寻找一个“break
point”(或者简单的指定每个细胞需要包含不低于10个转录本),point上游的为大部分的真实细胞和少量的背景,point下游的假设为纯背景信息。以下为示例(R语言环境):
# 原始文件
https://github.com/hemberg-lab/scRNA.seq.course/raw/master/droplet_id_example_per_barcode.txt.gz
https://github.com/hemberg-lab/scRNA.seq.course/raw/master/droplet_id_example_truth.gz
# 读入数据
umi_per_barcode <- read.table("droplet_id_example_per_barcode.txt.gz")
truth <- read.delim("droplet_id_example_truth.gz", sep=",")
# 排序后进行作图
barcode_rank <- rank(-umi_per_barcode[,2])
plot(barcode_rank, umi_per_barcode[,2], xlim=c(1,8000))
这里我们可以看到有一个指数曲线存在,所以这里log一下让check point更加突出
log_lib_size <- log10(umi_per_barcode[,2])
plot(barcode_rank, log_lib_size, xlim=c(1,8000))
OK这里明显更加陡峭了一点,随后我们就可以根据肉眼来鉴别一下check point在哪或者使用算法来创建重复性较高的check point:
# inflection point
o <- order(barcode_rank)
log_lib_size <- log_lib_size[o]
barcode_rank <- barcode_rank[o]
rawdiff <- diff(log_lib_size)/diff(barcode_rank)
inflection <- which(rawdiff == min(rawdiff[100:length(rawdiff)], na.rm=TRUE))
plot(barcode_rank, log_lib_size, xlim=c(1,8000))
abline(v=inflection, col="red", lwd=2)
# 简单理解就是算斜率,取一个最大的,那个点就是check point
随后我们再来检验一下检测到的比例
threshold <- 10^log_lib_size[inflection]
cells <- umi_per_barcode[umi_per_barcode[,2] > threshold,1]
TPR <- sum(cells %in% truth[,1])/length(cells)
Recall <- sum(cells %in% truth[,1])/length(truth[,1])
c(TPR, Recall)
[1] 1.0000000 0.7831707
inflection
[1] 3212
取前3212个细胞,100%的阳性率,78%的检出率,已经比较可以了。
或者通过建立一个mixture model并且找到check point
set.seed(-92497)
# mixture model
require("mixtools")
## Loading required package: mixtools
## mixtools package, version 1.1.0, Released 2017-03-10
## This package is based upon work supported by the National Science Foundation under Grant No. SES-0518772.
mix <- normalmixEM(log_lib_size)
## number of iterations= 43
plot(mix, which=2, xlab2="log(mol per cell)")
p1 <- dnorm(log_lib_size, mean=mix$mu[1], sd=mix$sigma[1])
p2 <- dnorm(log_lib_size, mean=mix$mu[2], sd=mix$sigma[2])
if (mix$mu[1] < mix$mu[2]) {
split <- min(log_lib_size[p2 > p1])
} else {
split <- min(log_lib_size[p1 > p2])
}
split
[1] 0.7781513
和第一个方法差不多
或者也可以使用固定比例来筛选:
n_cells <- length(truth[,1])
# CellRanger
totals <- umi_per_barcode[,2]
totals <- sort(totals, decreasing = TRUE)
# 99th percentile of top n_cells divided by 10
thresh = totals[round(0.01*n_cells)]/10
plot(totals, xlim=c(1,8000))
abline(h=thresh, col="red", lwd=2)
thresh
[1] 3764.3
作者新开发了一个R包专门来解决这个问题→EmptyDrops, 目前还处于测试版本,可从GitHub上下载使用,它使用X个细胞的全基因表达数据来代表所有的droplets(液滴)同时使用counts数最低的那群droplets作为背景来估计背景"RNA"的表达模式,随后寻找与背景表达模式相异的那群细胞当作真实检测到的细胞,这个方法同时结合了一个拐点方法来进行操作,因为背景RNA的表达模式往往和一个群体里面最大那群细胞的表达模式类似。目前来说,EmptyDrops是仅有的一个可以在高差异度样本里鉴定非常少量细胞barcode的软件。下面列出测试代码:
# 下载数据
wget https://github.com/tallulandrews/Tmp/blob/master/droplet_id_example.rds
require("Matrix")
raw.counts <- readRDS("droplet_id_example.rds")
require("DropletUtils")
# emptyDrops
set.seed(100)
e.out <- emptyDrops(my.counts)
is.cell <- e.out$FDR <= 0.01
sum(is.cell, na.rm=TRUE)
plot(e.out$Total, -e.out$LogProb, col=ifelse(is.cell, "red", "black"),
xlab="Total UMI count", ylab="-Log Probability")
cells <- colnames(raw.counts)[is.cell]
TPR <- sum(cells %in% truth[,1])/length(cells)
Recall <- sum(cells %in% truth[,1])/length(truth[,1])
c(TPR, Recall)
2.5 利用STAR进行比对
现在我们已经对reads进行了去除barcode和UMI的处理,下一步的操作就是把序列比对到参考基因组上去,如果我们需要定量基因表达量并且找出哪些基因是特异表达的,我们就需要一些特定的比对工具了。
我们今天主要介绍两个比对工具,第一个是STAR,我在之前的文章里介绍过高通量测序数据处理学习记录(一):比对软件STAR的使用,对于每个测序read,STAR都会找到一个能够匹配到参考基因组一个或者多个位置上的最长的可能序列。以下图举例,我们有一条read(蓝色)横跨两条外显子和一个剪切点(紫色)。STAR发现read的第一部分可以匹配到第一个外显子上,同时read的第二部分可以匹配到第二个外显子上,由于STAR可以通过这种形式识别剪切事件,所以它也被描述为“剪切感知”比对软件。
一般来说STAR将一条read比对到参考基因组上,同时也会检测是否存在新的剪切事件或者染色质易位,这就导致STAR需要很大RAM,尤其是你的参考基因组很大的时候(例如:人类和老鼠)。所以下面演示为了节省时间,我们会使用STAR将read比对到一个只包含2000个转录本的的转录参考组(注意这在正常情况下是不正常的个)。
STAR的比对需要两个步骤。在第一个步骤,用户需要向STAR提交参考基因组序列(FASTA格式)以及注释文件(GTF文件),STAR会依据此构建索引(index),第二步,STAR会将reads比对到索引上(index)。
今天我们只比对2000个转录组的数据而不是一整个参考基因组,可以从ensembl上获取很多模式生物的
参考基因组。构建索引的代码如下:
# download data
wget https://github.com/hemberg-lab/scRNA.seq.course/raw/master/2000_reference.transcripts.fa
mkdir indices
mkdir indices/STAR
STAR --runThreadN 4 --runMode genomeGenerate --genomeDir indices/STAR --genomeFastaFiles Share/2000_reference.transcripts.fa
随后进行比对:
mkdir results
mkdir results/STAR
STAR --runThreadN 4 --genomeDir indices/STAR --readFilesIn Share/ERR522959_1.fastq Share/ERR522959_2.fastq --outFileNamePrefix results/STAR/
学习任务:
- 阅读STAR manual进行命令学习
- 如果我们基于整个基因组创建索引的时候,命令会有什么区别
- 学习理解比对参数
- 尝试着去理解比对后的结果和报告
2.6 Kallisto 和 Pseudo-Alignment
STAR是一个基于reads的比对工具,而Kallisto则是一个pseudo-aligner(伪比对工具——翻译不全面,请自行理解) Bray et al. 2016[3]。而STAR和Kallisto的区别在于,STAR直接比对reads到参考基因组上而Kallisto则是将k-mers比对到参考基因组上。
2.6.1 什么是k-mer?
k-mer就是用一个read提取的长度为k的序列,举例来说,想象我们现在拥有一个read序列为ATCCCGGGTTAT并且我们想要基于此创造7-mers。我们会依次隔一个base选取长度为7的序列。示意如图二:2.6.2为什么是比对k-mers而不是reads?
主要有两个原因:
- Pseudo-aligners利用k-mers和一个巧妙的计算方法可以更快的完成比对。如果你对算法有兴趣,详情请参考文献:Bray et al., 2017
- 在某些条件下,pseudo-aligners可以比传统的比对软件更好的处理测序错误,想象假如我们在序列的第一个碱基拥有一个错误,它会影响第一个7-mer而不是影响后面的7-mers。
2.6.3 Kallisto的pseudo模式
Kallisto贴心的专门为单细胞测序数据准备了一个特殊模式。不像STAR,Kallisto的pseudo模式会将k-mers比对到转录组上而不是基因组上,这就意味着的Kallisto会将reads比对到剪切异构体上而不是基因上,比对到异构体上而不是基因上对于单细胞RNA-seq是很有挑战性的,原因如下:
- 单细胞RNA-seq的覆盖度比bulk RNA-seq要低,意味着从reads上可以获取的信息量减少了
- 许多RNA-seq技术拥有3' 端覆盖度的偏向性,这就意味着如果两个异构体只是在他们的5'端有差异的话,就不大可能判断read来源于哪条异构体。
- 一些单细胞测序方法的reads的读长比较短,也意味着很难通过较短的序列信息判断来源于哪个异构体
Kallisto的pseudo模式采取了一种略微区别于pseudo-alinment的方法。它将reads归类于某个异构体,而是将其归类于equivalence classes(等价类)。这就意味着假如一个read可以比对到多个异构体上,Kallisto会记录这个read到包含这几个异构体的equivalence classes里面,下游的分析也就区别于利用基因或者异构体的分析,而是以equivalence classes取代之。图三展示了比对原理:
今天的演示教程是对一个细胞的测序结果进行比对,但Kallisto可以通多对多细胞数据(也就是多批的单细胞数据)进行比对,详细的参数可见Manual。
就像STAR一样,你也需要在比对前为Kaliisto构建索引(index)。代码如下:
mkdir indices/Kallisto
kallisto index -i indices/Kallisto/transcripts.idx Share/2000_reference.transcripts.fa
TASK:阅读manual文档学习如何使用pseudo模式进行比对并进行实战操作。
2.6.4 Ksllisto Pseudo模式的比对
比对可见如下代码:
mkdir results/Kallisto
kallisto pseudo -i indices/Kallisto/transcripts.idx -o results/Kallisto -b batch.txt
index文件为上一步所创立,batch文件格式如下:
#id file1 file 2
cell1 cell1_1.fastq.gz cell1_1.fastq.gz
cell2 cell2_1.fastq.gz cell2_1.fastq.gz
cell3 cell3_1.fastq.gz cell3_1.fastq.gz
...
“#” 号所在的一行将会被忽略,第一列为cell id, 后两列为双端测序文件,如果加入
single
参数就只需要提供一个文件
--umi
参数被指定的时候,batch文件的格式为:
#id umi-file file-1
cell1 cell_1.umi cell_1.fastq.gz
cell2 cell_2.umi cell_2.fastq.gz
cell3 cell_3.umi cell_3.fastq.gz
...
其中umi文件的内容格式为:
TTACACTGAC
CCACTCTATG
CAGGAAATCG
...
需要注意的是,umi的顺序需要和fastq文件里面的reads顺序一致。
2.6.5 Kallisto Pseudo-Alignment比对结果的解读
上述的命令会产生四个文件:
- matrix.cells
- 包含一系列的细胞ID
- matrix.ec
- 包含比对过程中使用过的equivalence classes,每一行的第一个数字是class ID, 以“10 1,2,3”举例, 其代表的意思为,class 10包含有转录本ID 1,2和3。ID的序号对应着转录本在参考基因组上出现的顺序。需要注意的是,这里使用的是0-based的计数形式,也就是说ID 1,2,3对应的是2,3,4号转录本。
- matrix.tsv
- 包含每个细胞里面比对到对应equivalence classes上的read数目,第一个数字为equivalence classes class ID(与matrix.ec匹配),第二个数字为细胞DI,对应matrix.cells文件,第三个数字就是该细胞比对到该class的read数目。以“5 1 3”举例,其代表的意思为细胞1号中有3个reads比对到第5类equivalence classes上,这里同样使用的是0-based的计数形式,也就是说细胞1号对应着matrix.cells文件第二行的信息。
- run_info.json
- 包含运行信息,可以忽略。
参考文献
-
Kolodziejczyk, Aleksandra A., Jong Kyoung Kim, Valentine Svensson, John C. Marioni, and Sarah A. Teichmann. 2015. “The Technology and Biology of Single-Cell RNA Sequencing.” Molecular Cell 58 (4). Elsevier BV: 610–20. doi:10.1016/j.molcel.2015.04.005. ↩
-
babraham.ac.uk ↩
-
Bray, Nicolas L, Harold Pimentel, Páll Melsted, and Lior Pachter. 2016. “Near-Optimal Probabilistic Rna-Seq Quantification.” Nat Biotechnol 34 (5): 525–27. doi:10.1038/nbt.3519. ↩