计算FPKM、TPM以及FPKM to TPM

之前已经简单学习了测序raw counts的FPKM标准化,接下来就一个实际例子,利用R语言手动计算原始数据的FPKM值;并与官方给出的FPKM文件对比验证。

1、准备数据

nm_count <- read.csv("GSE81861_CRC_NM_epithelial_cells_COUNT.csv/GSE81861_CRC_NM_epithelial_cells_COUNT.csv")
row.names(nm_count) <- nm_count[,1]
nm_count <- nm_count[,-1]
dim(nm_count)
nm_count <- as.matrix(nm_count)
#原始的基因名太长,我们仅留ensemble ID
rownames(nm_count) <- sapply(strsplit(rownames(nm_count),"_"),"[",3)
rownames(nm_count) <- sapply(strsplit(rownames(nm_count),"[.]"),"[",1)
#注意R好像不能直接以点号为分隔符,需要在两边加上英文中括号,才能识别
dim(nm_count) #57241个基因
nm_count[1:4,1:4]
1-2

计算FPKM的三要素:原始counts矩阵,样本总reads数,基因长度。其中样本总reads数直接使用colSums()函数即可。基因长度是需要加载特定的生物dataset package进行计算。因为基因长度有不同的定义标准,所以也有不同的计算思路,有意思的是结果可能也有很大的不同。

2、基因长度

2.1 TxDb.Hsapiens.UCSC.hg19.knownGene 包

if (!requireNamespace("BiocManager", quietly = TRUE))
  install.packages("BiocManager")
if (!requireNamespace("TxDb.Hsapiens.UCSC.hg19.knownGene", quietly = TRUE))
  BiocManager::install("TxDb.Hsapiens.UCSC.hg19.knownGene")
txdb <- TxDb.Hsapiens.UCSC.hg19.knownGene
exon_txdb=exons(txdb)
genes_txdb=genes(txdb)
##The GRanges class is a container for the genomic locations and their associated annotations.
定义1:非冗余外显子长度之和
o <- findOverlaps(exon_txdb,genes_txdb)
o
genes_txdb[581] #chr1 11874-14409
exon_txdb[1] #chr1 11874-12227
exon_txdb[2] #chr1 12595-12721
exon_txdb[5] #chr1 13221-14409

如上可知道,581的gene包含了ID1,2,3,4,5的exon。所以基因长度之和就是求所有外显子的长度和。但注意要考虑外显子间可能存在交叉,所以不能直接把长度相加。
思路:假设外显子1序列3-10,2序列3-5。计算总长度即uniq所有的具体的数即可。3,4,5,6,7,8,9,10,3,4,5 unique下还是8个


2-1
  t1=exon_txdb[queryHits(o)]
  t2=genes_txdb[subjectHits(o)]
  t1=as.data.frame(t1)
  t1$geneid=mcols(t2)[,1]
# 得到exon_id与geneid的对应关系
2-2
g_l.1 <- lapply(split(t1,t1$geneid),function(x){
    #按gene id拆分表格
    head(x)
    tmp=apply(x,1,function(y){
      y[2]:y[3]
    }) #根据每一个gene所有exon的区间,生成区间内的整数,返回的为list。
    length(unique(unlist(tmp)))
    #计算共有多少种整数,即为最终的非冗余exon长度之和
  })
head(g_l.1) #为一个list
g_l.1=data.frame(gene_id=names(g_l.1),length=as.numeric(g_l.1))
dim(g_l.1)
head(g_l.1)
#为基因ID增添ENSEMBLE ID
library(org.Hs.eg.db)
s2g=toTable(org.Hs.egENSEMBL)
head(s2g)
g_l.1=merge(g_l.1,s2g,by='gene_id') 
#把g_l,s2g两个数据框以'gene_id'为连接进行拼接
head(g_l.1)
2-3
定义2:最长转录本
t_l=transcriptLengths(txdb)
head(t_l)
t_l=na.omit(t_l)
#先按基因ID,再按转录本长度从大到小排序
t_l=t_l[order(t_l$gene_id,t_l$tx_len,decreasing = T),]
head(t_l);dim(t_l)
2-4
#根据gene_id去重,选择第一个,也就是最长的那个
t_l=t_l[!duplicated(t_l$gene_id),]
head(t_l);dim(t_l)
g_l=t_l[,c(3,5)]
g_l.2=merge(g_l.2,s2g,by='gene_id') 
head(g_l.2)
2-5

2.2 biomaRt 包

https://bioconductor.org/packages/release/bioc/vignettes/biomaRt/inst/doc/biomaRt.html

if (!requireNamespace("biomaRt", quietly = TRUE))
  BiocManager::install("biomaRt")
ensembl <- useMart("ensembl") #connect to a specified BioMart database
ensembl = useDataset("hsapiens_gene_ensembl",mart=ensembl)
#use the hsapiens(人类) dataset.或者直接如下设置
#ensembl = useMart("ensembl",dataset="hsapiens_gene_ensembl")
test <- getBM(attributes=c('ensembl_gene_id', 'start_position',
                           'end_position','ensembl_transcript_id',
                           'transcript_length'),
              mart = ensembl)

如图,分别找到基因ensemble ID、基因起止位点、转录本ID,转录本长度


2-6
test <- test[order(test$ensembl_gene_id,test$transcript_length,
                   decreasing = T),]
g_l.3 <- test[!duplicated(test$ensembl_gene_id),]
g_l.3 <- g_l[,c(1,5)]
head(g_l.3)

比较上述三种方法计算的基因长度

