@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.
@@ -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 |