@nahisaho/satori 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -30
- package/package.json +1 -1
- package/src/.github/skills/scientific-advanced-imaging/SKILL.md +382 -0
- package/src/.github/skills/scientific-chembl-assay-mining/SKILL.md +509 -0
- package/src/.github/skills/scientific-data-submission/SKILL.md +357 -0
- package/src/.github/skills/scientific-deep-chemistry/SKILL.md +350 -0
- package/src/.github/skills/scientific-ensembl-genomics/SKILL.md +378 -0
- package/src/.github/skills/scientific-expression-comparison/SKILL.md +303 -0
- package/src/.github/skills/scientific-gpu-singlecell/SKILL.md +296 -0
- package/src/.github/skills/scientific-marine-ecology/SKILL.md +429 -0
- package/src/.github/skills/scientific-md-simulation/SKILL.md +315 -0
- package/src/.github/skills/scientific-model-organism-db/SKILL.md +329 -0
- package/src/.github/skills/scientific-nci60-screening/SKILL.md +307 -0
- package/src/.github/skills/scientific-perturbation-analysis/SKILL.md +297 -0
- package/src/.github/skills/scientific-plant-biology/SKILL.md +321 -0
- package/src/.github/skills/scientific-rrna-taxonomy/SKILL.md +379 -0
- package/src/.github/skills/scientific-scatac-signac/SKILL.md +300 -0
- package/src/.github/skills/scientific-scvi-integration/SKILL.md +344 -0
- package/src/.github/skills/scientific-string-network-api/SKILL.md +376 -0
- package/src/.github/skills/scientific-toxicology-env/SKILL.md +309 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-scatac-signac
|
|
3
|
+
description: |
|
|
4
|
+
scATAC-seq 解析スキル (Signac/SnapATAC2/episcanpy)。
|
|
5
|
+
ピークコーリング・モチーフ解析・Gene Activity スコア・
|
|
6
|
+
RNA+ATAC マルチモーダル統合 (WNN)。K-Dense: signac。
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scientific scATAC-seq / Signac
|
|
10
|
+
|
|
11
|
+
Signac / SnapATAC2 / episcanpy を活用した scATAC-seq (single-cell
|
|
12
|
+
ATAC-seq) 解析パイプラインを提供する。クロマチンアクセシビリティ
|
|
13
|
+
解析、モチーフエンリッチメント、マルチモーダル統合。
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- scATAC-seq データの前処理とピークコーリングを行うとき
|
|
18
|
+
- 転写因子モチーフのエンリッチメント解析を実行するとき
|
|
19
|
+
- Gene Activity スコアで scATAC を発現レベルで解釈するとき
|
|
20
|
+
- scRNA-seq + scATAC-seq のマルチモーダル統合を行うとき
|
|
21
|
+
- クロマチンアクセシビリティの細胞型特異的パターンを同定するとき
|
|
22
|
+
- エピゲノム (ヒストン修飾/DNAメチル化) とクロマチンを統合するとき
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
## 1. scATAC-seq 前処理 (SnapATAC2)
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import snapatac2 as snap
|
|
32
|
+
import anndata as ad
|
|
33
|
+
import numpy as np
|
|
34
|
+
import pandas as pd
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def scatac_preprocessing(fragment_file, genome="hg38",
|
|
39
|
+
min_tsse=5, min_fragments=1000):
|
|
40
|
+
"""
|
|
41
|
+
SnapATAC2 — scATAC-seq 前処理パイプライン。
|
|
42
|
+
|
|
43
|
+
Parameters:
|
|
44
|
+
fragment_file: str — fragments.tsv.gz パス
|
|
45
|
+
genome: str — リファレンスゲノム ("hg38", "mm10")
|
|
46
|
+
min_tsse: float — 最小 TSS enrichment スコア
|
|
47
|
+
min_fragments: int — 最小フラグメント数
|
|
48
|
+
"""
|
|
49
|
+
# フラグメントファイル読み込み
|
|
50
|
+
adata = snap.pp.import_data(
|
|
51
|
+
fragment_file,
|
|
52
|
+
chrom_sizes=snap.genome(genome),
|
|
53
|
+
sorted_by_barcode=False,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# QC メトリクス
|
|
57
|
+
snap.metrics.tsse(adata, snap.genome(genome))
|
|
58
|
+
snap.metrics.frag_size_distr(adata)
|
|
59
|
+
|
|
60
|
+
# フィルタリング
|
|
61
|
+
snap.pp.filter_cells(
|
|
62
|
+
adata,
|
|
63
|
+
min_counts=min_fragments,
|
|
64
|
+
min_tsse=min_tsse,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
print(f"scATAC preprocessing: {adata.n_obs} cells, "
|
|
68
|
+
f"TSS enrichment ≥ {min_tsse}")
|
|
69
|
+
return adata
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 2. ピークコーリング & Tile Matrix
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
def scatac_peak_calling(adata, genome="hg38", peak_method="macs2",
|
|
76
|
+
n_features=50000):
|
|
77
|
+
"""
|
|
78
|
+
scATAC-seq ピークコーリング & アクセシビリティマトリクス作成。
|
|
79
|
+
|
|
80
|
+
Parameters:
|
|
81
|
+
adata: AnnData — 前処理済み scATAC データ
|
|
82
|
+
genome: str — リファレンスゲノム
|
|
83
|
+
peak_method: str — "macs2" or "tile"
|
|
84
|
+
n_features: int — feature selection 上位数
|
|
85
|
+
"""
|
|
86
|
+
if peak_method == "tile":
|
|
87
|
+
# Tile matrix (500bp bins)
|
|
88
|
+
snap.pp.add_tile_matrix(adata, bin_size=500)
|
|
89
|
+
else:
|
|
90
|
+
# MACS2 peak calling (クラスタ別)
|
|
91
|
+
snap.pp.make_peak_matrix(adata)
|
|
92
|
+
|
|
93
|
+
# Feature selection
|
|
94
|
+
snap.pp.select_features(adata, n_features=n_features)
|
|
95
|
+
|
|
96
|
+
# 次元削減
|
|
97
|
+
snap.tl.spectral(adata)
|
|
98
|
+
snap.tl.umap(adata)
|
|
99
|
+
|
|
100
|
+
# クラスタリング
|
|
101
|
+
snap.pp.knn(adata)
|
|
102
|
+
snap.tl.leiden(adata)
|
|
103
|
+
|
|
104
|
+
n_clusters = adata.obs["leiden"].nunique()
|
|
105
|
+
print(f"Peak calling ({peak_method}): {n_clusters} clusters, "
|
|
106
|
+
f"{n_features} features")
|
|
107
|
+
return adata
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 3. モチーフエンリッチメント (chromVAR)
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
def motif_enrichment(adata, genome="hg38", motif_db="JASPAR2022"):
|
|
114
|
+
"""
|
|
115
|
+
chromVAR モチーフエンリッチメント解析。
|
|
116
|
+
|
|
117
|
+
Parameters:
|
|
118
|
+
adata: AnnData — ピークマトリクス付き scATAC データ
|
|
119
|
+
genome: str — リファレンスゲノム
|
|
120
|
+
motif_db: str — モチーフデータベース
|
|
121
|
+
"""
|
|
122
|
+
# モチーフスキャン
|
|
123
|
+
snap.tl.motif_enrichment(
|
|
124
|
+
adata,
|
|
125
|
+
motifs=motif_db,
|
|
126
|
+
genome=genome,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# クラスタ別差分モチーフ
|
|
130
|
+
cluster_motifs = {}
|
|
131
|
+
for cluster in adata.obs["leiden"].unique():
|
|
132
|
+
mask = adata.obs["leiden"] == cluster
|
|
133
|
+
# chromVAR deviation を取得
|
|
134
|
+
if "chromvar" in adata.obsm:
|
|
135
|
+
deviations = adata.obsm["chromvar"][mask].mean(axis=0)
|
|
136
|
+
top_idx = np.argsort(deviations)[-10:]
|
|
137
|
+
top_motifs = [adata.uns["motif_names"][i] for i in top_idx]
|
|
138
|
+
cluster_motifs[cluster] = top_motifs
|
|
139
|
+
|
|
140
|
+
results = []
|
|
141
|
+
for cluster, motifs in cluster_motifs.items():
|
|
142
|
+
for rank, motif in enumerate(motifs, 1):
|
|
143
|
+
results.append({
|
|
144
|
+
"cluster": cluster,
|
|
145
|
+
"rank": rank,
|
|
146
|
+
"motif": motif,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
df = pd.DataFrame(results)
|
|
150
|
+
print(f"Motif enrichment: {len(cluster_motifs)} clusters, "
|
|
151
|
+
f"{len(df)} motif-cluster pairs")
|
|
152
|
+
return df
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 4. Gene Activity スコア
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
def gene_activity_score(adata, genome="hg38", upstream=2000, body=True):
|
|
159
|
+
"""
|
|
160
|
+
Gene Activity スコア計算 — ATAC → 擬似発現量。
|
|
161
|
+
|
|
162
|
+
Parameters:
|
|
163
|
+
adata: AnnData — scATAC データ
|
|
164
|
+
genome: str — リファレンスゲノム
|
|
165
|
+
upstream: int — プロモーター上流距離 (bp)
|
|
166
|
+
body: bool — 遺伝子本体を含むか
|
|
167
|
+
"""
|
|
168
|
+
snap.pp.make_gene_matrix(
|
|
169
|
+
adata,
|
|
170
|
+
gene_anno=snap.genome(genome),
|
|
171
|
+
upstream=upstream,
|
|
172
|
+
include_body=body,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Gene Activity を .layers に保存
|
|
176
|
+
if hasattr(adata, "uns") and "gene_activity" in adata.uns:
|
|
177
|
+
gene_act = adata.uns["gene_activity"]
|
|
178
|
+
else:
|
|
179
|
+
gene_act = adata.X # Gene matrix mode
|
|
180
|
+
|
|
181
|
+
# 正規化
|
|
182
|
+
gene_act_norm = gene_act / gene_act.sum(axis=1, keepdims=True) * 10000
|
|
183
|
+
gene_act_log = np.log1p(gene_act_norm)
|
|
184
|
+
|
|
185
|
+
print(f"Gene activity: {gene_act_log.shape[1]} genes, "
|
|
186
|
+
f"{gene_act_log.shape[0]} cells")
|
|
187
|
+
return gene_act_log
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 5. RNA + ATAC マルチモーダル統合 (WNN)
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
import scanpy as sc
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def multimodal_wnn(adata_atac, adata_rna, n_neighbors=20):
|
|
197
|
+
"""
|
|
198
|
+
Weighted Nearest Neighbor (WNN) — RNA + ATAC 統合。
|
|
199
|
+
|
|
200
|
+
Parameters:
|
|
201
|
+
adata_atac: AnnData — scATAC データ (LSI 済み)
|
|
202
|
+
adata_rna: AnnData — scRNA データ (PCA 済み)
|
|
203
|
+
n_neighbors: int — 近傍数
|
|
204
|
+
"""
|
|
205
|
+
# 共通バーコード
|
|
206
|
+
common_bc = list(
|
|
207
|
+
set(adata_atac.obs_names) & set(adata_rna.obs_names)
|
|
208
|
+
)
|
|
209
|
+
atac_sub = adata_atac[common_bc].copy()
|
|
210
|
+
rna_sub = adata_rna[common_bc].copy()
|
|
211
|
+
|
|
212
|
+
print(f"Common barcodes: {len(common_bc)}")
|
|
213
|
+
|
|
214
|
+
# 各モダリティの kNN
|
|
215
|
+
sc.pp.neighbors(rna_sub, use_rep="X_pca", key_added="rna")
|
|
216
|
+
sc.pp.neighbors(atac_sub, use_rep="X_spectral", key_added="atac")
|
|
217
|
+
|
|
218
|
+
# WNN 統合 (muon)
|
|
219
|
+
try:
|
|
220
|
+
import muon as mu
|
|
221
|
+
mdata = mu.MuData({"rna": rna_sub, "atac": atac_sub})
|
|
222
|
+
mu.pp.neighbors(mdata, key="wnn")
|
|
223
|
+
mu.tl.umap(mdata, neighbors_key="wnn")
|
|
224
|
+
mu.tl.leiden(mdata, neighbors_key="wnn")
|
|
225
|
+
|
|
226
|
+
n_clusters = mdata.obs["leiden"].nunique()
|
|
227
|
+
print(f"WNN integration: {n_clusters} clusters")
|
|
228
|
+
return mdata
|
|
229
|
+
except ImportError:
|
|
230
|
+
print("muon not installed — falling back to concatenation")
|
|
231
|
+
# Fallback: 単純連結
|
|
232
|
+
combined = ad.concat([rna_sub, atac_sub], axis=1, merge="same")
|
|
233
|
+
sc.pp.neighbors(combined)
|
|
234
|
+
sc.tl.leiden(combined)
|
|
235
|
+
return combined
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## 6. scATAC-seq 統合パイプライン
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
def scatac_pipeline(fragment_file, rna_h5ad=None, genome="hg38",
|
|
242
|
+
output_dir="results"):
|
|
243
|
+
"""
|
|
244
|
+
scATAC-seq 統合解析パイプライン。
|
|
245
|
+
|
|
246
|
+
Parameters:
|
|
247
|
+
fragment_file: str — fragments.tsv.gz
|
|
248
|
+
rna_h5ad: str — scRNA-seq h5ad (マルチモーダル用, optional)
|
|
249
|
+
genome: str — リファレンスゲノム
|
|
250
|
+
output_dir: str — 出力ディレクトリ
|
|
251
|
+
"""
|
|
252
|
+
output_dir = Path(output_dir)
|
|
253
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
254
|
+
|
|
255
|
+
# 1) 前処理
|
|
256
|
+
adata = scatac_preprocessing(fragment_file, genome=genome)
|
|
257
|
+
|
|
258
|
+
# 2) ピーク & クラスタリング
|
|
259
|
+
adata = scatac_peak_calling(adata, genome=genome)
|
|
260
|
+
adata.write(output_dir / "scatac_clustered.h5ad")
|
|
261
|
+
|
|
262
|
+
# 3) モチーフ
|
|
263
|
+
motifs = motif_enrichment(adata, genome=genome)
|
|
264
|
+
motifs.to_csv(output_dir / "motif_enrichment.csv", index=False)
|
|
265
|
+
|
|
266
|
+
# 4) Gene Activity
|
|
267
|
+
gene_act = gene_activity_score(adata, genome=genome)
|
|
268
|
+
|
|
269
|
+
# 5) マルチモーダル統合
|
|
270
|
+
if rna_h5ad:
|
|
271
|
+
adata_rna = sc.read_h5ad(rna_h5ad)
|
|
272
|
+
mdata = multimodal_wnn(adata, adata_rna)
|
|
273
|
+
mdata.write(output_dir / "multimodal_wnn.h5mu")
|
|
274
|
+
|
|
275
|
+
print(f"scATAC pipeline: {output_dir}")
|
|
276
|
+
return adata
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## パイプライン統合
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
epigenomics-chromatin → scatac-signac → single-cell-genomics
|
|
285
|
+
(ChIP/ATAC bulk) (scATAC-seq) (scRNA 統合)
|
|
286
|
+
│ │ ↓
|
|
287
|
+
peak-annotation ──────────┘ spatial-transcriptomics
|
|
288
|
+
(ENCODE/ChIPAtlas) │ (Visium/MERFISH)
|
|
289
|
+
↓
|
|
290
|
+
gene-regulatory-network
|
|
291
|
+
(GRN 推定)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## パイプライン出力
|
|
295
|
+
|
|
296
|
+
| ファイル | 説明 | 次スキル |
|
|
297
|
+
|---------|------|---------|
|
|
298
|
+
| `results/scatac_clustered.h5ad` | クラスタリング済み scATAC | → single-cell-genomics |
|
|
299
|
+
| `results/motif_enrichment.csv` | モチーフエンリッチメント | → gene-regulatory-network |
|
|
300
|
+
| `results/multimodal_wnn.h5mu` | RNA+ATAC 統合 | → spatial-transcriptomics |
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-scvi-integration
|
|
3
|
+
description: |
|
|
4
|
+
scvi-tools シングルセル統合スキル。scVI 変分オートエンコーダ統合・
|
|
5
|
+
scANVI 半教師有りアノテーション・totalVI CITE-seq
|
|
6
|
+
RNA+タンパク質結合解析・SOLO ダブレット検出・潜在空間解析。
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scientific scVI Integration
|
|
10
|
+
|
|
11
|
+
scvi-tools を活用したシングルセル確率的モデルベース統合
|
|
12
|
+
パイプラインを提供する。scVI/scANVI/totalVI/SOLO による
|
|
13
|
+
バッチ統合、半教師有りアノテーション、マルチモーダル解析。
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- 複数バッチの scRNA-seq データを統合するとき (scVI)
|
|
18
|
+
- 半教師有りで細胞型アノテーションを転移するとき (scANVI)
|
|
19
|
+
- CITE-seq (RNA + ADT) データを結合解析するとき (totalVI)
|
|
20
|
+
- ダブレット (doublet) を検出・除去するとき (SOLO)
|
|
21
|
+
- 差次的発現を確率的にテストするとき
|
|
22
|
+
- 潜在空間を用いたクラスタリングを行うとき
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
## 1. scVI モデルセットアップ & 訓練
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import scvi
|
|
32
|
+
import scanpy as sc
|
|
33
|
+
import anndata as ad
|
|
34
|
+
import numpy as np
|
|
35
|
+
import pandas as pd
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def setup_scvi(adata, batch_key="batch", layer=None, n_latent=30,
|
|
39
|
+
n_hidden=128, n_layers=2):
|
|
40
|
+
"""
|
|
41
|
+
scVI 変分オートエンコーダのセットアップ & 訓練。
|
|
42
|
+
|
|
43
|
+
Parameters:
|
|
44
|
+
adata: AnnData — 入力データ (raw counts)
|
|
45
|
+
batch_key: str — バッチキー
|
|
46
|
+
layer: str — カウント格納レイヤー
|
|
47
|
+
n_latent: int — 潜在次元数
|
|
48
|
+
n_hidden: int — 隠れユニット数
|
|
49
|
+
n_layers: int — ニューラルネットレイヤー数
|
|
50
|
+
|
|
51
|
+
K-Dense: scvi-tools
|
|
52
|
+
"""
|
|
53
|
+
# scVI データ登録
|
|
54
|
+
scvi.model.SCVI.setup_anndata(
|
|
55
|
+
adata,
|
|
56
|
+
batch_key=batch_key,
|
|
57
|
+
layer=layer,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# モデル構築
|
|
61
|
+
model = scvi.model.SCVI(
|
|
62
|
+
adata,
|
|
63
|
+
n_latent=n_latent,
|
|
64
|
+
n_hidden=n_hidden,
|
|
65
|
+
n_layers=n_layers,
|
|
66
|
+
gene_likelihood="zinb", # zero-inflated negative binomial
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# 訓練
|
|
70
|
+
model.train(max_epochs=400, early_stopping=True)
|
|
71
|
+
|
|
72
|
+
# 潜在空間取得
|
|
73
|
+
latent = model.get_latent_representation()
|
|
74
|
+
adata.obsm["X_scVI"] = latent
|
|
75
|
+
|
|
76
|
+
print(f"scVI trained: {len(adata)} cells → {n_latent}D latent space")
|
|
77
|
+
print(f" Batches: {adata.obs[batch_key].nunique()}")
|
|
78
|
+
return model
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 2. scVI バッチ統合 & UMAP
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
def scvi_integration(adata, model, resolution=1.0):
|
|
85
|
+
"""
|
|
86
|
+
scVI 潜在空間でバッチ統合 & クラスタリング。
|
|
87
|
+
|
|
88
|
+
Parameters:
|
|
89
|
+
adata: AnnData — scVI 登録済みデータ
|
|
90
|
+
model: scvi.model.SCVI — 訓練済みモデル
|
|
91
|
+
resolution: float — Leiden 解像度
|
|
92
|
+
"""
|
|
93
|
+
# 潜在空間から近傍グラフ
|
|
94
|
+
sc.pp.neighbors(adata, use_rep="X_scVI")
|
|
95
|
+
sc.tl.umap(adata)
|
|
96
|
+
sc.tl.leiden(adata, resolution=resolution)
|
|
97
|
+
|
|
98
|
+
n_clusters = adata.obs["leiden"].nunique()
|
|
99
|
+
print(f"scVI integration: {n_clusters} clusters (resolution={resolution})")
|
|
100
|
+
return adata
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 3. scANVI 半教師有りアノテーション
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
def scanvi_annotation(adata, scvi_model, labels_key="cell_type",
|
|
107
|
+
unlabeled_category="Unknown", n_epochs=20):
|
|
108
|
+
"""
|
|
109
|
+
scANVI で半教師有り細胞型アノテーション転移。
|
|
110
|
+
|
|
111
|
+
Parameters:
|
|
112
|
+
adata: AnnData — scVI 登録済みデータ
|
|
113
|
+
scvi_model: scvi.model.SCVI — 訓練済み scVI
|
|
114
|
+
labels_key: str — 既知ラベルカラム
|
|
115
|
+
unlabeled_category: str — 未知ラベル値
|
|
116
|
+
n_epochs: int — 追加訓練エポック
|
|
117
|
+
"""
|
|
118
|
+
# scANVI = scVI + 半教師有り
|
|
119
|
+
scanvi_model = scvi.model.SCANVI.from_scvi_model(
|
|
120
|
+
scvi_model,
|
|
121
|
+
unlabeled_category=unlabeled_category,
|
|
122
|
+
labels_key=labels_key,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
scanvi_model.train(max_epochs=n_epochs)
|
|
126
|
+
|
|
127
|
+
# 予測ラベル
|
|
128
|
+
predictions = scanvi_model.predict()
|
|
129
|
+
adata.obs["scANVI_prediction"] = predictions
|
|
130
|
+
|
|
131
|
+
# 予測確率
|
|
132
|
+
soft_predictions = scanvi_model.predict(soft=True)
|
|
133
|
+
adata.obs["scANVI_confidence"] = soft_predictions.max(axis=1)
|
|
134
|
+
|
|
135
|
+
n_labeled = (adata.obs[labels_key] != unlabeled_category).sum()
|
|
136
|
+
n_unlabeled = (adata.obs[labels_key] == unlabeled_category).sum()
|
|
137
|
+
mean_conf = adata.obs["scANVI_confidence"].mean()
|
|
138
|
+
|
|
139
|
+
print(f"scANVI annotation:")
|
|
140
|
+
print(f" Labeled: {n_labeled}, Unlabeled: {n_unlabeled}")
|
|
141
|
+
print(f" Mean confidence: {mean_conf:.3f}")
|
|
142
|
+
return scanvi_model
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 4. totalVI CITE-seq 統合
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
def totalvi_citeseq(adata, protein_expression_obsm="protein_expression",
|
|
149
|
+
batch_key="batch", n_latent=20, n_epochs=400):
|
|
150
|
+
"""
|
|
151
|
+
totalVI で RNA + ADT (CITE-seq) 結合解析。
|
|
152
|
+
|
|
153
|
+
Parameters:
|
|
154
|
+
adata: AnnData — RNA counts + protein expression
|
|
155
|
+
protein_expression_obsm: str — ADT 格納 obsm キー
|
|
156
|
+
batch_key: str — バッチキー
|
|
157
|
+
n_latent: int — 潜在次元数
|
|
158
|
+
"""
|
|
159
|
+
scvi.model.TOTALVI.setup_anndata(
|
|
160
|
+
adata,
|
|
161
|
+
batch_key=batch_key,
|
|
162
|
+
protein_expression_obsm_key=protein_expression_obsm,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
model = scvi.model.TOTALVI(
|
|
166
|
+
adata,
|
|
167
|
+
n_latent=n_latent,
|
|
168
|
+
latent_distribution="normal",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
model.train(max_epochs=n_epochs, early_stopping=True)
|
|
172
|
+
|
|
173
|
+
# 潜在空間
|
|
174
|
+
latent = model.get_latent_representation()
|
|
175
|
+
adata.obsm["X_totalVI"] = latent
|
|
176
|
+
|
|
177
|
+
# タンパク質前景確率 (denoised)
|
|
178
|
+
_, protein_fore = model.get_normalized_expression(
|
|
179
|
+
n_samples=25,
|
|
180
|
+
return_mean=True,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
n_proteins = protein_fore.shape[1] if hasattr(protein_fore, 'shape') else 0
|
|
184
|
+
print(f"totalVI: {len(adata)} cells, {n_proteins} proteins")
|
|
185
|
+
print(f" Latent dim: {n_latent}")
|
|
186
|
+
return model
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## 5. SOLO ダブレット検出
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
def solo_doublet_detection(adata, scvi_model, threshold=0.5):
|
|
193
|
+
"""
|
|
194
|
+
SOLO でダブレット検出。
|
|
195
|
+
|
|
196
|
+
Parameters:
|
|
197
|
+
adata: AnnData — scVI 登録済みデータ
|
|
198
|
+
scvi_model: scvi.model.SCVI — 訓練済み scVI
|
|
199
|
+
threshold: float — ダブレット判定閾値
|
|
200
|
+
"""
|
|
201
|
+
solo_model = scvi.external.SOLO.from_scvi_model(scvi_model)
|
|
202
|
+
solo_model.train()
|
|
203
|
+
|
|
204
|
+
# ダブレット予測
|
|
205
|
+
predictions = solo_model.predict()
|
|
206
|
+
predictions["label"] = predictions.apply(
|
|
207
|
+
lambda x: "doublet" if x["doublet"] > threshold else "singlet",
|
|
208
|
+
axis=1,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
adata.obs["solo_doublet"] = predictions["label"].values
|
|
212
|
+
adata.obs["solo_score"] = predictions["doublet"].values
|
|
213
|
+
|
|
214
|
+
n_doublets = (adata.obs["solo_doublet"] == "doublet").sum()
|
|
215
|
+
doublet_rate = n_doublets / len(adata) * 100
|
|
216
|
+
|
|
217
|
+
print(f"SOLO doublet detection:")
|
|
218
|
+
print(f" Doublets: {n_doublets} ({doublet_rate:.1f}%)")
|
|
219
|
+
print(f" Singlets: {len(adata) - n_doublets}")
|
|
220
|
+
return predictions
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## 6. scVI 差次的発現
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
def scvi_differential_expression(model, adata, groupby="leiden",
|
|
227
|
+
group1="0", group2="1",
|
|
228
|
+
delta=0.25, batch_size=256):
|
|
229
|
+
"""
|
|
230
|
+
scVI による確率的差次的発現。
|
|
231
|
+
|
|
232
|
+
Parameters:
|
|
233
|
+
model: scvi.model.SCVI — 訓練済みモデル
|
|
234
|
+
adata: AnnData — 入力データ
|
|
235
|
+
groupby: str — グループカラム
|
|
236
|
+
group1: str — 比較グループ1
|
|
237
|
+
group2: str — 比較グループ2
|
|
238
|
+
delta: float — LFC 閾値
|
|
239
|
+
"""
|
|
240
|
+
de_results = model.differential_expression(
|
|
241
|
+
groupby=groupby,
|
|
242
|
+
group1=group1,
|
|
243
|
+
group2=group2,
|
|
244
|
+
delta=delta,
|
|
245
|
+
batch_size=batch_size,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# 有意な遺伝子フィルタ
|
|
249
|
+
sig_genes = de_results[
|
|
250
|
+
(de_results["is_de_fdr_0.05"])
|
|
251
|
+
& (de_results["lfc_mean"].abs() > delta)
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
sig_genes = sig_genes.sort_values("lfc_mean", ascending=False)
|
|
255
|
+
|
|
256
|
+
print(f"scVI DE: {group1} vs {group2}")
|
|
257
|
+
print(f" Significant genes: {len(sig_genes)}")
|
|
258
|
+
print(f" Up in {group1}: {(sig_genes['lfc_mean'] > 0).sum()}")
|
|
259
|
+
print(f" Up in {group2}: {(sig_genes['lfc_mean'] < 0).sum()}")
|
|
260
|
+
return sig_genes
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## 7. 統合パイプライン
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
def scvi_pipeline(adata_paths, batch_labels=None, labels_key="cell_type",
|
|
267
|
+
output_dir="results"):
|
|
268
|
+
"""
|
|
269
|
+
scVI → scANVI → SOLO 統合パイプライン。
|
|
270
|
+
|
|
271
|
+
Parameters:
|
|
272
|
+
adata_paths: list — AnnData ファイルパスリスト
|
|
273
|
+
batch_labels: list — バッチラベル
|
|
274
|
+
labels_key: str — 細胞型ラベルカラム
|
|
275
|
+
output_dir: str — 出力ディレクトリ
|
|
276
|
+
"""
|
|
277
|
+
from pathlib import Path
|
|
278
|
+
output_dir = Path(output_dir)
|
|
279
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
280
|
+
|
|
281
|
+
# 1) データ結合
|
|
282
|
+
adatas = []
|
|
283
|
+
for i, path in enumerate(adata_paths):
|
|
284
|
+
a = sc.read_h5ad(path)
|
|
285
|
+
a.obs["batch"] = batch_labels[i] if batch_labels else f"batch_{i}"
|
|
286
|
+
adatas.append(a)
|
|
287
|
+
|
|
288
|
+
adata = ad.concat(adatas, join="inner")
|
|
289
|
+
adata.obs_names_make_unique()
|
|
290
|
+
|
|
291
|
+
# 2) 前処理
|
|
292
|
+
sc.pp.highly_variable_genes(
|
|
293
|
+
adata, n_top_genes=2000, batch_key="batch", flavor="seurat_v3"
|
|
294
|
+
)
|
|
295
|
+
adata = adata[:, adata.var["highly_variable"]].copy()
|
|
296
|
+
|
|
297
|
+
# 3) scVI 訓練
|
|
298
|
+
scvi_model = setup_scvi(adata, batch_key="batch")
|
|
299
|
+
|
|
300
|
+
# 4) SOLO ダブレット除去
|
|
301
|
+
solo_results = solo_doublet_detection(adata, scvi_model)
|
|
302
|
+
adata = adata[adata.obs["solo_doublet"] == "singlet"].copy()
|
|
303
|
+
|
|
304
|
+
# 5) 再訓練 (ダブレット除去後)
|
|
305
|
+
scvi_model = setup_scvi(adata, batch_key="batch")
|
|
306
|
+
|
|
307
|
+
# 6) scANVI アノテーション
|
|
308
|
+
if labels_key in adata.obs.columns:
|
|
309
|
+
scanvi_model = scanvi_annotation(adata, scvi_model, labels_key=labels_key)
|
|
310
|
+
|
|
311
|
+
# 7) UMAP & クラスタリング
|
|
312
|
+
scvi_integration(adata, scvi_model)
|
|
313
|
+
|
|
314
|
+
# 保存
|
|
315
|
+
adata.write(output_dir / "integrated.h5ad")
|
|
316
|
+
scvi_model.save(str(output_dir / "scvi_model"))
|
|
317
|
+
|
|
318
|
+
print(f"Pipeline complete: {len(adata)} cells integrated")
|
|
319
|
+
return adata, scvi_model
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## パイプライン統合
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
single-cell-genomics → scvi-integration → spatial-transcriptomics
|
|
328
|
+
(scRNA-seq QC) (scVI/scANVI/totalVI) (Visium/MERFISH)
|
|
329
|
+
│ │ ↓
|
|
330
|
+
perturbation-analysis ────────┘ gene-expression
|
|
331
|
+
(Perturb-seq) │ (DEG/マーカー)
|
|
332
|
+
↓
|
|
333
|
+
expression-comparison
|
|
334
|
+
(Expression Atlas 比較)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## パイプライン出力
|
|
338
|
+
|
|
339
|
+
| ファイル | 説明 | 次スキル |
|
|
340
|
+
|---------|------|---------|
|
|
341
|
+
| `results/integrated.h5ad` | 統合 AnnData | → spatial-transcriptomics |
|
|
342
|
+
| `results/scvi_model/` | scVI 訓練済みモデル | → perturbation-analysis |
|
|
343
|
+
| `results/de_results.csv` | 差次的発現結果 | → gene-expression |
|
|
344
|
+
| `results/annotations.csv` | scANVI アノテーション | → expression-comparison |
|