量化免疫浸润时CIBERSORT的注意事项

CIBERSORT的注意事项

CIBERSORT的使用,我们举了几个例子,但是还有一些疑问,只要有疑问在,就感觉心里不踏实。
比如:
1.CIBERSORT需要什么样的数据?
是芯片数据还是测序数据,是什么格式的,需要取log么?
2.CIBERSORT输出的数据是表示什么?
在进过一系列运算后我们会得到一个矩阵

image

行是样本,总共25列,前面22列代表22个免疫细胞。那么表格内的数值是什么意思?
3.上面的表格中,最后还有三列

image

这里的p值,相关性系数怎么算出来的?RMSE是什么啊?

带着这些问题,我们就探索一下吧。

CIBERSORT究竟在干什么?

我们能够对CIBERSORT进行探索,在于他除了有网页版本之外,还提供了一个脚本。
这个脚本在登陆官网后可以申请获得。
我们先来展示一下,在R语言里面如何运行CIBERSORT
你需要三个东西:
第一,这个脚本。
第二,基因特征文本
第三,表达量矩阵。

前两个都可以在官网下载。
最后一个是我们自己的数据,行是基因,列是样本。

image

然后运行就可以了

  1. ## 加载函数
  2. source("CIBERSORT/CIBERSORT.R")
  3. ## 指定基因特征集
  4. sig_matrix <- "CIBERSORT/LM22.txt"
  5. ## 指定表达矩阵
  6. mixture_file = 'exprMat.txt'
  7. ## 运行
  8. res_cibersort <- CIBERSORT(sig_matrix, mixture_file, perm=10, QN=TRUE)

其中nperm给的是置换的次数,QN如果是芯片设置为T,如果是测序就设置为F,测序数据最好是TPM
因为我对这个小函数进行了一点改造,所以会展示计算过程,就是缓解一些等待的焦虑

image

最后就得到了我们之前说的结果。就这么简单。
当我们运行第一句的时候

  1. source("CIBERSORT/CIBERSORT.R")

环境中出现了三个函数

image

CIBERSORT是主函数,CoreAlg用来进行SVM支持向量机分析,doPerm置换检验,产生背景r值。

我们可以打开CIBERSORT函数看看

  1. CIBERSORT <- function(sig_matrix, mixture_file, perm=0, QN=TRUE){
  2. print("Modified by Guozi")
  3. print(paste0("Started at:",Sys.time()))
  4. #read in data
  5. X <- read.table(sig_matrix,header=T,sep="t",row.names=1,check.names=F)
  6. Y <- read.table(mixture_file, header=T, sep="t", check.names=F)
  7. Y <- Y[!duplicated(Y[,1]),]
  8. rownames(Y)<-Y[,1]
  9. Y<-Y[,-1]
  10. X <- data.matrix(X)
  11. Y <- data.matrix(Y)

order

X <- X[order(rownames(X)),]
Y <- Y[order(rownames(Y)),]

P <- perm #number of permutations

anti-log if max < 50 in mixture file

if(max(Y) < 50) {Y <- 2^Y}

quantile normalization of mixture file

if(QN == TRUE){
tmpc <- colnames(Y)
tmpr <- rownames(Y)
Y <- preprocessCore::normalize.quantiles(Y)
colnames(Y) <- tmpc
rownames(Y) <- tmpr
}

intersect genes

Xgns <- row.names(X)
Ygns <- row.names(Y)
YintX <- Ygns %in% Xgns
Y <- Y[YintX,]
XintY <- Xgns %in% row.names(Y)
X <- X[XintY,]

standardize sig matrix

X <- (X – mean(X)) / sd(as.vector(X))

矩阵X是LM22矩阵

矩阵Y是待预测的矩阵

然后对

empirical null distribution of correlation coefficients

if(P > 0) {nulldist <- sort(doPerm(P, X, Y)$dist)}

print(nulldist)

header <- c(‘Mixture’,colnames(X),“P-value”,“Correlation”,“RMSE”)

print(header)

output <- matrix()
itor <- 1
mixtures <- dim(Y)[2]
pval <- 9999

iterate through mixtures

print(paste0(length(colnames(Y)),” Samples in total”))
while(itor <= mixtures){

print(paste0(“Dealing with sample:”,itor))
y <- Y[,itor]

standardize mixture

y <- (y – mean(y)) / sd(y)

run SVR core algorithm

result <- CoreAlg(X, y)

get results

w <- resultmix_r
mix_rmse <- result$mix_rmse

calculate p-value

if(P > 0) {pval <- 1 – (which.min(abs(nulldist – mix_r)) / length(nulldist))}

print output

out <- c(colnames(Y)[itor],w,pval,mix_r,mix_rmse)
if(itor == 1) {output <- out}
else {output <- rbind(output, out)}

itor <- itor + 1

}

save results

write.table(rbind(header,output), file=“CIBERSORT-Results.txt”, sep=“t”, row.names=F, col.names=F, quote=F)

return matrix object containing all results

obj <- rbind(header,output)
obj <- obj[,-1]
obj <- obj[-1,]
obj <- matrix(as.numeric(unlist(obj)),nrow=nrow(obj))
rownames(obj) <- colnames(Y)
colnames(obj) <- c(colnames(X),“P-value”,“Correlation”,“RMSE”)
print(paste0(“Finished at:”,Sys.time()))
obj
}

