$N_c$種類の化合物と$N_p$種類のPathwayがあるとします。
このとき、「化合物$i$ ($i=1, …, N_c$)の標的タンパク群」と「Pathway$j$ ($j=1, …, N_p$)を構成するタンパク群」の重なり度合いを、Fisherの正確確率検定によって$p$値として評価することができます。
具体的には以下のような表が得られます。
Pathway $1$ | Pathway $2$ | … | Pathway $j$ | … | Pathway $N_p$ | |
化合物 $1$ | $p_{11}$ | $p_{12}$ | … | $p_{1j}$ | … | $p_{1N_{p}}$ |
化合物 $2$ | $p_{21}$ | $p_{22}$ | … | $p_{2j}$ | … | $p_{2N_{p}}$ |
… | … | … | … | … | … | … |
化合物 $i$ | $p_{i1}$ | $p_{i2}$ | … | $p_{ij}$ | … | $p_{iN_{p}}$ |
… | … | … | … | … | … | … |
化合物 $N_c$ | $p_{N_{c}1}$ | $p_{N_{c}2}$ | … | $p_{N_{c}j}$ | … | $p_{N_{c}N_{p}}$ |
上記のテーブルの行で定義される横ベクトルを「化合物の標的Pathway」とします。
そこで本記事では上記のテーブルを作成する手順を説明します。
0. はじめに
本記事は、以下の記事の続きになります。
1. 化合物の標的タンパクを取得
以下の記事に従い、化合物のInChIKeyから標的タンパクのリスト(シンボル表記)を得る辞書(ink_syms
)を作成します。
あるいは以下のようにpickleでロードします。
with open(f'{TEMP_DIR}/pathwayTerm_to_symbols.pkl', 'rb') as tf: ptm_syms = pickle.load(tf)
2. Pathwayを構成するタンパクを取得
以下の記事に従い、PathwayのIDからPathwayに含まれるのタンパクのリスト(シンボル表記)を得る辞書(ptm_syms
)を作成します。
あるいは以下のようにpickleでロードします。
with open(f'{TEMP_DIR}/pathwayTerm_to_symbols.pkl', 'rb') as tf: ptm_syms = pickle.load(tf)
3. Fisherの正確確率検定
冒頭でも示した以下の表について、$p_{ij}$を計算する方法を説明します。
Pathway $1$ | Pathway $2$ | … | Pathway $j$ | … | Pathway $N_p$ | |
化合物 $1$ | $p_{11}$ | $p_{12}$ | … | $p_{1j}$ | … | $p_{1N_{p}}$ |
化合物 $2$ | $p_{21}$ | $p_{22}$ | … | $p_{2j}$ | … | $p_{2N_{p}}$ |
… | … | … | … | … | … | … |
化合物 $i$ | $p_{i1}$ | $p_{i2}$ | … | $p_{ij}$ | … | $p_{iN_{p}}$ |
… | … | … | … | … | … | … |
化合物 $N_c$ | $p_{N_{c}1}$ | $p_{N_{c}2}$ | … | $p_{N_{c}j}$ | … | $p_{N_{c}N_{p}}$ |
まずは化合物 $i$ とPathway $j$ について以下のような分割表を作成します。
全タンパク ($N$) | 化合物 $i$ に | 合計 | ||
含まれない | 含まれる | |||
Pathway $j$ に | 含まれる | $a$ | $b$ | $a + b$ |
含まれない | $c$ | $d$ | $c + d$ | |
合計 | $a + c$ | $b + d$ | $a + b + c + d = N$ |
合計部分を固定した場合、上記のような表が得られる確率は超幾何分布に従います(詳しくはこちらの記事)。
具体的には以下のようにして$p_{ij}$を計算できます。
$$p_{ij} = \frac{{}_{a+c}C_a \times {}_{b+d}C_b}{{}_{n}C_{a+b}}$$
4. 実装
まずは全タンパクの数($=N$)を計算します。pathways_proteins
はこちらの記事で、compounds_proteins
はこちらの記事で作成した変数です。
# n_proteins proteins_string_chembl = list(set(pathways_proteins) | set(compounds_proteins)) n_proteins = len(proteins_string_chembl) print(f'# of unique proteins found in either string dataset or chembl dataset: {n_proteins}')
# of unique proteins found in either string dataset or chembl dataset: 24214
次に分割表を作成する関数を定義します。引数に渡す値は以下です。
target_proteins
:化合物$i$の標的タンパクのシンボル表記のリスト(ink_syms[i]
)proteins_in_pathway
:Pathway$j$を構成するタンパクのシンボル表記のリスト(ptm_syms[j]
)n_proteins
:先ほど計算したn_proteins
# library import pandas as pd # contingency table: each compound x each pathway def make_contingency_table( target_proteins, proteins_in_pathway, n_proteins ): _b = set(target_proteins) & set(proteins_in_pathway) _a = set(proteins_in_pathway) - _b _d = set(target_proteins) - _b a, b, d = len(list(_a)), len(list(_b)), len(list(_d)) c = n_proteins - (a + b + d) df_table = pd.DataFrame([[a, b], [c, d]]) df_table.columns = ['~is_target', 'is_target'] df_table.index = ['isin_pathway', '~isin_pathway'] df_table = df_table.T return df_table
続いて、上記で作成した分割表を作成する関数(make_contingency_table
)の返り値を引数として、Fisherの正確確率検定による$p$値を返す関数を作成します。
# library from scipy.stats import fisher_exact # fisher's exact test def calc_p(table): odds_ratio, p = fisher_exact(table, alternative='less') return p
最後に、化合物のInChIKeyのリストとPathwayのID(term)をリストを引数とし、化合物とPathwayの全組合せのパターンについて上記のcalc_p
関数を実施する関数enrichment_analysis
を作成します。
なお、InChIKeyから標的タンパクのシンボル表記を得るためにink_syms
を、Pathway ID (term)からPathwayを構成するタンパクのシンボル表記を得るためにptm_syms
も引数として渡します。加えて、正確確率検定に必要なn_proteins
も引数に加えます。
# make table & tests for all inputs def enrichment_analysis( inkList, ptmList, n_proteins, ink_syms, ptm_syms ): pvals = [] for i, ik in enumerate(inkList): pvals.append([]) for pt in ptmList: table = make_contingency_table(ink_syms[ik], ptm_syms[pt], n_proteins) pvals[-1].append(calc_p(table)) return pvals
ただし実際は以下に留意して
- 適当な間隔で進捗と計算時間を標準出力
- 適当な間隔で結果を保存する
以下のようにした方が良いかもしれません。引数のrep_interval
は間隔に関する値です。
# library import datetime import pickle import time import os # const TEMP_DIR = 'temp' # 結果の保存先のディレクトリ名に用いるタイムスタンプ出力関数 def now(): return str(datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')) # make table & tests for all inputs def enrichment_analysis( inkList, ptmList, n_proteins, ink_syms, ptm_syms, rep_interval=50 ): # 保存先の作成 no = now() os.makedirs(f'{TEMP_DIR}/pvals/{no}/{len(inkList)}', exist_ok=True) # 計算時間測定用 st0 = time.time() st = time.time() # enrichment analysis pvals = [] for i, ik in enumerate(inkList): pvals.append([]) for pt in ptmList: table = make_contingency_table(ink_syms[ik], ptm_syms[pt], n_proteins) pvals[-1].append(calc_p(table)) # save & stdout if (i + 1) % rep_interval == 0: # save temp array with open(f"{TEMP_DIR}/pvals/{len(inkList)}/pvals.{i + 1}.pkl", "wb") as tf: pickle.dump(pvals[(i + 1) - rep_interval:(i + 1)], tf) # stdout (time) print(f'{i + 1} / {len(inkList)}: Elapsed time was {time.time() - st:.1f} sec.') st = time.time() # save results with open(f"{TEMP_DIR}/pvals/{len(inkList)}/pvals.{i + 1}.pkl", "wb") as tf: pickle.dump(pvals[len(pvals) - (len(pvals) % rep_interval):], tf) # stdout (time) print(f'Finished: {time.time() - st0:.1f} sec.') return pvals
5. Pathwayベクトルの算出
前節で定義した関数を用いて、冒頭の表に相当する結果を作成します。
# ChEMBLから取得した化合物のInChIKeyリスト inks = list(ink_syms.keys()) # STRINGから取得したpathwayのterm(ID)のリスト ptms = [pt for pt in ptm_syms.keys() if 3 <= len(ptm_syms[pt])] # enrichment analysis pvals = enrichment_analysis( inks, ptms, n_proteins, ink_syms, ptm_syms, rep_interval=100 )
6. Pathwayスコアベクトルの算出
前節で計算したp値のベクトルをそのまま使用することは少なく、実際は$-log_{10}$をとった「スコア」を利用します。
スコアの計算は以下のように実施します。
# library import numpy as np # dataframe df_pvals = pd.DataFrame(pvals) df_pvals.index = inks df_pvals.columns = ptms # score df_pvals = df_pvals.drop_duplicates() df_score = df_pvals.map(lambda x: -np.log10(x)) # save no = now() os.makedirs(f'{TEMP_DIR}/scores/{no}/df_score', exist_ok=True) with open(f'{TEMP_DIR}/scores/{no}/df_score.pkl', 'wb') as tf: pickle.dump(df_score, tf)
7. まとめ
本記事ではChEMBLの標的タンパクデータとSTRINGのPathwayデータを利用し、化合物の標的Pathwayをスコアベクトルの形で算出しました。
コメント