10X单细胞(10X空间转录组)数据分析之约束非负矩阵分解(cNMF)

hello,大家好,之前在文章10X单细胞(10X空间转录组)数据分析之NMF(非负矩阵分解)中介绍了NMF在单细胞数据分析中的运用,简单总结一下就是NMF算法的基本思想是将原始非负矩阵分解为两个非负矩阵的乘积从而对原始高维矩阵进行降维表示。

我们在做WGCNA的时候,得到的模块未必与细胞类型或者样本关联度很高,但是我们又想得到基因模块与某一种细胞类型或者一种样本类型高度关联而与其他类型几乎没关系,怎么做呢? cNMF就是为了这个而来。

python代码如下:

import numpy as np
import random

def nmf(X, r, k, e):
    '''
    X是原始矩阵
    r是分解的两个非负矩阵的隐变量维度,要远小于原始矩阵的维度
    k是迭代次数
    e是理想误差

    input X
    output U,V
    '''
    d, n = X.shape
    #print(d,n)
    #U = np.mat(random.random((d, r)))
    U = np.mat(np.random.rand(d, r))
    #V = np.mat(random.random((n, r)))
    V = np.mat(np.random.rand(n, r))
    #print(U, V)

    x = 1
    for x in range(k):
        print('---------------------------------------------------')
        print('开始第', x, '轮迭代')
        #error
        X_pre = U * V.T
        E = X - X_pre
        #print E
        err= 0.0
        for i in range(d):
            for j in range(n):
                err += E[i,j] * E[i,j]
        print('误差:', err)

        if err < e:
            break
        #update U
        a_u = U * (V.T) * V
        b_u = X * V
        for i_1 in range(d):
            for j_1 in range(r):
                if a_u[i_1, j_1] != 0:
                    U[i_1, j_1] = U[i_1, j_1] * b_u[i_1, j_1] / a_u[i_1, j_1]
        #print(U)
        
        #update V
        a_v = V * (U.T) * U
        b_v = X.T * U
        print(r,n)
        for i_2 in range(n):
            for j_2 in range(r):
                if a_v[i_2,j_2] != 0:
                    V[i_2,j_2] = V[i_2,j_2] * b_v[i_2,j_2] / a_v[i_2,j_2]
        #print(V)
        print('第', x, '轮迭代结束')

    return U, V

if __name__ == "__main__":
    X = [[5, 3, 2, 1, 2, 3],
         [4, 2, 2, 1, 1, 5],
         [1, 1, 2, 5, 2, 3],
         [1, 2, 2, 4, 3, 2],
         [2, 1, 5, 4, 1, 1],
         [1, 2, 2, 5, 3, 2],
         [2, 5, 3, 2, 2, 5],
         [2, 1, 2, 5, 1, 1], ]
    X = np.mat(X)
    #print(X)
    U, V = nmf(X, 2, 100, 0.001)
    print(U*V.T)

其实在单细胞数据分析的过程中,细胞基因的表达模式受多种因素干扰,基因通过协同作用维持细胞类型特有的生物学特征,而相互协同的基因作为基因表达模块(GEP)一起诱导和相应内外部信号,执行复杂的细胞功能。功能基因模块可以出现在多个不同的细胞类型中,而细胞类型基因模块代表一个单一的细胞类型,因此可利用这一事实来区分细胞类型基因模块和功能基因模块。通过cNMF分析,可同时推断出细胞类型相关和功能相关的GEPs,从而改进marker基因的推断,使得细胞类型鉴定更加准确。

什么是cNMF??

约束非负矩阵分解(CNMF)算法,该算法将标签信息作为附加的硬约束,使得具有相同类标签信息的数据在新的低维空间中仍然保持一致。
但是,CNMF算法对于无标签数据样本没有任何约束,因此在很少的标签信息时它的性能受限,并且对于类中只有一个样本有标签的情形,CNMF算法中构建的约束矩阵将退化为单位矩阵,失去其意义。

实现代码

import numpy as np
import random

