Pre-processing
本文將要介紹的是在R中進行RNA-seq 數據預處理的實戰代碼
原文地址:https://bioinformatics-core-shared-training.github.io/RNAseq-R/rna-seq-preprocessing.nb.html
主要包括以下方面:
載入mapping, counting后的數據
過濾低表達基因
對表達數據進行質量控制
標準化處理
本次流程需要的包
library(edgeR)
library(limma)
library(Glimma)
library(gplots)
library(org.Mm.eg.db)
library(RColorBrewer)
- 溫馨提示:
org.Mm.eg.db
有60多M,個人推薦沒有安裝過的朋友先切換到國內的鏡像,再下載這個包。
options(repos = 'https://mirrors.tuna.tsinghua.edu.cn/CRAN/')
getOption('repos')
BiocManager::install('org.Mm.eg.db')
另外,本次所需要的數據均可以在該網站下載:https://figshare.com/s/1d788fd384d33e913a2a
下載的數據有:
- Sampleinfo.txt
- GSE60450_Lactation-GenewiseCounts.txt
- mouse_c2_v5.rdata
- mouse_H_v5.rdata
載入數據
首先,我們讀入樣本信息"SampleInfo.txt"
> sampleinfo <- read.delim("data/SampleInfo.txt")
> sampleinfo
FileName SampleName CellType Status
1 MCL1.DG_BC2CTUACXX_ACTTGA_L002_R1 MCL1.DG luminal virgin
2 MCL1.DH_BC2CTUACXX_CAGATC_L002_R1 MCL1.DH basal virgin
3 MCL1.DI_BC2CTUACXX_ACAGTG_L002_R1 MCL1.DI basal pregnant
4 MCL1.DJ_BC2CTUACXX_CGATGT_L002_R1 MCL1.DJ basal pregnant
5 MCL1.DK_BC2CTUACXX_TTAGGC_L002_R1 MCL1.DK basal lactate
6 MCL1.DL_BC2CTUACXX_ATCACG_L002_R1 MCL1.DL basal lactate
7 MCL1.LA_BC2CTUACXX_GATCAG_L001_R1 MCL1.LA basal virgin
8 MCL1.LB_BC2CTUACXX_TGACCA_L001_R1 MCL1.LB luminal virgin
9 MCL1.LC_BC2CTUACXX_GCCAAT_L001_R1 MCL1.LC luminal pregnant
10 MCL1.LD_BC2CTUACXX_GGCTAC_L001_R1 MCL1.LD luminal pregnant
11 MCL1.LE_BC2CTUACXX_TAGCTT_L001_R1 MCL1.LE luminal lactate
12 MCL1.LF_BC2CTUACXX_CTTGTA_L001_R1 MCL1.LF luminal lactate
再讀入基因計數后的數據"GSE60450_Lactation-GenewiseCounts.txt"
> seqdata <- read.delim("data/GSE60450_Lactation-GenewiseCounts.txt",
+ stringsAsFactors = FALSE)
> head(seqdata)
EntrezGeneID Length MCL1.DG_BC2CTUACXX_ACTTGA_L002_R1 MCL1.DH_BC2CTUACXX_CAGATC_L002_R1
1 497097 3634 438 300
2 100503874 3259 1 0
3 100038431 1634 0 0
4 19888 9747 1 1
5 20671 3130 106 182
6 27395 4203 309 234
MCL1.DI_BC2CTUACXX_ACAGTG_L002_R1 MCL1.DJ_BC2CTUACXX_CGATGT_L002_R1
1 65 237
2 1 1
3 0 0
4 0 0
5 82 105
6 337 300
MCL1.DK_BC2CTUACXX_TTAGGC_L002_R1 MCL1.DL_BC2CTUACXX_ATCACG_L002_R1
1 354 287
2 0 4
3 0 0
4 0 0
5 43 82
6 290 270
MCL1.LA_BC2CTUACXX_GATCAG_L001_R1 MCL1.LB_BC2CTUACXX_TGACCA_L001_R1
1 0 0
2 0 0
3 0 0
4 10 3
5 16 25
6 560 464
MCL1.LC_BC2CTUACXX_GCCAAT_L001_R1 MCL1.LD_BC2CTUACXX_GGCTAC_L001_R1
1 0 0
2 0 0
3 0 0
4 10 2
5 18 8
6 489 328
MCL1.LE_BC2CTUACXX_TAGCTT_L001_R1 MCL1.LF_BC2CTUACXX_CTTGTA_L001_R1
1 0 0
2 0 0
3 0 0
4 0 0
5 3 10
6 307 342
> dim(seqdata) # 數據包含了27179行的基因信息,1列ID、1列基因長度和12列的樣本數據
[1] 27179 14
由于我們關注的是基因計數的信息,因此為了方便下游分析處理,我們移除seqdata
中前兩列,并將第一列的ID命名為行名
> countdata <- seqdata[,-(1:2)]
> rownames(countdata) <- seqdata[,1]
> head(countdata)
MCL1.DG_BC2CTUACXX_ACTTGA_L002_R1 MCL1.DH_BC2CTUACXX_CAGATC_L002_R1
497097 438 300
100503874 1 0
100038431 0 0
19888 1 1
20671 106 182
27395 309 234
MCL1.DI_BC2CTUACXX_ACAGTG_L002_R1 MCL1.DJ_BC2CTUACXX_CGATGT_L002_R1
497097 65 237
100503874 1 1
100038431 0 0
19888 0 0
20671 82 105
27395 337 300
MCL1.DK_BC2CTUACXX_TTAGGC_L002_R1 MCL1.DL_BC2CTUACXX_ATCACG_L002_R1
497097 354 287
100503874 0 4
100038431 0 0
19888 0 0
20671 43 82
27395 290 270
MCL1.LA_BC2CTUACXX_GATCAG_L001_R1 MCL1.LB_BC2CTUACXX_TGACCA_L001_R1
497097 0 0
100503874 0 0
100038431 0 0
19888 10 3
20671 16 25
27395 560 464
MCL1.LC_BC2CTUACXX_GCCAAT_L001_R1 MCL1.LD_BC2CTUACXX_GGCTAC_L001_R1
497097 0 0
100503874 0 0
100038431 0 0
19888 10 2
20671 18 8
27395 489 328
MCL1.LE_BC2CTUACXX_TAGCTT_L001_R1 MCL1.LF_BC2CTUACXX_CTTGTA_L001_R1
497097 0 0
100503874 0 0
100038431 0 0
19888 0 0
20671 3 10
27395 307 342
另外,我們注意到countdata
中的列名過于冗長,而樣本信息中的名字也只是前7位字母而已,因此我們使用substr()
函數截取列名
> sampleinfo$SampleName
[1] MCL1.DG MCL1.DH MCL1.DI MCL1.DJ MCL1.DK MCL1.DL MCL1.LA MCL1.LB MCL1.LC MCL1.LD MCL1.LE MCL1.LF
12 Levels: MCL1.DG MCL1.DH MCL1.DI MCL1.DJ MCL1.DK MCL1.DL MCL1.LA MCL1.LB MCL1.LC ... MCL1.LF
> colnames(countdata)
[1] "MCL1.DG_BC2CTUACXX_ACTTGA_L002_R1" "MCL1.DH_BC2CTUACXX_CAGATC_L002_R1"
[3] "MCL1.DI_BC2CTUACXX_ACAGTG_L002_R1" "MCL1.DJ_BC2CTUACXX_CGATGT_L002_R1"
[5] "MCL1.DK_BC2CTUACXX_TTAGGC_L002_R1" "MCL1.DL_BC2CTUACXX_ATCACG_L002_R1"
[7] "MCL1.LA_BC2CTUACXX_GATCAG_L001_R1" "MCL1.LB_BC2CTUACXX_TGACCA_L001_R1"
[9] "MCL1.LC_BC2CTUACXX_GCCAAT_L001_R1" "MCL1.LD_BC2CTUACXX_GGCTAC_L001_R1"
[11] "MCL1.LE_BC2CTUACXX_TAGCTT_L001_R1" "MCL1.LF_BC2CTUACXX_CTTGTA_L001_R1"
> colnames(countdata) <- substr(colnames(countdata), 1, 7)
> colnames(countdata)
[1] "MCL1.DG" "MCL1.DH" "MCL1.DI" "MCL1.DJ" "MCL1.DK" "MCL1.DL" "MCL1.LA" "MCL1.LB" "MCL1.LC"
[10] "MCL1.LD" "MCL1.LE" "MCL1.LF"
> table(colnames(countdata)==sampleinfo$SampleName) #使用table函數檢驗修改后的名字與原名字是否相同
TRUE
12
數據過濾
對于表達量過低的基因而言,其數據在差異分析中的可信度是較差的。因此,在本流程中我們先使用edgeR
的cpm()
函數,將counting后的數據轉換為CPM(counts-per-million),并取CPM在兩個重復樣本中均大于0.5的基因進入后續分析。
> myCPM <- cpm(countdata)
> head(myCPM)
MCL1.DG MCL1.DH MCL1.DI MCL1.DJ MCL1.DK MCL1.DL MCL1.LA
497097 18.85684388 13.77543859 2.69700983 10.45648006 16.442685 14.3389690 0.0000000
100503874 0.04305215 0.00000000 0.04149246 0.04412017 0.000000 0.1998463 0.0000000
100038431 0.00000000 0.00000000 0.00000000 0.00000000 0.000000 0.0000000 0.0000000
19888 0.04305215 0.04591813 0.00000000 0.00000000 0.000000 0.0000000 0.4903857
20671 4.56352843 8.35709941 3.40238163 4.63261775 1.997275 4.0968483 0.7846171
27395 13.30311589 10.74484210 13.98295863 13.23605071 13.469996 13.4896224 27.4615975
MCL1.LB MCL1.LC MCL1.LD MCL1.LE MCL1.LF
497097 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000
100503874 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000
100038431 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000
19888 0.1381969 0.4496078 0.09095771 0.0000000 0.0000000
20671 1.1516411 0.8092940 0.36383085 0.1213404 0.4055595
27395 21.3744588 21.9858214 14.91706476 12.4171715 13.8701357
> thresh <- myCPM > 0.5 # This produces a logical matrix with TRUEs and FALSEs
> head(thresh)
MCL1.DG MCL1.DH MCL1.DI MCL1.DJ MCL1.DK MCL1.DL MCL1.LA MCL1.LB MCL1.LC MCL1.LD MCL1.LE
497097 TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
100503874 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
100038431 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
19888 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
20671 TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE
27395 TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
MCL1.LF
497097 FALSE
100503874 FALSE
100038431 FALSE
19888 FALSE
20671 FALSE
27395 TRUE
用table()
函數每個基因(每行)在幾個樣本中是符合條件的
> table(rowSums(thresh))
0 1 2 3 4 5 6 7 8 9 10 11 12
10857 518 544 307 346 307 652 323 547 343 579 423 11433
#可以發現有11433個基因在12個樣本中CPM均大于0.5
隨后,保留至少在兩個樣本符合條件的基因。
> keep <- rowSums(thresh) >= 2
# Subset the rows of countdata to keep the more highly expressed genes
> counts.keep <- countdata[keep,]
> summary(keep)
Mode FALSE TRUE
logical 11375 15804
> dim(counts.keep)
[1] 15804 12
我們保留了15804個相對表達可信的基因。至于,counts threshold的選擇可以參考原文:
As a general rule, a good threshold can be chosen by identifying the CPM that corresponds to a count of 10, which in this case is about 0.5. You should filter with CPMs rather than filtering on the counts directly, as the latter does not account for differences in library sizes between samples.
創建表達矩陣
接著我們使用edgeR
的DGEList
函數來創建表達矩陣
# 將counts 轉換為 DGEList 對象
> dgeObj <- DGEList(counts.keep)
> dgeObj
An object of class "DGEList"
$counts
MCL1.DG MCL1.DH MCL1.DI MCL1.DJ MCL1.DK MCL1.DL MCL1.LA MCL1.LB MCL1.LC MCL1.LD MCL1.LE
497097 438 300 65 237 354 287 0 0 0 0 0
20671 106 182 82 105 43 82 16 25 18 8 3
27395 309 234 337 300 290 270 560 464 489 328 307
18777 652 515 948 935 928 791 826 862 668 646 544
21399 1604 1495 1721 1317 1159 1066 1334 1258 1068 926 508
MCL1.LF
497097 0
20671 10
27395 342
18777 581
21399 500
15799 more rows ...
$samples
group lib.size norm.factors
MCL1.DG 1 23218026 1
MCL1.DH 1 21768136 1
MCL1.DI 1 24091588 1
MCL1.DJ 1 22656713 1
MCL1.DK 1 21522033 1
7 more rows ...
# 看看 dgeObj 中保存了什么信息
> names(dgeObj)
[1] "counts" "samples"
根據CellType
和小鼠的Status
,我們可以創建分組信息。
group <- paste(sampleinfo$CellType,sampleinfo$Status,sep=".")
group <- factor(group)
質量控制
由于我們的數據并不是正態分布的,為了下游分析的進行,使用cpm
函數對我們的數據取log2處理
> logcounts <- cpm(dgeObj,log=TRUE)
> # 使用箱線圖檢查數據的分布
> boxplot(logcounts, xlab="", ylab="Log2 counts per million",las=2)
> # 使用藍色的橫線標出logCPM的中值
> abline(h=median(logcounts),col="blue")
> title("Boxplots of logCPMs (unnormalised)")
從箱線圖中可以看出樣本數據分布雖然存在差異,但這種差異程度是我們還能接受的程度。
主成分分析
主成分分析在RNA-seq分析中是一個較為重要的步驟,其指出了樣本數據中造成主要差異的因子是什么。一般而言,我們當然希望數據中最大的差異是由于處理與否引起的
> plotMDS(dgeObj)
可以看到兩個生物學重復樣本都能較好的聚在一起
另外,我們也可以運用各種參數使主成分分析的可視化更加符合我們的要求,例如根據細胞類型或小鼠狀態標注顏色:
總而言之,利用主成分分析,可以觀察出引起樣本變化的主要因子。
標準化處理
此處我們采用了The trimmed mean of M-values normalization method (TMM) 去校正文庫之間的組成偏好。
> dgeObj <- calcNormFactors(dgeObj)
> dgeObj$samples
group lib.size norm.factors
MCL1.DG 1 23218026 1.2368993
MCL1.DH 1 21768136 1.2139485
MCL1.DI 1 24091588 1.1255640
MCL1.DJ 1 22656713 1.0698261
MCL1.DK 1 21522033 1.0359212
MCL1.DL 1 20008326 1.0872153
MCL1.LA 1 20384562 1.3684449
MCL1.LB 1 21698793 1.3653200
MCL1.LC 1 22235847 1.0047431
MCL1.LD 1 21982745 0.9232822
MCL1.LE 1 24719697 0.5291015
MCL1.LF 1 24652963 0.5354877
# 此步在‘samples’信息中直接添加了校正因子。
我們可以單獨抽一個樣本出來看看校正前后的數據分布情況
可以看出校正后,樣本表達更加集中于0。
至此,數據的預處理也已經完結,文中的可視化部分僅是為了輔助我們查看數據與方便我們的學習,在實戰中,部分數據預處理時的可視化也可省略。
#保存預處理分析結果
save(group,dgeObj,sampleinfo,file="~preprocessing.Rdata")
暫完。