耐心一点的可以一句一句运行探索这个函数
这个事情,我们常常做。

  1. sig_matrix <- "CIBERSORT/LM22.txt"

  2. mixture_file = 'exprMat.txt'

  3. #read in data

  4. ## 读入signature数据

  5. X <- read.table(sig_matrix,header=T,sep="t",row.names=1,check.names=F)

  6. ## 读入表达量数据

  7. Y <- read.table(mixture_file, header=T, sep="t", check.names=F)

  8. ## 去除重复基因

  9. Y <- Y[!duplicated(Y[,1]),]

  10. ## 第一列转行名

  11. rownames(Y)<-Y[,1]

  12. Y<-Y[,-1]

  13. ## 数据框转为矩阵

  14. X <- data.matrix(X)

  15. Y <- data.matrix(Y)

  16. #order

  17. ## 行名排序

  18. X <- X[order(rownames(X)),]

  19. Y <- Y[order(rownames(Y)),]

这些操作就是分别读入表达矩阵和基因特征,最终产生基因数目一样的两个矩阵。
X现在是这样的

image

Y是这样的

image

接下来就是设置一下置换次数

  1. ## 设置置换次数
  2. ## #number of permutations
  3. perm <- 1000
  4. P <- perm

下面这一步很重要,这个函数会自动检测你的数据。
如果你的数据被log话之后,他会自动帮你去掉log,
所以,CIBERSORT接受的数据是不去掉log的,如果你一不小心取了log,也不要害怕,他会帮你再去掉。
当然检测数据是否被log化,我们有更好的方法,GEO已经提供了。
来完成你的生信作业,这是最有诚意的GEO数据库教程

  1. #anti-log if max < 50 in mixture file
  2. ## 这里说明,当他检测数据已经被log化之后,就会帮我们去log化
  3. if(max(Y) < 50) {Y <- 2^Y}

为什么要这么做,因为他是要以基因集为依据来推断,我们看看基因特征的表达量发现。
这些数据有的很大,可以看出是没有取过log的。

image

接下里要对数据进行quantile 矫正
理解 Quntile Normalization

使用的是这个函数preprocessCore::normalize.quantile

  1. QN=T
  2. if(QN == TRUE){
  3. tmpc <- colnames(Y)
  4. tmpr <- rownames(Y)
  5. Y <- preprocessCore::normalize.quantiles(Y)
  6. colnames(Y) <- tmpc
  7. rownames(Y) <- tmpr
  8. }

