第 12 章 counts 矩阵标准化
counts 数据的标准化方式有很多种,常见的有RPKM、FPKM 、TPM和CPM这几种方法,这些归一化方式除了CPM外,都是基于基因长度的归一化方法,CPM是基于样本的归一化方法。
有多种方法可以提取出基因的有效长度(或非冗余总外显子长度、总转录本长度)。 最简单的方法就是从featureCounts的输出文件中提取出基因的长度信息,也可以从GTF文件中提取出基因的长度信息。
12.1 featureCounts
从 featureCounts 软件生成的 counts 矩阵可以直接提取出来,然后进行标准化处理。
# 加载包
bio.p <- c("tidyverse", "data.table")
status = suppressPackageStartupMessages(
lapply(bio.p, library, character.only = TRUE)
)读取 counts 数据
# 读取数据
feature <- fread("DATA/featureCounts.txt") %>% as.data.frame()
counts <- feature %>%
column_to_rownames(var = "Geneid")
counts <- counts[, 6:ncol(counts)]提取基因的有效长度信息
# 提取 Geneid 和 Length
geneid_efflen <- feature %>%
select(Geneid, Length) %>%
rename(geneid = Geneid, efflen = Length)
# 获取对应的 efflen
efflen <- geneid_efflen$efflen[match(rownames(counts), geneid_efflen$geneid)]12.1.1 TPM
# 定义转换为 TPM 的函数
counts2TPM <- function(count, efflength) {
RPK <- count / (efflength / 1000) # 每千碱基 reads (reads per kilobase)
PMSC_rpk <- sum(RPK) / 1e6 # RPK 的每百万缩放因子
RPK / PMSC_rpk
}
# 计算 TPM
TPM <- apply(counts, 2, counts2TPM, efflength = efflen) %>%
as.data.frame(check.names = FALSE) %>%
`rownames<-`(rownames(counts)) %>%
setNames(paste0(colnames(counts), "_TPM"))12.1.2 FPKM/RPKM
RPKM是针对单端测序的RNA-seq而言的,其中每个reads对应于一个已测序的单个片段。 FPKM用于双端测序的RNA-seq。使用双端测序RNA-seq,两个reads可以对应一个片段(Fragment)。 RPKM和FPKM之间的唯一区别是FPKM考虑到两次reads可以映射到一个片段(因此它不会对该片段进行两次计数)。 即单端测序:reads=fragments,双端测序:2 * reads≈fragments 而经过上游处理,双端测序两个reads可以对应一个片段的过程已经完成,最后得到的counts就已经相当于是片段fragments了, 因此下游分析由counts计算RPKM、 FPKM这两者的公式完全一致。
# 定义转换为 FPKM/RPKM 的函数
counts2FPKM <- function(count=count, efflength=efflen){
PMSC_counts <- sum(count)/1e6 # counts的每百万缩放因子 (“per million” scaling factor) 深度标准化
FPM <- count/PMSC_counts # 每百万reads/Fragments (Reads/Fragments Per Million) 长度标准化
FPM/(efflength/1000)
}
# 计算 FPKM/RPKM
FPKM <- apply(counts, 2, counts2FPKM, efflength = efflen) %>%
as.data.frame(check.names = FALSE) %>%
`rownames<-`(rownames(counts)) %>%
setNames(paste0(colnames(counts), "_FPKM"))12.1.4 CPM
# 定义转换为 CPM 的函数
counts2CPM <- function(count) {
# 计算每百万缩放因子
PMSC_counts <- sum(count) / 1e6 # counts 的每百万缩放因子 (“per million” scaling factor)
# 计算 CPM
CPM <- count / PMSC_counts # 每百万 reads (Counts Per Million)
}
# 计算 CPM
CPM <- apply(counts, 2, counts2CPM) %>%
as.data.frame(check.names = FALSE) %>%
`rownames<-`(rownames(counts)) %>%
setNames(paste0(colnames(counts), "_CPM"))12.2 拓展
从 GTF 文件中提取基因的长度信息。
# 加载必要的库
library(parallel) # 并行计算
library(GenomicFeatures) # 处理基因组注释
# 创建并行计算集群,使用 3/4 的可用核心
cl <- makeCluster(ceiling(0.75 * detectCores()))
# 从 GTF 文件创建 TxDb 对象
txdb <- makeTxDbFromGFF("gencode.vM25.chr_patch_hapl_scaff.annotation.gtf.gz", format = "gtf")
# 提取基因的外显子信息
exons_gene <- exonsBy(txdb, by = "gene")
# 计算每个基因的总外显子长度
exons_gene_lens <- parLapply(cl, exons_gene, function(x) {
sum(width(reduce(x)))
})
# 转换为数据框
geneid_efflen <- data.frame(
geneid = names(exons_gene_lens),
efflen = as.numeric(exons_gene_lens)
)
# 打印前 10 个基因的有效长度
print(head(geneid_efflen, 10))
# 停止并行计算集群
stopCluster(cl)# 加载必要的库
library(parallel) # 并行计算
library(rtracklayer) # 导入 GTF 文件
# 创建并行计算集群,使用 3/4 的可用核心
cl <- makeCluster(ceiling(0.75 * detectCores()))
# 导入 GTF 文件并转换为数据框
gtf <- as.data.frame(rtracklayer::import("gencode.vM25.chr_patch_hapl_scaff.annotation.gtf.gz"))
# 查看不同类型注释的频数
print(table(gtf$type))
# 提取外显子信息
exon <- gtf[gtf$type == "exon", c("start", "end", "gene_id")]
# 按基因 ID 分割外显子信息
exon_bygeneid <- split(exon, exon$gene_id)
# 计算每个基因的有效外显子长度
efflen <- parLapply(cl, exon_bygeneid, function(x) {
# 对每个外显子生成位置序列并去重
unique_positions <- unique(unlist(apply(x, 1, function(y) seq(y[1], y[2]))))
length(unique_positions)
})
# 转换为数据框
geneid_efflen <- data.frame(
geneid = names(efflen),
efflen = as.numeric(efflen)
)
# 打印前 10 个基因的有效长度
print(head(geneid_efflen, 10))
# 停止并行计算集群
stopCluster(cl)