基于Python实现通路富集模型

前言

本文章主要涉及基因功能富集分析的原理解释,统计检验以及最终基于Python代码的整体逻辑实现。富集分析应该算生信里是最常用的分析方法之一了,很多做生信的都是基于R或者Spss等软件,所以这次想用Python来回顾每一步处理任务。

一:超几何分布介绍

超几何分布时一种离散型概率分布,也许中学就学过最经典的例子:假设一个袋子有10个球,其中红色球6个,白色球4个,那么我一次从袋子中抓取4个,请问这4个球中至少3个是红球的概率是多少?

解:至少3个红球,那就是3个或者4个,设随机变量 X 为4个中含有红球的个数,即求解 P r e s = P ( X = 3 ) + P ( X = 4 ) P_{res} = P(X=3)+P(X=4) Pres=P(X=3)+P(X=4)
通过超几何概率公式: P ( X = k ) = C M k C N − M n − k C N n P(X=k)=\frac{C_M^{k}C_{N-M}^{n-k}}{C_N^n} P(X=k)=CNnCMkCNMnk ,这里 N N N 等价于10个球, n n n 等价于抓取的4个球, M M M 等价于6个红色球,那么最后的 k k k 就是我们要求手里的目标球的个数,这里为 3和4。

  • 计算结果

我们先手动计算此结果,然后用python计算,由于此公式比较简单,直接调用相关函数即可。

手动结果 P r e s = P ( X = 3 ) + P ( X = 4 ) = 80 210 + 15 210 = 0.45 P_{res} = P(X=3)+P(X=4)=\frac{80}{210}+\frac{15}{210}=0.45 Pres=P(X=3)+P(X=4)=21080+21015=0.45

我们用代码函数计算

from scipy import stats

p1 = stats.hypergeom.pmf(k=3, M=10, n=6, N=4)
p2 = stats.hypergeom.pmf(k=4, M=10, n=6, N=4)
print(round(p1 + p2,2))###0.45
  • 期望与方差

对超几何分布 X ∼ H ( N , M , n ) X\sim H(N,M,n) XH(N,M,n) ,随机变量 X X X 的数学期望

E ( X ) = n M / N E(X)=nM/N E(X)=nM/N

方差由定义推导比较麻烦,我们直接给出

D ( X ) = E ( X 2 ) − E ( X ) 2 = n M N ( 1 − M N ) N − n N − 1 D(X)=E(X^2)-E(X)^2=n\frac{M}{N}(1-\frac{M}{N})\frac{N-n}{N-1} D(X)=E(X2)E(X)2=nNM(1NM)N1Nn

二:富集原理

富集分析原理就是超几何分布的使用。假设人类背景基因3W个,已知共500个基因参与某一通路,由已知我们有一个自己的含有200个基因的基因群,且这200个基因都来自背景基因,恰巧这200个基因里面有6个参与此通路,那么这6个基因到底是不是偶然还是显著富集在此通路上?于是我们要做相应的显著性检验。

  • p 值检验

原假设 H 0 H_0 H0: 此一组基因并不显著富集在此通路上

p p p 值计算公式: p = 1 − ∑ i = 0 m − 1 C M i C N − M n − i C N n p=1-\sum_{i=0}^{m-1}{\frac{C_{M}^{i}C_{N-M}^{n-i}}{C_{N}^{n}}} p=1i=0m1CNnCMiCNMni ,若 p < 0.5 p<0.5 p<0.5,则为统计意义上的显

著,即拒绝原假设。

统计上,一般原假设的建立都是要被推翻的。

很显然上面的 p p p 值的计算也就是基于超几何分布公式的,假设取遍除了自身的所有指定元素然后算其概率并与 1作差即可。

  • 温和的BH(Benjaminiand Hochberg)法

我们知道尽管计算出来的 p p p值往往都很小,一次“不好的出现”也许只有0.001,但如果重复1万次,那么再低的概率也许都能发生,按照平均期望最起码也能出现10次 。所以基于这种可能,在做检验时,我们也怕重复过多而出现不显著结果过多出现。

