CIBERSORT初探

简介

CIBERSORT这款软件利用反卷积的方法,利用单细胞RNA-seq的数据,提取特征后,反推Bulk-seq各类细胞成分所占比例。这款软件目前推出了CIBERSORTx,但是账号貌似有点难申请
而这次我们着重介绍下CIBERSORT的R版本
而CIBERSORT计算细胞组分的方法使用去卷积原理实现的,而且通常是线性去卷积

原理图

如图所示,Bulk-seq表达矩阵的结果可以利用单细胞表达矩阵的结果乘一个权重向量来表示,那么这个权重向量表示的意义即为在Bulk-seq,每一个sample的细胞组分含量
比方说,在这里的cell_1和cell_2可以认为是两种不同的cell type,那么i = 1;j = 2表示的意思是在Bulk-seq中,sample_1中有1份的cell_1和2份的cell_2

R

核心代码由三部分组成:CoreAlgdoPermCIBERSORT,其中该软件利用的是svm反卷积进行推算:

#读取文件
X <- read.table(sig_matrix,header=T,sep="\t",row.names=1,check.names=F)
Y <- read.table(mixture_file, header=T, sep="\t", row.names=1,check.names=F)
  
X <- data.matrix(X)
Y <- data.matrix(Y)
  
#order
X <- X[order(rownames(X)),]
Y <- Y[order(rownames(Y)),]

其中mixture_file是RNA-seq的数据;sig_matrix为单细胞RNA-seq数据


mixture_file

sig_matrix

紧接着对两个文件的基因取交集并且分别标准化:

#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))

#standardize mixture
y <- Y[,itor]
y <- (y - mean(y)) / sd(y)
#这里的itor遍历的是mixture_file的列元素,即y是mixture_file的列元素

注意:这里的 itor 遍历的是mixture_file的列元素,即y是mixture_file的列元素,表示的是Bulk-seq的每一个sample,而x代表单细胞数据

接下来就是整个代码的核心段了,涉及到CoreAlg:

#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<-svm(X,y,type="nu-regression",kernel="linear",nu=nus,scale=F)
    model
}
  
#输出结果
out <- mclapply(1:svn_itor, res, mc.cores=1)
##svn_itor遍历不同的nu

关于参数type:

1.If you have a classification problem, i.e., discrete label to predict, you can use C-classification and nu-classification.
2.If you have a regression problem, i.e., continuous number to predict, you can use eps-regression and nu-regression.
3.If you only have one class of the data, i.e., normal behavior, and want to detect outliers. one-classification.

这一部分res()函数通过调用svm()函数,并调试不同的nu值建立不同的模型(这里 nu 值为1-3),而对于整个svm的过程可以理解为回归问题(SVR,即y为响应变量;X为决策变量,每种细胞类型相当于每一维的数据)

这里简单介绍下SVR(支持向量机回归):

来自微信

SVM是用作分类模型,而SVR用作的是回归模型;SVM主要是找到一个超平面能够将数据分类,而SVR寻找的是一个线性模型y=wx+b,目的是使真实值与预测值差距达到最小,从而构建出回归模型,来描述数据的趋势

接下来需要将out的结果(里面囊括3个svm模型)循环赋值出来,并且定义相关的权重:

#do cibersort
 t <- 1
  while(t <= svn_itor) {
    weights = t(out[[t]]$coefs) %*% 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
 } 

##svn_itor遍历不同的nu

这里的 t 代表不同参数(nu,一共3个值)下的svm模型
这里的权重(weight)定义为:svm的coefs与SV的乘积(svm模型$一下可以找到这个两个系数),这个乘积表示为线性回归平面的法向量(权重)weight,并且权重weight小于0的都定义为0;w代表的是权重矩阵的每一个元素除以该矩阵元素的总和 ;这里的u为X矩阵(单细胞表达谱)的每一个元素减去w;定义ku矩阵每一行求和;定义
nusvm为 sqrt((mean((k - y)^2)));定义corrv为 corrv[t] <- cor(k, y),即k和y的相关系数

注明:
svm的coefs指的是svm模型的系数;而SV指的是支持向量,支持向量是数据集的点,它们靠近分隔类别的平面

coefs

这里的coefs指的是svm模型的系数

SV

这里的SV指的是每一种细胞类型对应于每个基因的支持向量,
而这里的coefs与SV相乘目的是构造出细胞组分的数值,这个数值是线性回归平面的法向量 w(如下图),并且svm模型的系数长度与支持向量的长度是一致的,这个法向量的大小代表着分类指标的贡献