def cnmf(X, C, r, k, e):
    '''
    X是原始矩阵,维度为d*n
    C是有标签样本指示矩阵,维度为l*c(l——有标签的样本数量,c——类别数量)
    r是分解的两个非负矩阵的隐变量维度,要远小于原始矩阵的维度
    k是迭代次数
    e是理想误差
  
    input X,C
    output U,V
    '''
    d, n = X.shape
    l, c = C.shape

    #计算A矩阵
    I = np.mat(np.identity(n-l))
    A = np.zeros((n, n + c - l))

    for i in range(l):
        for j in range(c):
            A[i,j] = C[i,j]

    for i2 in range(n-l):
        A[l+i2, c+i2] = I[i2, i2]
    A = np.mat(A)
    U = np.mat(np.random.rand(d, r))
    Z = np.mat(np.random.rand(n + c - l, r))

    #print(A)

    x = 1
    for x in range(k):
        print('---------------------------------------------------')
        print('开始第', x, '轮迭代')
        #error
        X_pre = U * (A*Z).T
        E = X - X_pre
        #print E
        err= 0.0
        for i in range(d):
            for j in range(n):
                err += E[i,j] * E[i,j]
        print('误差:', err)

        if err < e:
            break
        #update U
        a_u = U * Z.T * A.T * A * Z
        b_u = X * A * Z
        for i_1 in range(d):
            for j_1 in range(r):
                if a_u[i_1, j_1] != 0:
                    U[i_1, j_1] = U[i_1, j_1] * b_u[i_1, j_1] / a_u[i_1, j_1]
        #print(U)

        #update Z
        #print(Z.shape,n,r)
        a_z = A.T * A * Z * U.T * U
        b_z = A.T* X.T * U
        for i_2 in range(n + c - l):
            for j_2 in range(r):
                #print(i_2, j_2, a_z[i_2,j_2])
                if a_z[i_2,j_2] != 0:
                    Z[i_2,j_2] = Z[i_2,j_2] * b_z[i_2,j_2] / a_z[i_2,j_2]
        #print(V)
        print('第', x, '轮迭代结束')

    V = (A*Z).T
    return U, V

if __name__ == "__main__":
    X = [[5, 3, 2, 1, 2, 3],
         [4, 2, 2, 1, 1, 5],
         [1, 1, 2, 5, 2, 3],
         [1, 2, 2, 4, 3, 2],
         [2, 1, 5, 4, 1, 1],
         [1, 2, 2, 5, 3, 2],
         [2, 5, 3, 2, 2, 5],
         [2, 1, 2, 5, 1, 1],]#8*6,6个样本
    X = np.mat(X)
    C = [[0, 0, 1],
         [0, 1, 0],
         [0, 1, 0],
         [1, 0, 0],]#4*3,假设有4个样本有标签,总共有三类标签
    #print(X)
    C = np.mat(C)
    U, V = cnmf(X, C, 2, 100, 0.01)
    print(U.shape, V.shape)
    print(U * V)

cNMF在单细胞数据分析中的运用(文献用法)

cNMF的目的

To better characterize sample heterogeneity in whole-tumor samples
图片.png
图注: Heatmap showing the correlation between signatures obtained for all 13 whole-tumour samples.

用法

The cNMF algorithm was applied individually to each whole-tumor sample with some modifications。In brief, nonnegative matrix factorization was run 100 times for k from 2 to 15 signatures. For each k, the 100 repetitions are clustered in k groups. We expect a stable clustering solution would produce tight clusters with one signature per cluster for each of the 100 repetitions. The proportion of repetitions with one signature per cluster was called reproducibility. Clustering of the signatures was done by k means with a constraint of uniform cluster sizes, prioritizing higher correlations. The largest k with a reproducibility above 0.9 was chosen. For a chosen k, we confirmed the clustering solution was appropriate by running tSNE on the signatures it generated. The final signatures for a given sample was obtained by averaging the signature repetitions within a cluster, excluding repetitions with poor reproducibility (the ones which did not produce a signature per cluster). We obtained between five and nine final signatures per sample, 79 signatures in total. From the inter-signature Pearson correlations, we used hierarchical clustering to find trends of signatures (Fig. 1e, hierarchical tree). Six main groups emerged. In one of these groups, important variations in gene weights were observed: signatures characterized by OLIG2 and ASCL1, for example, had less DCX and STMN1, and vice versa. We reclustered this group in two, yielding the final seven groups (Fig. 1e and Supplementary Fig. 2f,就是上图).
We identified the most characteristic genes of each signature group by ranking genes according to their relative signal to noise ratio (snr) and chose the top 40 for the heatmap.
图片.png

关于信噪比(SNR),大家可以参考我的文章单细胞数据信噪比(Signal-to-noise ratio,SNR)

We scored each signature according to the TCGA using the method described above (Classifying cells by TCGA subtype). A given signature was labeled with the subtype yielding the highest score.(下图)
图片.png

看看文献的原始代码(参照上述cNMF的实现方式)