B H BH BH方法一定程度上能解决这种事件过多的出现。
B H BH BH 法需要将总计 M M M 次检验的结果按由小到大进行排序, k k k 为其中一次检验结果的 p p p 值所对应的排名。找到符合原始阈值 α \alpha α 的最大的 k k k 值,满足 p ( k ) < α ∗ k / M p(k)<\alpha*k/M p(k)<αk/M,认为排名从1到 k k k 的所有检验存在显著差异,并计算对应的 q q q 值公式为 q = p ∗ ( M / k ) q=p*(M/k) q=p(M/k)

举个例子就能很清楚地说明此修正过程:

在这里插入图片描述

图1

比如 k = 1 k=1 k=1 ,则 α ∗ k / M = 0.05 ∗ 1 / 5 = 0.025 \alpha*k/M=0.05*1/5=0.025 αk/M=0.051/5=0.025 p ( 1 ) = 0.001 < 0.025 p(1)=0.001<0.025 p(1)=0.001<0.025 是显著的,同理 k = 2 k=2 k=2 α ∗ k / M = 0.05 ∗ 2 / 5 = 0.02 > P 2 = 0.023 \alpha*k/M=0.05*2/5=0.02>P_2=0.023 αk/M=0.052/5=0.02>P2=0.023 不再显著,则 G 1 G1 G1 G 4 G4 G4 G 5 G5 G5 G 2 G2 G2都不是显著表达基因,那么我们就把 k k k 找到了,再根据公式能算出对应的 q q q 值。

三:代码计算

我们依旧使用上面图中的数据,看代码计算结果:
在这里插入图片描述

图2

从图2可以发现,计算过程完全正确。但是我们要注意一点,就是并不是任何 p p p 值结果都能进行 B H BH BH检验的,从不等式 p ( k ) < α ∗ k / M p(k)<\alpha*k/M p(k)<αk/M 来看,此不等式并不是一直成立的(很有可能 p p p 值过于大导致不等式不存在),那么我们只能用原始 p p p 值进行显著性分析了。

def BH_test(alpha,pvals,gn):
    l = len(pvals)
    k = [i for i in range(1,l+1)]
    k = np.array(k)
    sort_id = np.argsort(np.array(pvals))
    pvals = sorted(np.array(pvals))##升序排序
    gn = np.array(gn)[sort_id]##基因名字按照p值从小到大排序
    P_k = alpha * k / len(pvals)##计算P(k)向量值
    q =  pvals * (l / k)##计算qvals
    comp_res = pvals < P_k
    idx = [i for i,x in enumerate(list(comp_res)) if x==1]
    df = pd.DataFrame([gn,pvals,P_k,comp_res,q],index=['order_name','pvals','P(k)','compres','qvals']).T
    try:
        loc = idx[-1] + 1###取最后的一个索引
        print(df)
        print('\033[1;33m对应位置坐标最大k值:\033[0m',loc)
        print('\033[1;34m最终筛选出基因跟疾病有关的疾病列表为(取一般置信度=%s水平下):%s\033[0m'%(alpha,gn[:loc]))
        return df
    except Exception as ee:###异常处理
        print('\033[1;31m{0:*^80}\033[1;0m'.format('BH认证失败,可能是所有原始P_value过大,如下结果展示'))
        df = pd.DataFrame([pvals,P_k],index=['P_value','P(k)']).T
        print(df)
        return df

pls = [0.023,0.12,0.5,0.15,0.082]
namels = ['g1','g2','g3','g4','g5']
BH_test(0.05,pls,namels)

在这里插入图片描述

图3

四:总结

富集分析的原理其实很简单,在这里只是根据原理做相关总结实验,并仅仅提供些具体数值的计算代码。但实际运用中可能要考虑具体场景问题做相应的数据处理。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43577256/article/details/123005684