dim(g_l.1);dim(g_l.2);dim(g_l.3)
#[1] 23729     3
#[1] 25159     3
#[1] 67130     2
#TxDb.Hsapiens.UCSC.hg19.knownGene包的两种方法结果都只有2w+
#biomaRt包的结果有6w+的基因数量
g_l.1 <- g_l.1[,-1]
colnames(g_l.1) <- c("g_l.1","ensembl_id")
head(g_l.1)
g_l.2 <- g_l.2[,-1]
colnames(g_l.2) <- c("g_l.2","ensembl_id")
head(g_l.2)
colnames(g_l.3) <- c("ensembl_id","g_l.3")
head(g_l.3)

g_l_all <- merge(g_l.1, g_l.2, by="ensembl_id")
g_l_all <- merge(g_l_all, g_l.3, by="ensembl_id")
head(g_l_all,10)
summary(g_l_all) 
  • 如下结果,三种方法总体分布大致相同,但是存在部分基因长度差别还是挺大的。此外数量方面两个包得到的结果也是有较大差异。最终计算FPKM值只有第三中方法比较理想(与官方给的FPKM文件相近),可能与数量差异存在一定关联。


    2-7

    2-8

3、批量计算FPKM

  • 如上所说,我们选取第三种方法得出的基因长度进行演示
g_l <- g_l.3
dim(nm_count) #57241
dim(g_l) #67130
#选取二者交集基因
ng=intersect(rownames(nm_count),g_l$ensembl_gene_id) 
length(ng) #最后保留50813个
lengths=g_l[match(ng,g_l$ensembl_gene_id),2]
names(lengths) <- g_l[match(ng,g_l$ensembl_gene_id),1]
head(lengths)
#最终得到排好序的gene length数值向量,并进行命名
#ENSG00000000003 ENSG00000000005 ENSG00000000419 
#           3796            1205            1161 
#ENSG00000000457 ENSG00000000460 ENSG00000000938 
#           6308            4355            2637
#根据最终选取的counts矩阵计算每个样本的文库大小
nm_count <- nm_count[names(lengths),]
total_count <- colSums(nm_count)
head(total_count)
#RHC4104__stemTA__.2749FE RHC6087__stemTA__.2749FE RHL2880__stemTA__.2749FE 
#                593518.0                 702180.9                 103072.8 
#RHC5949__stemTA__.2749FE RHC4975__stemTA__.2749FE RHC4099__stemTA__.2749FE 
#               1636163.9                 964027.4                 991415.4
  • 最终得到nm_countlengths、·total_count,根据公式批量计算即可。
    3-1
nm_fpkm <- t(do.call( rbind,
                   lapply(1:length(total_count),
                          function(i){
                            10^9*nm_count[,i]/lengths/total_count[i]
                            #lengths向量自动遍历
                          }) ))
nm_fpkm[1:4,1:4]
#最终得到的fpkm矩阵
3-2
  • 比较下官方给的fpkm矩阵
nm_fpkm_paper <- read.csv("GSE81861_CRC_NM_epithelial_cells_FPKM.csv/GSE81861_CRC_NM_epithelial_cells_FPKM.csv")
row.names(nm_fpkm_paper) <- nm_fpkm_paper[,1]
nm_fpkm_paper <- nm_fpkm_paper[,-1]
dim(nm_fpkm_paper)
nm_fpkm_paper <- as.matrix(nm_fpkm_paper)
rownames(nm_fpkm_paper) <- sapply(strsplit(rownames(nm_fpkm_paper),"_"),"[",3)
rownames(nm_fpkm_paper) <- sapply(strsplit(rownames(nm_fpkm_paper),"[.]"),"[",1)
colnames(nm_fpkm_paper) <- c(1:160)
  • 如下图,总体来说还是蛮接近的,也验证了计算公式。


    3-3
# count to tpm
nm_tkm <- t(do.call(rbind,
                     lapply(1:nrow(ensembl_matrix),
                            function(i){
                              print(i)
                              10^9*ensembl_matrix[i,]/gene_len[i]/total_count
                              #lengths向量自动遍历
                            }) ))
nm_tkm[1:4,1:4]

2021-08-22

counts = ensembl_matrix
featureLength = gene_len #对应counts rownames的基因名长度
#meanFragmentLength = apply(counts, 2, mean)
counts_to_tpm <- function(counts, featureLength, meanFragmentLength) {
  
  # Ensure valid arguments.
  stopifnot(length(featureLength) == nrow(counts))
  #stopifnot(length(meanFragmentLength) == ncol(counts))
  
  # Compute effective lengths of features in each library.
  #effLen <- do.call(cbind, lapply(1:ncol(counts), function(i) {
  #  featureLength - meanFragmentLength[i] + 1
  #}))
  
  # Exclude genes with length less than the mean fragment length.
  #idx <- apply(effLen, 1, function(x) min(x) > 1)
  #counts <- counts[idx,]
  #effLen <- effLen[idx,]
  #featureLength <- featureLength[idx]
  
  # Process one column at a time.
  tpm <- do.call(cbind, lapply(1:ncol(counts), function(i) {
    rate = log(counts[,i]) - log(featureLength ) #每一列(单个样本的count)/对应基因的长度
    denom = log(sum(exp(rate))) #上一步更新后的新的细胞文库大小
    exp(rate - denom + log(1e6)) # 按照细胞文库归一化
  }))
  
  # fpkm <- do.call(cbind, lapply(1:ncol(counts), function(i) {
  #   N <- sum(counts[,i])
  #   exp( log(counts[,i]) + log(1e9) - log(featureLength) - log(N))
  # }))
  
  # fpkmToTpm <- function(fpkm) {
  #   exp(log(fpkm) - log(sum(fpkm)) + log(1e6))
  # }
  # tpm2 = apply(fpkm, 2, fpkmToTpm)
  
  colnames(tpm) <- colnames(counts)
  rownames(tpm) <- rownames(counts)
  return(tpm)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343