需要强调的是,只有芯片是需要这样做的,这跟芯片产生的过程有关。
而RNAseq数据,论文中推荐的是TPM

然后把两个数据的基因取交集,然后以交集的基因再去调整两个数据
用到的函数是%in%

  1. Xgns <- row.names(X)
  2. Ygns <- row.names(Y)
  3. YintX <- Ygns %in% Xgns
  4. Y <- Y[YintX,]
  5. XintY <- Xgns %in% row.names(Y)
  6. X <- X[XintY,]

现在X和Y的行都是517行

image

接下来他们对基因特征的数据进行了z-score转换
z-score的标准化究竟怎么弄?

  1. X <- (X - mean(X)) / sd(as.vector(X))

好了准备工作做完了,现在正式开始

核心函数CoreAlgd

这个函数是主要计算的函数,他接受基因集以及一个样本的数据,返回一个列表

  1. CoreAlg <- function(X, y){

try different values of nu

svn_itor <- 3

res <- function(i){
if(i==1){nus <- 0.25}
if(i==2){nus <- 0.5}
if(i==3){nus <- 0.75}
model<-e1071::svm(X,y,type=“nu-regression”,kernel=“linear”,nu=nus,scale=F)
model
}

if(Sys.info()[‘sysname’] == ‘Windows’) out <- parallel::mclapply(1:svn_itor, res, mc.cores=1) else
out <- parallel::mclapply(1:svn_itor, res, mc.cores=svn_itor)

nusvm <- rep(0,svn_itor)
corrv <- rep(0,svn_itor)

do cibersort

t <- 1
while(t <= svn_itor) {
weights = t(out[[t]]SV
weights[which(weights<0)]<-0
w<-weights/sum(weights)
u <- sweep(X,MARGIN=2,w,‘*’)
k <- apply(u, 1, sum)
nusvm[t] <- sqrt((mean((k – y)^2)))
corrv[t] <- cor(k, y)
t <- t + 1
}

pick best model

rmses <- nusvm
mn <- which.min(rmses)
model <- out[[mn]]

get and normalize coefficients

q <- t(modelSV
q[which(q<0)]<-0
w <- (q/sum(q))

mix_rmse <- rmses[mn]
mix_r <- corrv[mn]

newList <- list(“w” = w, “mix_rmse” = mix_rmse, “mix_r” = mix_r)

}

我们可以测试这个函数的结果

  1. ## 提取一个样本
  2. y <- Y[,1]
  3. y <- (y - mean(y)) / sd(y)
  4. ## 计算
  5. result <- CoreAlg(X, y)
  6. str(result)

image

返回的结果记录了三个结果,我们可以通过再次探索CoreAlg来确定那是三个返回值
w,mix_rmse,mix_r

这一段,我写不下去了,会录制成视频更新在答疑群中。
简单来说

image

w实际上就是就是个人除以总数,体现在最后的22个免疫细胞的值是个率,加起来等于1
RMSE就是均方根误差(Root Mean Squared Error),越小效果越好,解释看这个帖子
花了100个小时学习线性回归,写了个万字长文作总结。
而相关性是这样算的。
首先这个样本是有每个基因的表达量的。
然后算出来每个免疫细胞的占比后,用这个占比去把基因矩阵中的每个值都按照这个比值处理了

  1. u <- sweep(X,MARGIN=2,w,'*')

接下来,算出预测的单个样本中每个基因的表达量

  1. k <- apply(u, 1, sum)

这个表达量k和本身的表达量y求相关性得到相关性系数。

置换函数doPerm

那么现在只剩下p值了,p值是怎么算的呢?
使用置换检验。
就是用蒙特卡罗方法,不断地从原来的数据中跟单个样本等长的数据,模拟新样本,进行CoreAlg计算。
每次抽取出相关性系数r,当次数足够多的时候,就形成背景相关性系数分布。
我做了1000次后是这样的。

image

这个计算由doPerm函数完成,这个方法未来在GSEA课程的时候还会碰到。

  1. doPerm <- function(perm, X, Y){
  2. itor <- 1
  3. Ylist <- as.list(data.matrix(Y))
  4. dist <- matrix()
  5. print(paste0("do permutations:",perm," in total"))
  6. while(itor <= perm){
  7. print(paste0("nperm:",itor))
  8. #random mixture
  9. yr <- as.numeric(Ylist[sample(length(Ylist),dim(X)[1])])
  10. #standardize mixture
  11. yr <- (yr - mean(yr)) / sd(yr)
  12. #run CIBERSORT core algorithm
  13. result <- CoreAlg(X, yr)
  14. mix_r <- result$mix_r
  15. #store correlation
  16. if(itor == 1) {dist <- mix_r}
  17. else {dist <- rbind(dist, mix_r)}
  18. itor <- itor + 1
  19. }
  20. newList <- list("dist" = dist)
  21. }

这个函数做的工作刚才已经讲了,很简单。
就是通过sample命令不断产生模拟样本,进行CoreAlg计算抽取r值。

  1. yr <- as.numeric(Ylist[sample(length(Ylist),dim(X)[1])])

有了背景r分布,每次也能产生各个细胞的比率,有均方根误差RMSE,有相关性系数,还有p值,
这样最终结果的一行就有了。
那么只要批量对每一个样本计算即可得到所有结果
接下来的CIBERSORT主函数就用这句命令,计算的p值

  1. if(P > 0) {pval <- 1 - (which.min(abs(nulldist - mix_r)) / length(nulldist))}

我强行解释一波,每次真正的样本计算出来的r值,找到其在背景分布的位置,然后看右边红色的概率有多大。如果小于0.05,说明产生这种极端值并不是偶然的。
那么该样本的反卷积解析就是成功的,可信的。

这样批量每个样本后,就返回了我们想要的结果,以obj代替。

image

如果样本多,我们还能可以先把p值小的样本删掉。
既然每个细胞的值是个比率,加起来等于1,那么我们就可以画图来看了。
画图用到的技能
我喜欢的gather快要被淘汰了,迎面走来了更好用的宽长转换工具

  1. library(dplyr)
  2. library(tidyr)
  3. library(tibble)
  4. dd1 <- obj %>%
  5. as.data.frame() %>%
  6. rownames_to_column("sample") %>%
  7. pivot_longer(cols=2:23,
  8. names_to= "celltype",
  9. values_to = "Proportion")

现在的数据是这样的

image

有了数据画图就很容易

  1. library(ggplot2)
  2. ggplot(dd1,aes(sample,Proportion,fill = celltype)) +
  3. geom_bar(position = "stack",stat = "identity")+
  4. theme_bw()+
  5. guides(fill=guide_legend(ncol=1))

image

看纵坐标加起来就是1。
但是ssGSEA的数据,最后是富集分数,画这种图是不合适的。这个会在之后的帖子讲解。

现在CIBERSORT的输入输出都知道了。用起来会很有底气。

还能怎么改进呢?
置换那里速度太慢了,设置成1000次,就跟多了1000个样本一样,我们可以设置并行化加速。

要补充一点的是,如果研究的TCGA数据,其实大部分软件都把结果计算好了。比如CIBERSORT计算的 TCGA结果在这里
https://gdc.cancer.gov/about-data/publications/panimmune

image

直接去下载读入R语言就可以用了。
那么讲这么长有什么用呢?
是这样的:
这个函数使用起来这么简单,只要我们替换掉基因特征数据,就可以迁移到其他物种,
比如,我们想要量化小鼠的组织,那怎么办呢,只要找到小鼠免疫细胞的这个特征集就行了。
还好,这个事情,不需要我们做,有人已经做好了,下次我们就讲这个。
量化免疫浸润时CIBERSORT的注意事项

你可能感兴趣的:(量化免疫浸润时CIBERSORT的注意事项)