那么怎么理解这个贡献呢?


那么对应的决策变量W越高,那么它对响应变量的贡献(指的是增长贡献)越大

举个例子:

  1. 其中,在bulk的表达矩阵中,gene1 的表达量 E1 = β1×e1 + β2×e4,其中 e1 和 e2 分别是 cell cluster A 和 cell cluster B gene1 的表达量,同理可知其他基因的,也就是 bulk 中 gene1 的表达量由 cell cluster A 和 cell cluster B gene1 的表达量组成,而系数 β1 和 β2 理解为细胞比例
  2. 从图像来看,假设 gene 多的话,单独分离开 cell cluster A 和 cell cluster B来看:
    图中的红点代表不同基因的表达量,由于响应变量代表基因在bulk中的表达量,因此纵坐标在 cell cluster A 和 cell cluster B 是一样的。显然在 cell cluster A 中的基因表达量要偏低一些,而在 cell cluster B 中表达量偏高一些;因此基因表达量偏高的细胞,其比例会偏少,因此基因表达量偏低的细胞,其比例会偏多(斜率 β1 > β2; α > θ) 注明 E1 = β1×e1 + β2×e4,bulk的表达量由对应 cell cluster A 和 cell cluster B 的基因表达量组成
  3. 这种比例应该理解为SVR多元回归中拟合出来的情况,只不过借助线性方程E_bulk = β1×e_cell_A + β2×e_cell_B的加和状态,将参数β1和β2比作为细胞比例(细胞的量)乘相应的cell cluster对应基因的表达量,作为最终bulk的基因表达量

然后从 out 中选择最优模型:

#pick best model
rmses <- nusvm
##令rmses 等于 nusvm
mn <- which.min(rmses)
##选择最小值
model <- out[[mn]]
##提取最优模型
  
#get and normalize coefficients
q <- t(model$coefs) %*% model$SV
q[which(q<0)]<-0
w <- (q/sum(q))
##标准化
  
mix_rmse <- rmses[mn]
mix_r <- corrv[mn]  
#提取最优模型的rmses(nusvm)和corrv

那么经过CoreAlg计算出来的结果为:

result = CoreAlg(X, y)

那么相应的w,mix_r和mix_rmse为:

w <- result$w
mix_r <- result$mix_r
mix_rmse <- result$mix_rmse

其中w为:

w

每一个w表示其中一个sample各个细胞的组分,即表示bulk-seq在每一个sample中各个细胞组分的占比
mix_r mix_rmse为:

然而,这只是mixture_file(Bulk-seq)的一列数据,接下来我们计算每一列的rmses(nusvm)和corrv

while(itor <= mixtures){
   
    y <- Y[,itor]
    #standardize mixture
    y <- (y - mean(y)) / sd(y)
    #run SVR core algorithm
    result <- CoreAlg(X, y)
     
    #get results
    w <- result$w
    mix_r <- result$mix_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 
    ##这里的itor遍历的是mixture_file的列元素,即y是mixture_file的列元素
  }

最后输出的结果为bulk-seq每一个sample的w矩阵:



即每个组分所占比例

小tip

这个官方代码中quantile的标准化方式可能会报错,不妨在CIBERSORT函数下修改如下:

quantile_normalisation <- function(df){
    df_rank <- apply(df,2,rank,ties.method="min")
    df_sorted <- data.frame(apply(df, 2, sort))
    df_mean <- apply(df_sorted, 1, mean)
    
    index_to_mean <- function(my_index, my_mean){
      return(my_mean[my_index])
    }
    df_final <- apply(df_rank, 2, index_to_mean, my_mean=df_mean)
    rownames(df_final) <- rownames(df)
    return(df_final)
  }

##改动如下:
##原:
if(QN == TRUE){
    tmpc <- colnames(Y)
    tmpr <- rownames(Y)
    Y <- normalize.quantiles(Y)
    colnames(Y) <- tmpc
    rownames(Y) <- tmpr
  }

##改:
 if(QN == TRUE){
    tmpc <- colnames(Y)
    tmpr <- rownames(Y)
    Y <- quantile_normalisation(Y)
    colnames(Y) <- tmpc
    rownames(Y) <- tmpr
  }

另外,有关p值计算,该软件采用的是置换检验的方式,详见微信文章:《量化免疫浸润时CIBERSORT的注意事项》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容