【シングルセル】Scanpyを用いて各クラスターに自動で細胞種を割り当てる【scRNA-seq】 

【シングルセル】Scanpyを用いて各クラスターに自動で細胞種を割り当てる【scRNA-seq】 

Pythonを使って細胞アノテーションをしたくありませんか?今回はPythonだけを用いてクオリティコントロール・クラスタリング・アノテーションを行います。更にアノテーションについてdecouplerとCellTypistの二つの方法を紹介します。Pythonは機械学習のライブラリが豊富にあり、より詳しい解析をする事が出来ます。また、2つのアノテーション方法を知っておくと、目的に応じて使い分けられます。ぜひ挑戦してみましょう。

Tested Environment

google colaboratory

細胞アノテーション(注釈付け)とは?


細胞アノテーションとは、細胞が発現しているRNA量(発現遺伝子)に基づき、細胞名を決定することです。これにより、試料の中にどんな細胞が存在していたのかを同定する事が出来ます。

以下でもう少し詳しく説明します。

細胞アノテーションは遺伝子の発現情報に基づき、データベースの細胞タイプの中から最も類似した細胞タイプを割り当てます。これには様々な方法があり、今回はdecouplerとCellTypistについて解説します。

decoupler

decouplerは、クラスタリングしたのデータに対して、それぞれのクラスターの遺伝子の発現量と、データベースの細胞の発現量を比較します。そこで統計的な検定を行い、データベースの中でもっとも類似した細胞タイプを割り当てます(加えて、他のグループよりも有意に類似度が高い細胞タイプを選ぶ)。RのSingleRとほぼ同じ手順で行えるために容易に使えるのが利点です。また、次元削減によるクラスタリングに基づいているため、結果の解釈が容易です。

参考:

https://github.com/saezlab/decoupler-py/tree/main/decoupler

・utils_anndataのrank_sources_groups関数
・method_oraのora関数

CellTypist

CellTypistは組織に局在する免疫細胞を解析するために作られたライブラリであり、免疫細胞の細かいサブクラスまでアノテーション出来る点について優れています。次元削減・クラスタリング前に行うため、これらの手法に依存せずにアノテーションできま、より客観的に細胞名を評価できます。

クラスタリングするのデータに対して、遺伝子の発現量を重み付けして和を取り、シグモイド関数に代入した値としてスコアを付けます。:

$$
\text{細胞タイプAのスコア} = sigmoid(\sum_{i\space for\space 全ての遺伝子} w_i^A * \text{(遺伝子iの発現量)})
$$

データベースの全ての細胞タイプ(それぞれが対応する重みwをもつ)について、このスコアを計算します。このスコアが最小のものとして、細胞タイプを決定します。これは遺伝子の発現量を空間にプロットした時の、wで定まる超平面との”距離”を測ることに相当します。重み付けのパラメータwは、クラスタリング済みのデータを用いて事前に最適化されています。

参考: C. Domínguez Conde et al. ,Cross-tissue immune cell analysis reveals tissue-specific features in humans.Science376,eabl5197(2022).DOI:10.1126/science.abl519
のSupplementary Materials p.27 Cell type prediction

decouplerとCellTypistの実装方法


以上をpythonでコーディングしてみます。

解析に用いるデータファイルを、「#データ読み込み」のdata_dirに指定してください。

以下ではSeurat Clustering Tutorialで用いられているデータセットを使います。データは以下からダウンロードできます。

https://cf.10xgenomics.com/samples/cell/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz

ダウンロードしたのちに解凍し、(google colabで実行する場合はgoogle driveにアップし)、フォルダのパスをdata_dirに指定して下さい:

data_dir = "???\\filtered_gene_bc_matrices\\hg19" 

コードの流れ

全体の流れ

  1. クオリティコントロール
  2. 正規化
  3. Scaling
  4. 次元削減・クラスタリング

