刘小泽写于18.11.12 半年之前我和花花成立了“生信星球”,转眼过得好快,我们每天都能和爱生信的小伙伴们交流,还办了9期培训,灰常开心
终于可以回归写推送了,连续半个月奋战两篇文章,好累,真不如学知识写推送轻松。时隔半月,有点忘记,但是重新学一次,又能学到新知识,还是不错的
数据准备:
数据地址在 http://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE63310&format=file
files <- c("GSE63310_RAW/GSM1545535_10_6_5_11.txt",
"GSE63310_RAW/GSM1545536_9_6_5_11.txt",
"GSE63310_RAW/GSM1545538_purep53.txt",
"GSE63310_RAW/GSM1545539_JMS8-2.txt",
"GSE63310_RAW/GSM1545540_JMS8-3.txt",
"GSE63310_RAW/GSM1545541_JMS8-4.txt",
"GSE63310_RAW/GSM1545542_JMS8-5.txt",
"GSE63310_RAW/GSM1545544_JMS9-P7c.txt",
"GSE63310_RAW/GSM1545545_JMS9-P8c.txt")
# 读取第一个文件,看下前5行
read.delim(files[1], nrow=5)
#> EntrezID GeneLength Count
#> 1 497097 3634 1
#> 2 100503874 3259 0
#> 3 100038431 1634 0
#> 4 19888 9747 0
#> 5 20671 3130 1
批量读取+转换:
- 方法一:一次性读取全部,用readDGE函数
suppressMessages(library(limma))
suppressMessages(library(edgeR))
#输入基因名和count的列
x <- readDGE(files, columns = c(1,3))
# x包含了两部分信息:sample和count
class(x)
#> [1] "DGEList"
#> attr(,"package")
#> [1] "edgeR"
dim(x)
#> [1] 27179 9
- 方法二:分别读取,再用DGEList函数转换全部
转换下样本信息
存储样本与cell type(basal,LP,ML)/ genotype(wild-type, knockout)/ phenotype(status, sex, age)/
sample treatment(drug, control)/ batch effect (date, lane)
# 输入start、stop 位置
samplenames <- substring(colnames(x), 25, nchar(colnames(x)))
samplenames
#> [1] "10_6_5_11" "9_6_5_11" "purep53" "JMS8-2" "JMS8-3" "JMS8-4"
#> [7] "JMS8-5" "JMS9-P7c" "JMS9-P8c"
#去掉列名的重复项,重新命名
colnames(x) <- samplenames
#添加分组信息,比如细胞类型
group <- as.factor(c("LP", "ML", "Basal", "Basal", "ML", "LP",
"Basal", "ML", "LP"))
x$samples$group <- group
# 添加lane信息
lane <- as.factor(rep(c("L004","L006","L008"), c(3,4,2)))
x$samples$lane <- lane
x$samples
#> files group lib.size
#> 10_6_5_11 GSE63310_RAW/GSM1545535_10_6_5_11.txt LP 32863052
#> 9_6_5_11 GSE63310_RAW/GSM1545536_9_6_5_11.txt ML 35335491
#> purep53 GSE63310_RAW/GSM1545538_purep53.txt Basal 57160817
#> JMS8-2 GSE63310_RAW/GSM1545539_JMS8-2.txt Basal 51368625
#> JMS8-3 GSE63310_RAW/GSM1545540_JMS8-3.txt ML 75795034
#> JMS8-4 GSE63310_RAW/GSM1545541_JMS8-4.txt LP 60517657
#> JMS8-5 GSE63310_RAW/GSM1545542_JMS8-5.txt Basal 55086324
#> JMS9-P7c GSE63310_RAW/GSM1545544_JMS9-P7c.txt ML 21311068
#> JMS9-P8c GSE63310_RAW/GSM1545545_JMS9-P8c.txt LP 19958838
#> norm.factors lane
#> 10_6_5_11 1 L004
#> 9_6_5_11 1 L004
#> purep53 1 L004
#> JMS8-2 1 L006
#> JMS8-3 1 L006
#> JMS8-4 1 L006
#> JMS8-5 1 L006
#> JMS9-P7c 1 L008
#> JMS9-P8c 1 L008
增加一个基因注释信息
基因注释涉及到ID转换,方法一:clusterProfiler
# 数据集中使用的是EntrezID
suppressMessages(library(Mus.musculus, supr))
suppressMessages(library(clusterProfiler))
geneid <- rownames(x)
# 但是好像没有染色体信息
genes <- bitr(geneid, OrgDb = Mus.musculus, fromType = "ENTREZID", toType = "SYMBOL" )
#> 'select()' returned 1:1 mapping between keys and columns
#> Warning in bitr(geneid, OrgDb = Mus.musculus, fromType = "ENTREZID", toType
#> = "SYMBOL"): 3.52% of input gene IDs are fail to map...
head(genes)
#> ENTREZID SYMBOL
#> 1 497097 Xkr4
#> 2 100503874 Gm19938
#> 3 100038431 Gm10568
#> 4 19888 Rp1
#> 5 20671 Sox17
#> 6 27395 Mrpl15
方法二:biomaRt(属于Ensembl)
suppressMessages(library(Mus.musculus))
geneid <- rownames(x)
# 增加Symbol名称和染色体信息
genes <- select(Mus.musculus, keys=geneid,
columns=c("SYMBOL", "TXCHROM"),keytype="ENTREZID")
#> 'select()' returned 1:many mapping between keys and columns
head(genes)
#> ENTREZID SYMBOL TXCHROM
#> 1 497097 Xkr4 chr1
#> 2 100503874 Gm19938
#> 3 100038431 Gm10568
#> 4 19888 Rp1 chr1
#> 5 20671 Sox17 chr1
#> 6 27395 Mrpl15 chr1
需要注意的是:ID转换中可能出现多个同样的Entrez ID,比如某个基因出现在不同染色体,
就会在不同位置出现同一个ID。这种情况需要先检查下
dup <- genes$ENTREZID[duplicated(genes$ENTREZID)]
genes[genes$ENTREZID %in% dup,][1:10,]
#> ENTREZID SYMBOL TXCHROM
#> 5360 100316809 Mir1906-1 chr12
#> 5361 100316809 Mir1906-1 chrX
#> 9563 12228 Btg3 chr16
#> 9564 12228 Btg3 chr17
#> 11350 433182 Eno1b chr4
#> 11351 433182 Eno1b chr18
#> 11543 100217457 Snord58b chr14
#> 11544 100217457 Snord58b chr18
#> 13226 735274 Mir684-1 chr2
#> 13227 735274 Mir684-1 chr5
# 出现多次的基因只统计出现第一次的,得到的是一个位置向量,即位于多少行
mat <- match(geneid, genes$ENTREZID)
genes <- genes[mat,]
#再次验证
genes[genes$ENTREZID %in% dup,][1:5,]
#> ENTREZID SYMBOL TXCHROM
#> 5360 100316809 Mir1906-1 chr12
#> 9563 12228 Btg3 chr16
#> 11350 433182 Eno1b chr4
#> 11543 100217457 Snord58b chr14
#> 13226 735274 Mir684-1 chr2
x$genes <- genes
数据预处理
raw-count标准化
不管是差异分析还是其他用到count值的相关分析,如WGCNA,都基本不直接用原始count值,原因是这个数值容易受到外界的干扰。一般来讲,影响raw count的有两个因素:
- 测序深度
这个比较好理解,深度越大,得到的reads数就越多,当然count差异就越大 - RNA的组织/时间特异性
不同时期基因表达量是不一样的,很难保证两组样本中RNA是同一时期的
因此需要先对测序深度做一个转换,也就是所谓的“标准化”,减少外在影响
比较流行的标准化方式有:counts per million (CPM), log2-counts per million (log-CPM), reads per kilobase of transcript per million (RPKM), fragments per kilobase of transcript per million (FPKM); 其中CPM与logCPM没有考虑基因长度(要求略低),直接对矩阵进行转换。
# edegR中的CPM与log-CPM
cpm <- cpm(x)
lcpm <- cpm(x, log=TRUE)
# 如果用edegR中的rpkm,需要提供gene length信息
# 用TMM方法
x <- calcNormFactors(x, method = "TMM")
去掉不表达基因
所有的表达量数据都存在表达与非表达基因,拿到表达量数据先看下非表达基因占多少。
#这里有9组样本,因此可以统计9组中表达量都为0的基因
table(rowSums(x$counts==0)==9)
#>
#> FALSE TRUE
#> 22026 5153
基因必须至少在一组处理中或者任意3个样本中有表达量才能被保留。当然,非表达的需要去除,以减少后续分析压力
keep.exprs <- rowSums(cpm>1)>=3
# 行代表基因,列代表样本;这里选择全部样本
x <- x[keep.exprs,, keep.lib.sizes=FALSE]
dim(x)
#> [1] 14165 9
基因表达分布的标准化
使用trimmed mean of M-values(TMM),也就是目前认为最准确的标准化方式
为了解决样本间由于组织、时间特异性而引起的误差,TMM首先从所有样本中挑选一个样本作为参照,当对其他样本进行归一化时,就只考虑参照样本和待归一化的样本间共有的RNA
一般来讲,mRNA数据使用TMM就可以【但是单细胞测序及全转录组一般是不行的,因为它们的差异表达比较多】
x <- calcNormFactors(x, method = "TMM")
非监督式样本聚类
在正式开始差异分析之前,可以使用非监督式学习处理一下:
什么是非监督?
将数据按相似度聚类(clustering)成不同的分组或者用降维的方式,保留基本的数据结构,同时压缩数据。包含了K 均值聚类、层次聚类、主成分分析(PCA)和奇异值分解(SVD)
- K均值聚类
目标是将数据点分组,让不同聚类中的数据点不同,同一聚类中的数据点类似;使用这种方法希望结果的数据点被聚类为 K 组。K 大分组就小,就有更多粒度;K 小时,分组就大,粒度更少(可以理解成零散程度)
比如一个领导者一般气场比较大,周围会有一些像被吸附了的帮手;如果这样的领导只有一个,那么群众们都围着他转;如果英雄辈出,那么各自会形成一个小中心圈,分别集结一小帮人
- 层次聚类
我们日常说的“做个聚类树”,就是指的这个。目标是构建一个树状层次,让相似的离得更近。它是这么做的:
- 先从 第N 个聚类开始,每个数据点一个聚类;
- 将彼此靠得最近的两个聚类融合为一个,那么现在有 N-1 个聚类
- 重新计算这些聚类之间的距离(方法很多,其中一种“average-linkage clustering”将两个聚类之间的距离看作是聚类中元素所有距离的平均值)
- 重复2-3,再选择聚类的数量(就是要将整体分成几部分),画条水平线
- 降维
两种办法:主成分分析和奇异值分解
- 主成分分析:假设我们现在有1w维空间的数据,然后怎么样才能知道数据间的联系呢,哪些比较强势?我们可以将原始数据映射到另一个更紧凑的空间中(比如只有100维),减少了复杂度(也就是维度),还保证了原始结构不变(即方差不变)
- 奇异值分解(SVD)
如果说我们的数据是一个a X b的大型矩阵,通过SVD将大矩阵拆分成几个小矩阵的乘积,从中找一个对角矩阵叫做“奇异值”,之所以奇异,是因为它可以用于压缩原来的矩阵。如果我们丢弃奇异值中最小的 20% 以及其他矩阵中相关的列,我们就可以节省大量空间,并能很好地表示原来的矩阵
这里怎么做?
这里采用limma的multidimensional scaling (MDS) plot即“多维尺度变换”,这个会用非监督式显示样本之间的异同,在正式差异分析前帮助我们判断潜在的差异样本。它的结果会将所有样本划分成几个维度,第一维度的样本代表了最大的差异,维度越往后,相似度就越大,差异分析的结果越不理想
当实验设计中存在多个实验因子时,每个实验因子都要进行这几个维度的检测。这样做,还能看出哪个实验因子对差异表达的贡献最大,基本没什么影响的因子,就无法参与后续分析。
suppressMessages(library(RColorBrewer))
# 这里就用最简单的CPM值
lcpm <- cpm(x, log=TRUE)
par(mfrow=c(1,2))
col.group <- group
levels(col.group) <- brewer.pal(nlevels(col.group), "Set1")
col.group <- as.character(col.group)
col.lane <- lane
levels(col.lane) <- brewer.pal(nlevels(col.lane), "Set2")
col.lane <- as.character(col.lane)
# 1、2维度
plotMDS(lcpm, labels=group, col=col.group)
title(main="A. Sample groups")
# 3、4维度
plotMDS(lcpm, labels=lane, col=col.lane, dim=c(3,4))
title(main="B. Sequencing lanes")
欢迎关注我们的公众号~_~
我们是两个农转生信的小硕,打造生信星球,想让它成为一个不拽术语、通俗易懂的生信知识平台。需要帮助或提出意见请后台留言或发送邮件到[email protected]