% cNMF_seperate.m
% use NMR to find signatures in scRNA data sample by sample. Based on cNMF (Kotliar et al.)  ###看来是样本作为主要标签。
% Author: Charles Couturier
% Date: August 3, 2018

%% prepare data and initialize parameters
% genes: list of gene names and ensembl code; logm: log of raw counts; sample: vector 
%  identifying cells by sample; sample_id: a list of the samples
load('gbm.mat','genes','logm','sample','sample_id')
kvals = 2:15;
L = length(kvals);
nreps = 100;

allHs = cell(L,1);
allHc = cell(L,1);
allSil = cell(L,1);
allE = cell(L,1);
allR = cell(L,1);
allCL = cell(L,1);

for s = 1:length(sample_id)
fprintf('Sample %s in progress, %i of %i\n',sample_id{s},s,length(sample_id))
    
data = logm(sample==s,:);

% initialize
m = size(data,1);
n = size(data,2);

E = zeros(L,1);
R = zeros(L,1);
Sil = zeros(L,1);
consensus_H = cell(L,1);
allH = cell(L,1);
CL = cell(L,1);

%% NMF
opt = statset('MaxIter',1e6);
parfor kid = 1:L
    k = kvals(kid);
    hi = zeros(k*nreps,n);
    consensus_hi = zeros(k,n);
    D = zeros(nreps,1);
    
    for i = 1:nreps
        [~,hi(i*k-k+1:i*k,:),D(i)]=nnmf(data,k,'replicates',20,'options',opt,'algorithm','mult');
    end
    
    % k clusters
    cl = uniform_kmeans(hi,k,'Replicates',10); 
    % find replicates with 1 of each cluster
    goodrep = all(sort(reshape(cl,k,nreps),1) == [1:k]',1); 
    goodrep = repmat(goodrep,k,1);
    goodrep = goodrep(:);
    
    % consensus hi finds median of each component cluster, removing
    % bad replicates (see above)
    for i = 1:k
        consensus_hi(i,:) = median(hi(cl==i & goodrep,:),1); 
    end
    
    allH{kid} = hi;
    consensus_H{kid} = consensus_hi;
    Sil(kid) = mean(silhouette(hi,cl));
    E(kid) = mean(D);
    R(kid) = sum(goodrep)/(k*nreps);
    CL{kid} = cl;
    fprintf('Run for %i components completed\n',k)
end

allHs{s} = allH;
allHc{s} = consensus_H;
allSil{s} = Sil;
allE{s} = E;
allR{s} = R;
allCL{s} = CL;

end

save 'NMF_results_separate.mat' allHs allHc allSil allE allR allCL

%% find best ks

best_k = zeros(length(sample_id),1);
th = 0.9; % threshold in reproducibility to reach to select k

for s = 1:length(sample_id)
    kid = find(kvals==best_k(s));
    E = allE{s};
    R = allR{s};
    Sil = allSil{s};
    CL = allCL{s};
    allH = allHs{s};

    kid = max(find(R>=th));
    best_k(s) = kvals(kid);
    
    figure;
    subplot(1,2,1)
    %subplot(length(sample_id),2,2*s-1)
    yyaxis left
    plot(kvals,E)
    yyaxis right
    plot(kvals,R)
    hold on
    plot(kvals,Sil)
    %legend({'E','Reproducibility of distinct clusters','Silhouette score'})
    
    y = tsne(allH{kid});
    subplot(1,2,2)
    gscatter(y(:,1),y(:,2),CL{kid},lines)
    legend('off')
    title(sprintf('%s',sample_id{s}))
    
    print('-depsc2',sprintf('%s.eps',sample_id{s}))
end


%% get W from H

H = [];
sig_id = [];
for s = 1:length(sample_id)
    
    data = logm(sample==s,:);
    consensus_H = allHc{s};
    kid = find(kvals==best_k(s));
    H0 = consensus_H{kid};
    W0 = max(0,data/H0);

    opt = statset('Maxiter',1e6,'Display','final');
    [~,H1] = nnmf(data,best_k(s),'h0',H0,'w0',W0,...
                     'options',opt,'replicates',100,...
                     'algorithm','als');
    H = [H; H1];
    sig_id = [sig_id; ones(size(H1,1),1)*s];
             
end

save all_signatures.mat H sig_id

相对还是很简单的,大家不妨试一试,找一找细胞类型相关协同性很高的gene set。

生活很好,有你更好

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

推荐阅读更多精彩内容