CellTypistでは正規化した後に、遺伝子発現量から細胞名をラベリングします。ラベル済みの細胞が二次元へと次元削減されクラスタリングされます。一方で、decouplerは次元削減された後に、それぞれのクラスターで高発現している遺伝子マーカーを用いて細胞名が決定されます。

libraryのinstallとimport

#CellTypistに必要
!pip install celltypist

#decouplerに必要
!pip install decoupler
!pip install omnipath

#umapに必要
!pip3 install igraph
!pip3 install leidenalg
!pip install python-igraph louvain

#umapに必要
!pip3 install igraph
!pip3 install leidenalg
!pip install python-igraph louvain

# ScanpyとAnnData
!pip install scanpy anndata

from google.colab import drive
drive.mount('/content/drive')

import scanpy as sc
import anndata as ad
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.io

import celltypist
from celltypist import models

import decoupler as dc


データの読み込み

# Google Drive上のデータパスを指定
data_dir = '/content/drive/MyDrive/???' #???はデータを置いたフォルダのpathを指定してください

barcodes_path = f'{data_dir}/barcodes.tsv'
features_path = f'{data_dir}/features.tsv'
matrix_path = f'{data_dir}/matrix.mtx'

# バーコード、特徴量、マトリックスの読み込み
barcodes = pd.read_csv(barcodes_path, header=None)
features = pd.read_csv(features_path, header=None, sep='\\t')
matrix = scipy.io.mmread(matrix_path)

# AnnDataオブジェクトの作成
adata = sc.AnnData(X=matrix.T.tocsr())
adata.obs_names = barcodes[0].values #細胞名 obs.indexとおなじ
adata.var_names = features[1].values #遺伝子名 var.indexとおなじ


前処理

# 遺伝子の重複を取り除く
new_data = adata.to_df()
new_data = new_data.groupby(new_data.columns, axis=1).sum() 
adata =  sc.AnnData(X=new_data.values)
adata.obs_names = new_data.index
adata.var_names = new_data.columns

# Quality Control

def QC(adata):
    # ミトコンドリア遺伝子の発現割合を計算してobsに追加
    percent_mt = adata[:,mt_genes].X.sum(axis=1) / adata.X.sum(axis=1) * 100
    # 各細胞におけるユニークな遺伝子の数(非ゼロの遺伝子数)
    nFeature_RNA = (adata.X > 0).sum(axis=1)
    # 各細胞における全てのRNA分子の総数
    nCount_RNA = adata.X.sum(axis=1)

    df_violin_QC =\\
    pd.DataFrame({"nFeature_RNA":nFeature_RNA, "nCount_RNA":nCount_RNA, "percent_mt":percent_mt},\\
                index = adata.obs_names)
    return df_violin_QC
    
df_violin_QC = QC(adata)

adata_QC = adata[(200< df_violin_QC['nFeature_RNA'])\\
                 & (df_violin_QC['nFeature_RNA'] < 5000)\\
                 & (df_violin_QC['percent_mt'] < 15)]

Violin plot (不要・optional)

Violin Plotを使ってデータを可視化した場合は以下を実行する。これを飛ばしても問題ない。

violin_adata = sc.AnnData(df_violin_QC)
# プロット1: バイオリンプロット
sc.pl.violin(violin_adata, ["nFeature_RNA", "nCount_RNA", "percent_mt"], jitter=0.4, multi_panel=True)}}}

# プロット2: 散布図
corr_nCount_percent_mt = np.corrcoef(df_violin_QC['nCount_RNA'], df_violin_QC['percent_mt'])[0,1]
corr_nCount_nFeature_RNA = np.corrcoef(df_violin_QC['nCount_RNA'], df_violin_QC['nFeature_RNA'])[0, 1]

sc.pl.scatter(violin_adata, x='nCount_RNA', y='percent_mt', title=f'nCount_RNA vs percent_mt (corr: {corr_nCount_percent_mt:.2f})')
sc.pl.scatter(violin_adata, x='nCount_RNA', y='nFeature_RNA', title=f'nCount_RNA vs nFeature_RNA (corr: {corr_nCount_nFeature_RNA:.2f})')


前処理 つづき

# 正規化
sc.pp.normalize_total(adata_QC, target_sum=1e4)
sc.pp.log1p(adata_QC) # celltypistはlog1pで変換されていることが前提になっている。

# 高可変性遺伝子の検出
sc.pp.highly_variable_genes(adata_QC, flavor='seurat_v3', n_top_genes=2000)

# 高変動遺伝子のマスクを作成
highly_variable_mask = adata_QC.var['highly_variable']
# 高変動遺伝子のみをスケーリング
adata_highly_variable = adata_QC[:, highly_variable_mask].copy()
sc.pp.scale(adata_highly_variable)

高変動遺伝子の可視化(不要・Optional)

# 上位10個の高変動遺伝子を取得
top10 = adata_QC.var[adata_QC.var['highly_variable']].sort_values(by='highly_variable_rank').index[:10]

# プロットの作成
plt.figure(figsize=(10, 6))
ax = sc.pl.highly_variable_genes(adata_QC, show=False)

# 上位10個の遺伝子にラベルを付ける
for gene in top10:
    idx = adata_QC.var.index.get_loc(gene)
    ax.annotate(gene, (adata_QC.var['means'][idx], adata_QC.var['variances'][idx]),
                fontsize=7, color='red', ha='right')

plt.show()

スケーリング・次元削減

# CellTypistはスケーリング・次元削減前のデータを用いるので、ここで保存する。
# スケーリング前のデータを保存
adata_QC.layers['before_DimRed'] = adata_QC.X.copy()

# スケーリングされた高変動遺伝子のデータを元のデータに戻す
adata_QC[:, highly_variable_mask].X = adata_highly_variable.X

# PCAの実行
sc.tl.pca(adata_QC,svd_solver='arpack')

# UMAPの実行
sc.pp.neighbors(adata_QC, n_pcs=20)  # UMAPの前に隣接グラフを作成する必要があります
sc.tl.umap(adata_QC)

# 隣接グラフの構築
sc.pp.neighbors(adata_QC, n_pcs=10)

# クラスタリングの実行
sc.tl.leiden(adata_QC, resolution=0.5)

# UMAPプロットの作成
sc.pl.umap(adata_QC, color='leiden', legend_loc='on data', title='UMAP with Clusters', frameon=False)

#decouplerはクラスタリング後のデータを用いるので、ここで保存する。
adata_clustered = adata_QC.copy()


アノテーション

decoupler


adata_decoupler = adata_clustered.copy()

markers = dc.get_resource('PanglaoDB')

# 細胞マーカーをロードする
markers = dc.get_resource('PanglaoDB')
# markers = markers[markers['human'] & markers['canonical_marker'] & (markers['human_sensitivity'] > 0.5)]
markers = markers[~markers.duplicated(['cell_type', 'genesymbol'])]

# ORAを実行する
dc.run_ora(mat=adata_decoupler, net=markers, source='cell_type', target='genesymbol', min_n=3, verbose=True,use_raw=False)

# ORAのスコアを保存
acts = dc.get_acts(adata_decoupler, obsm_key='ora_estimate')
acts_v = acts.X.ravel()
max_e = np.nanmax(acts_v[np.isfinite(acts_v)])
acts.X[~np.isfinite(acts.X)] = max_e

#ORAスコアを元にクラスターごとに細胞タイプのランキングを作る
df_decoupler = dc.rank_sources_groups(acts, groupby='leiden', reference='rest', method='t-test_overestim_var')

# ランキング1位の細胞タイプを取り出す
annotation_dict = df_decoupler.groupby('group').head(1).set_index('group')['names'].to_dict()

# アノテーション
adata_decoupler.obs['cell_type'] = [annotation_dict[clust] for clust in adata_decoupler.obs['leiden']]

# UMAPで可視化
sc.pl.umap(adata_decoupler, color='cell_type')


CellTypist

adata_CellTypist= adata_QC.copy()
adata_CellTypist.X = adata_QC.layers['before_DimRed'].copy()

# modelをアップデート
models.download_models(force_update = True)

# モデルのインスタンス生成。免疫細胞を低解像度にアノテーションするモデルを設定
model = models.Model.load(model = 'Immune_All_Low.pkl')

# アノテーションの実行
predictions = celltypist.annotate(adata_CellTypist, model = 'Immune_All_Low.pkl', majority_voting = True)

# アノテーション結果の追加

adata_CellTypist = predictions.to_adata()

# アノテーション結果の保存
adata.write('annotated_data.h5ad')

# スケーリングされた高変動遺伝子のデータを元のデータに戻す
adata_CellTypist[:, highly_variable_mask].X = adata_highly_variable.X

# PCAの実行
sc.tl.pca(adata_CellTypist,svd_solver='arpack')

# UMAPの実行
sc.pp.neighbors(adata_CellTypist, n_pcs=20)  # UMAPの前に隣接グラフを作成する必要があります
sc.tl.umap(adata_CellTypist)

# 隣接グラフの構築
sc.pp.neighbors(adata_CellTypist, n_pcs=10)

# クラスタリングの実行
sc.tl.leiden(adata_CellTypist, resolution=0.5)
# UMAPプロットの作成
# sc.pl.umap(adata_CellTypist, color='predicted_labels', legend_loc='on data', title='UMAP with Annotations', frameon=False)

sc.pl.umap(adata_CellTypist, color='predicted_labels', title='UMAP with Annotations', frameon=False)


実行結果

dcecouper

decouplerによるアノテーション

CellTypist

CellTypistによるアノテーション

考察・比較

decoupler v.s. SingleR

decouplerのアノテーションとRのSingleRのアノテーションを比較しました。おおまかに単球・B細胞・T細胞・NK細胞の4つに分かれていることについては一致しています。T細胞とNK細胞がアフリカ大陸っぽい形の島を作り、B細胞と単球は孤島になっています。しかし、MDSCやナイーブなど、未分化の細胞に分類されていたり、γδ T細胞や主細胞が表れている部分で結果がかなり異なっています。

CellTypist

CellTypistについて、まずは細胞数が70以上のグループのみを取り出しました。(それ以外のグループでは細胞数が20以下でした。)。一番最後に、これを出力したコードを記載してます。

明確な境界をもたずに細胞のグループが重なっていることが目立ちますが、よくみると単球・B細胞・T細胞・NK細胞の4つに分かれている点について一致しています。ここから、SingleRと同じ結果を得られるうえに、CellTypistのほうが細かいアノテーションが出来ることが分かります。

decouplerではT細胞の島(北アフリカ)がナイーブTとメモリーTに分かれています。CellTypistについても、naive helper T(黄色)は全体に分布してますが、naive cytotoxic T cell(ピンク)は左側で memory/effector helper T cell (青)は右側により多く分布しています。この点については、decouplerに近い結果になっています。しかし、naive B cellやMDSC、主細胞などは無く、SingleRにより近い結果になりました。

更に、細胞数が20~5のレアなグループについて、表示してみます。

細胞数が少ない物についても、おおよそ単球・B細胞・T細胞・NK細胞のサブクラスが、同じ位置で分布していることが分かります。更に、これを用いれば細胞数が少ないレアな集団を見つけられるかもしれません。例えば、この図では巨核球Megakaryocytes/血小板が単球の近くに島を持つことが分かります。

decouplerやSingleRでは近くマクロファージ・単球のグループとして分類されています。これは次元削減した後にアノテーションをしているため、同じグループにクラスタリングされてしまったことが原因です。クラスタリング前に遺伝子の発現量を直接用いてクラスタリングをする方法では、クラスタリングとは独立にアノテーションを出来る為、少ない細胞集団を見つけいるのに適しています。

コードの解説


libraryのimportとpathの指定については説明を省きます。

解凍した後に、それぞれの.tarファイルがおいてあるディレクトリ(フォルダ)を指定していれば問題ないと思います。h5adなど、他の形式のファイルの場合でも同様にAnnDataとしてadataを定義すれば大丈夫だと思います。

# バーコード、特徴量、マトリックスの読み込み
barcodes = pd.read_csv(barcodes_path, header=None)
features = pd.read_csv(features_path, header=None, sep='\\t')
matrix = scipy.io.mmread(matrix_path)

# AnnDataオブジェクトの作成
adata = sc.AnnData(X=matrix.T.tocsr())
adata.obs_names = barcodes[0].values #細胞名 obs.indexとおなじ
adata.var_names = features[1].values #遺伝子名 var.indexとおなじ


barcodesは細胞を識別する番号をもつカラムを一つ持つdata frameです。featuresはカラム0が遺伝子ID、カラム1が遺伝子名になっています。疎行列であるmatrixから作ったAnnDataについて細胞名(.obs_names)と遺伝子名(.var_names)を設定します。

# 遺伝子の重複を取り除く
new_data = adata.to_df()
new_data = new_data.groupby(new_data.columns, axis=1).sum() 
adata =  sc.AnnData(X=new_data.values)
adata.obs_names = new_data.index
adata.var_names = new_data.columns

AnnDataはaxis=0が細胞でaxis=1が遺伝子を記録しています。遺伝子が重複している場合があるので、axis=1方向で和を取って、adataを上書きします。例えば、遺伝子Aが複数記録されていて、発現量が2,3,5なら、発現量10の遺伝子Aとして記録されます。

def QC(adata):
		mt_genes = adata.var_names.str.startswith('MT-')
    # ミトコンドリア遺伝子の発現割合を計算してobsに追加
    percent_mt = adata[:,mt_genes].X.sum(axis=1) / adata.X.sum(axis=1) * 100
    # 各細胞におけるユニークな遺伝子の数(非ゼロの遺伝子数)
    nFeature_RNA = (adata.X > 0).sum(axis=1)
    # 各細胞における全てのRNA分子の総数
    nCount_RNA = adata.X.sum(axis=1)

    df_violin_QC =\\
    pd.DataFrame({"nFeature_RNA":nFeature_RNA, "nCount_RNA":nCount_RNA, "percent_mt":percent_mt},\\
                index = adata.obs_names)
    return df_violin_QC
    
df_violin_QC = QC(adata)

adata_QC = adata[(200< df_violin_QC['nFeature_RNA'])\\
                 & (df_violin_QC['nFeature_RNA'] < 5000)\\
                 & (df_violin_QC['percent_mt'] < 15)]

QC関数はクオリティコントロールを行うのに必要な量を与えられたAnnDataから計算します。

  • percent_mtについて: 細胞ごとに、発現している遺伝子の総量に対してMT-と名の付く遺伝子の発現量の割合を計算します。それが15以下の細胞だけをadata_QCで取り出します。
  • nFeature_RNAについて: 細胞ごとに発現している遺伝子の種類を計算し、それが200以上5000以下の細胞だけをadata_QCに取り出します。
    • nCount_RNAについて:
    細胞ごとに発現量の合計を求めています。
# 正規化
sc.pp.normalize_total(adata_QC, target_sum=1e4)
sc.pp.log1p(adata_QC) # celltypistはlog1pで変換されていることが前提になっている。

# 高可変性遺伝子の検出
sc.pp.highly_variable_genes(adata_QC, flavor='seurat_v3', n_top_genes=2000)

# 高変動遺伝子のマスクを作成
highly_variable_mask = adata_QC.var['highly_variable']
# 高変動遺伝子のみをスケーリング
adata_highly_variable = adata_QC[:, highly_variable_mask].copy()
sc.pp.scale(adata_highly_variable)

正規化では発現量の合計が10000になるように細胞の発現量を何倍かします。その後、それぞれの発現量をx→log(1+x)なる変換をします。

高変動遺伝子の検出ではScanPyをもちいて、細胞ごとの発現量に大きなばらつき(分散)がある遺伝子の上位2000個を計算しています。AnnDataのうち、その部分のだけを取り出し、平均0分散1になるように標準化します。

# CellTypistはスケーリング・次元削減前のデータを用いるので、ここで保存する。
# スケーリング前のデータを保存
adata_QC.layers['before_DimRed'] = adata_QC.X.copy()

# スケーリングされた高変動遺伝子のデータを元のデータに戻す
adata_QC[:, highly_variable_mask].X = adata_highly_variable.X

CellTypistでは正規化した直後のデータをインプットにするので、この時点でのデータを保存しておきます。AnnDataにはlayerというメソッドがあり、ここに名前を付けて保存する事が出来ます。

decouplerでは次元削減まで行います。スケーリングしたAnnDataで上書きすることで元のデータの方もスケーリングします。

# PCAの実行
sc.tl.pca(adata_QC,svd_solver='arpack')

# UMAPの実行
sc.pp.neighbors(adata_QC, n_pcs=20)  # UMAPの前に隣接グラフを作成する必要があります
sc.tl.umap(adata_QC)

# 隣接グラフの構築
sc.pp.neighbors(adata_QC, n_pcs=10)

# クラスタリングの実行
sc.tl.leiden(adata_QC, resolution=0.5)

# UMAPプロットの作成
sc.pl.umap(adata_QC, color='leiden', legend_loc='on data', title='UMAP with Clusters', frameon=False)

#decouplerはクラスタリング後のデータを用いるので、ここで保存する。
adata_clustered = adata_QC.copy()

PCA, UMAPによる次元削減を行った後にleidenでクラスタリングを行っています。すべてScanPyを用いています。decouplerでは次元削減後のデータを用いるので、最後まで次元削減した後に、データを保存します。

decouplerのアノテーション

adata_decoupler = adata_clustered.copy()

markers = dc.get_resource('PanglaoDB')

# 細胞マーカーをロードする
markers = dc.get_resource('PanglaoDB')
# markers = markers[markers['human'] & markers['canonical_marker'] & (markers['human_sensitivity'] > 0.5)]
markers = markers[~markers.duplicated(['cell_type', 'genesymbol'])]

まずは細胞名を決めるための参照先となるデータをロードします。

# ORAを実行する
dc.run_ora(mat=adata_decoupler, net=markers, source='cell_type', target='genesymbol', min_n=3, verbose=True,use_raw=False)

# ORAのスコアを保存
acts = dc.get_acts(adata_decoupler, obsm_key='ora_estimate')
acts_v = acts.X.ravel()
max_e = np.nanmax(acts_v[np.isfinite(acts_v)])
acts.X[~np.isfinite(acts.X)] = max_e

decouplerのrun_oraはOver-Representation Analysisを実行します。matに与えられたデータとnetのデータを用いてフィッシャーの正確確率検定を行い、p値を求めます。このp値の-log(p)がora_estimateとして保存されます。np.nanmaxで無限大を除いた時の最大値を求め、無限大の値を最大値で補完します。以降このactsを使って細胞名を決定します。

詳しい解説

markersの細胞タイプとadata_decouplerの細胞の二つを比較したときに、遺伝子の発現量が独立しているかをフィッシャーの正確確率検定で検定しています。つまり、ORAの値-log(p)が大きいほど、markersの細胞タイプとadata_decouplerの細胞の遺伝子の発現には相関があるという事になり、同じ細胞であることが推察されます。

最終的にactsは細胞(obs)×細胞名(var)のAnnDataになり、ORAスコアを記録します。.obsはクラスタリングを記録する列をもつdata frameで、.varにはmarkersの細胞名をインデックスにする空のdata frameです。

#ORAスコアを元にクラスターごとに細胞タイプのランキングを作る
df_decoupler = dc.rank_sources_groups(acts, groupby='leiden', reference='rest', method='t-test_overestim_var')

# ランキング1位の細胞タイプを取り出す
annotation_dict = df_decoupler.groupby('group').head(1).set_index('group')['names'].to_dict()

actsのORAスコアを使って細胞名を決定します。あるグループとそれ以外の両者でt検定を行い、あるグループのORAスコアが有意に高いかを決定します。その検定のt統計量が最大となる細胞名としてグループの細胞名を決定します。つまり、他のグループと比較して有意に類似している細胞タイプとしてアノテーションします。

rank_sources_groupsはactsに記録されているデータを使って、同じグループに属する細胞のORAスコアの平均値と、そのグループ以外での平均値を計算します。ある細胞タイプについて、ORAスコアの平均値が他のグループより優位に高いかを検定します。その検定におけるp値やt統計量をdf_decouplerに記録します。

df_decouplerはgroup, names, statisticなどのカラムを持つdata frameです。groupはクラスタリングされたときの番号を記録します。namesにはmarkersに含まれるすべての細胞名が記録されています。あるグループが特定の細胞タイプに分類される可能性の高さ(ORAスコア)を記録しています。

# アノテーション
adata_decoupler.obs['cell_type'] = [annotation_dict[clust] for clust in adata_decoupler.obs['leiden']]

# UMAPで可視化
sc.pl.umap(adata_decoupler, color='cell_type')

CellTypistのアノテーション

adata_CellTypist= adata_QC.copy()
adata_CellTypist.X = adata_QC.layers['before_DimRed'].copy()

スケーリングする前のデータを用意します。AnnDataのlayerに保存していたデータでデータadata.CellTypist.Xを上書きします。こうすることで、発現量マトリクスだけは元に戻しつつobsやvarメソッドは同じ値を持ってこれます。

# modelをアップデート
models.download_models(force_update = True)

# モデルのインスタンス生成。免疫細胞を低解像度にアノテーションするモデルを設定
model = models.Model.load(model = 'Immune_All_Low.pkl')

# アノテーションの実行
predictions = celltypist.annotate(adata_CellTypist, model = 'Immune_All_Low.pkl', majority_voting = True)

# アノテーション結果の追加

adata_CellTypist = predictions.to_adata()

# アノテーション結果の保存
adata.write('annotated_data.h5ad')

事前学習済みのmodelを用いてアノテーションを実行する。

# スケーリングされた高変動遺伝子のデータを元のデータに戻す
adata_CellTypist[:, highly_variable_mask].X = adata_highly_variable.X

# PCAの実行
sc.tl.pca(adata_CellTypist,svd_solver='arpack')

# UMAPの実行
sc.pp.neighbors(adata_CellTypist, n_pcs=20)  # UMAPの前に隣接グラフを作成する必要があります
sc.tl.umap(adata_CellTypist)

# 隣接グラフの構築
sc.pp.neighbors(adata_CellTypist, n_pcs=10)

# クラスタリングの実行
sc.tl.leiden(adata_CellTypist, resolution=0.5)
# UMAPプロットの作成
# sc.pl.umap(adata_CellTypist, color='predicted_labels', legend_loc='on data', title='UMAP with Annotations', frameon=False)

sc.pl.umap(adata_CellTypist, color='predicted_labels', title='UMAP with Annotations', frameon=False)

スケーリング以降の手順を全て実行して可視化する

最後に


decouplerはデータベースの細胞タイプと発現量について検定を取っているだけなので原理上はクラスタリング前にも行う事ができるが、計算量が大きくなってしまいます。そのため、クラスタリング後に対してアノテーションをしたのかもしれないです。一方で、CellTypistは事前学習済みの機械学習モデルを使っていたので細胞ごとにアノテーションすることが可能だったのだと思います。

アノテーションの為の事前学習済みの機械学習モデルは他にも様々あるので試してみたいです。

おまけコード

CellTypistの細胞数が多いグループや少ないグループだけを取り出して可視化するコード

# 100以上の出現回数を持つ細胞タイプを取得
cell_type_counts = adata_CellTypist.obs['predicted_labels'].value_counts()
frequent_cell_types = cell_type_counts[cell_type_counts >= 70].index.tolist()
rare_cell_types = cell_type_counts[(cell_type_counts < 70) & (cell_type_counts >= 5)].index.tolist()

# 頻出する細胞タイプのインデックスを取得
selected_indices = adata_CellTypist.obs['predicted_labels'].isin(frequent_cell_types)

# 新しいAnnDataオブジェクトを作成(選択された細胞タイプ)
adata_CellTypist_selected = sc.AnnData(
    X=adata_CellTypist.X[selected_indices],
    obs=adata_CellTypist.obs.loc[selected_indices],
    var=adata_CellTypist.var,
    uns=adata_CellTypist.uns.copy(),
    obsm={'X_umap': adata_CellTypist.obsm['X_umap'][selected_indices]},
    varm=adata_CellTypist.varm,
    layers={key: layer[selected_indices] for key, layer in adata_CellTypist.layers.items()},
    obsp={key: connectivities[selected_indices][:, selected_indices] for key, connectivities in adata_CellTypist.obsp.items()}
)

# predicted_labelsのカテゴリを選択された細胞タイプに限定
adata_CellTypist_selected.obs['predicted_labels'] = pd.Categorical(
    adata_CellTypist_selected.obs['predicted_labels'],
    categories=frequent_cell_types
)

# 稀な細胞タイプのインデックスを取得

rare_indices = adata_CellTypist.obs['predicted_labels'].isin(rare_cell_types)

# 新しいAnnDataオブジェクトを作成(稀な細胞タイプ)
adata_CellTypist_rare = sc.AnnData(
    X=adata_CellTypist.X[rare_indices],
    obs=adata_CellTypist.obs.loc[rare_indices],
    var=adata_CellTypist.var,
    uns=adata_CellTypist.uns.copy(),
    obsm={'X_umap': adata_CellTypist.obsm['X_umap'][rare_indices]},
    varm=adata_CellTypist.varm,
    layers={key: layer[rare_indices] for key, layer in adata_CellTypist.layers.items()},
    obsp={key: connectivities[rare_indices][:, rare_indices] for key, connectivities in adata_CellTypist.obsp.items()}
)

# predicted_labelsのカテゴリを稀な細胞タイプに限定
rare_cell_types = list(set(adata_CellTypist_rare.obs['predicted_labels'].unique()) - set(frequent_cell_types))
adata_CellTypist_rare.obs['predicted_labels'] = pd.Categorical(
    adata_CellTypist_rare.obs['predicted_labels'],
    categories=rare_cell_types
)
# UMAPプロット
sc.pl.umap(adata_CellTypist_selected, 
           color='predicted_labels',
           title = "common cells", 
           frameon=False)

# UMAPプロット
sc.pl.umap(adata_CellTypist_rare, 
           color='predicted_labels', 
           title = "Rare Cells",
           frameon=False)
           
           
#少ない細胞数のグループを、多い細胞数のグループの上に重ねて表示する。
  # プロットの設定
fig, ax = plt.subplots(figsize=(12, 10))

# adata_CellTypist_selectedをグレーで表示(キャプションなし)
sc.pl.umap(adata_CellTypist_selected, 
           ax=ax, 
           color='predicted_labels', 
           palette=['lightgrey'] * len(adata_CellTypist_selected.obs['predicted_labels'].cat.categories),
           legend_loc=None,  # キャプションを非表示
           show=False, 
           title='')

# adata_CellTypist_rareをcolor='predicted_labels'で表示(キャプション付き)
sc.pl.umap(adata_CellTypist_rare, 
           ax=ax, 
           color='predicted_labels', 
        #    legend_loc='on data',  # キャプションをプロット内に表示
           legend_fontsize=8,
           legend_fontoutline=2,
           size=100,
           show=False, 
           title='')
plt.tight_layout()
plt.show()

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です