🎯 この記事の結論
- やること:複数サンプルの「測定のクセ(バッチ効果)」だけを消し、本物の生物差は残す
- 必ず守る1つ:近傍グラフで
use_rep="X_pca_harmony"を指定(=補正結果を“使う”) - 注意:消しすぎ(過補正)に注意。Harmony が直すのは埋め込みで、発現量そのものではない
この記事のゴール
複数サンプルをまたいで、「測定のクセ(バッチ効果)」だけを消し、本物の生物学的な違いは残したデータを、軽量・普通のPCで動く Harmony で作ること。
💡 ポイント:同じ細胞型でも、サンプル(測定)ごとに発現パターンが系統的にずれます。このズレ(バッチ効果)だけを打ち消し、本物の細胞型の違いはそのまま残す——これが統合です。
0. バッチ効果ってなに? なぜ補正するの?
同じ細胞型でも、測定した日・試薬のロット・10x のチップなどが違うと、発現のパターンが系統的にずれます。これがバッチ効果です。
補正しないとどうなるか? 次元削減やクラスタリングで、「細胞型」ではなく「サンプル」でグループが割れてしまい、解釈が壊れます。
💡 統合のゴールは「混ぜる」と「残す」の両立
良い統合とは、次の2つを同時に満たすことです。
– 🔵 同じ細胞型は、サンプルをまたいで混ざる(バッチ補正)
– 🔴 違う細胞型・違う状態は、分かれたまま(生物差の保存)
この2つはトレードオフの関係です。手法ごとに「どちらをどれだけ重視するか」で性格が分かれます(Luecken et al., 2022)。
⚠️ 最大の落とし穴:消しすぎ(過補正)
バッチ補正を強くかけすぎると、本物の細胞型の違いや処理の効果まで消えます。確かに「そろった」けれど、サンプル間の違い(生物学的に意味のある差)も消えています。「WT と KO がきれいに重なった!」は統合の成功ではなく、差を消してしまった失敗かもしれません。だから統合後は必ず評価します(最終節)。
1. 準備:正規化・特徴選択(HVG)
統合の前に、共通の前処理をします。ここでは、細胞ごとに発現が大きく変動する遺伝子だけに絞る高変動遺伝子(HVG、highly variable genes=高変動遺伝子)選択を行います。seurat_v3 という方法は生(なま)のカウントを必要とするので、先に生カウントを別の場所へ退避してから選びます。
pythonimport numpy as np
import scanpy as sc
import anndata as ad
# combined: 前の記事(QC)で得たクリーンな AnnData(obs["batch"] にサンプル/群ラベル)
adata = combined.copy()
# 生カウントを保存(seurat_v3 は生カウントを要求するため)
adata.layers["counts"] = adata.X.copy()
# HVG を「生カウント」から seurat_v3 で選択(バッチ考慮)
sc.pp.highly_variable_genes(
adata,
n_top_genes=2000,
flavor="seurat_v3",
layer="counts",
batch_key="batch",
subset=False,
)
🔧 環境メモ:flavor="seurat_v3" は scanpy の HVG 選択アルゴリズムの名前で、内部で scikit-misc を使います(R や Seurat 本体は不要)。未導入なら pip install scikit-misc。
💡 なぜ遺伝子を絞るの?
遺伝子の大半は、どの細胞でもほぼ一定の発現で、細胞集団を見分ける手がかりになりません(シグナルよりノイズを足すだけ)。発現の変動が大きい遺伝子だけに絞ると、細胞集団の構造が見えやすくなり、計算も軽くなります。
2. Harmony で統合する
仕組み(やさしく)
Harmony は 主成分分析(PCA、principal component analysis:情報を圧縮して扱いやすくする手法)の空間で動きます。やっていることはシンプルで、
- 似た細胞をゆるくグループ分けし、
- 各グループの中で「サンプル(バッチ)ごとの偏り」を打ち消し、
- これを何度か繰り返してバッチ差をそろえる、
というものです(Korsunsky et al., 2019)。深層学習を使わない軽量・高速で、普通のPCで十分な方法です。
コード(正規化 → PCA → Harmony → 近傍 → クラスタリング)
まず正規化(公平に比較できるよう数値をそろえる)と対数変換をして、HVG に絞って PCA をかけます。
pythonimport scanpy as sc
# 正規化と対数変換
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
adata.raw = adata # 全遺伝子の正規化済みデータを退避
# HVG に絞ってスケーリング → PCA
adata_h = adata[:, adata.var.highly_variable].copy()
sc.pp.scale(adata_h, max_value=10)
sc.tl.pca(adata_h, n_comps=30)
次に Harmony を適用します。
pythonimport harmonypy
def harmony_integrate_robust(adata, batch_key, basis="X_pca", adjusted="X_pca_harmony"):
ho = harmonypy.run_harmony(adata.obsm[basis], adata.obs, [batch_key])
Z = ho.Z_corr
if Z.shape[0] != adata.n_obs: # 古い向き (PCs, cells) の場合は転置
Z = Z.T
adata.obsm[adjusted] = np.asarray(Z) # → (細胞数, PC数) で格納
return adata
harmony_integrate_robust(adata_h, "batch") # obsm["X_pca_harmony"] が作られる
# ★ 補正後の埋め込みで近傍グラフを作る(ここが最重要)
sc.pp.neighbors(adata_h, use_rep="X_pca_harmony", n_neighbors=15)
sc.tl.umap(adata_h)
sc.tl.leiden(adata_h, key_added="leiden_harmony",
flavor="igraph", n_iterations=2, directed=False, resolution=1.0)
sc.pl.umap(adata_h, color=["batch", "leiden_harmony"], ncols=2, frameon=False)
🔧 補足:なぜ自前のヘルパー関数?
harmonypy 2.0 では出力の向きが変わり、よく使われる sc.external.pp.harmony_integrate() が形状エラーで落ちることがあります。上の harmony_integrate_robust(run_harmony を直接呼ぶ版)なら、新旧どちらのバージョンでも動きます。
⚠️ 落とし穴①:Harmony の結果を「使い忘れる」(最重要)
sc.pp.neighbors() は、何も指定しないと X_pca(補正していないPCA)を使います。Harmony を回しても、use_rep="X_pca_harmony" を指定しなければ補正は一切反映されません。「Harmony したのにサンプルで割れる」原因のNo.1がこれです。補正した結果を指定して使う——ここだけは必ず守ってください。
⚠️ 落とし穴②:Harmony が直すのは「埋め込み」であって「発現量」ではない
Harmony が補正するのは PCA の低次元表現(埋め込み X_pca_harmony)です。カウントデータ(遺伝子発現量)そのものは変えていません。UMAP やクラスタリングは、この補正後の埋め込みから作られます。
👉 つまり「バッチ補正後の発現量」というものは存在しません。遺伝子発現量を使う解析(発現変動解析〔DEG〕など)は、補正後の埋め込みではなく、元の正規化済み発現(adata.raw など)を使う点に注意してください。
💡 なぜ regress_out を入れないの?
古い手順では、PCA の前に sc.pp.regress_out(["total_counts","pct_counts_mt"]) を挟むことがよくありました。しかし今のベストプラクティスでは、既定では行いません(Heumos et al., 2023)。技術指標の回帰除去は、本物の生物シグナルまで削るリスクがあり、効果も限定的だからです。
3. 統合できたかを評価する(消しすぎていないか)
「UMAP(uniform manifold approximation and projection:高次元データを2次元に配置する手法)できれいに重なった」だけでは不十分です。確認するのは2点:「同じ細胞型はサンプルをまたいで重なったか(バッチ差は消えたか)」と「違う細胞型の違いは残ったか(消しすぎていないか)」です。
python# ① バッチが混ざっているか(混ざっていれば技術差は除けている)
sc.pl.umap(adata_h, color="batch", frameon=False)
# ② 既知マーカーで細胞型が保たれているか(消えていないか)
known_markers = ["GeneA", "GeneB", "GeneC"] # 自分の系のマーカーに置換
sc.pl.umap(adata_h, color=known_markers, color_map="RdBu_r", frameon=False)
💡 数値で評価したいなら scib パッケージ(Python 完結なら scib-metrics)
目で見るのに加え、scib で数値化できます。代表的な指標は、
– バッチ混合(よく混ざったか):iLISI、kBET(高いほど良い)
– 生物保存(細胞型が残ったか):cLISI、ARI、NMI(既知ラベルとの一致度)
4. もう一方の選択肢:AI 手法の scVI(GPU 必須)
統合にはもう一つ、深層学習ベースの scVI という選択肢があります(詳細は別記事 サンプル統合:scVI(GPU必須のAI手法) で解説)。強いバッチ効果や大規模データに強く、この後のダブレット検出(SOLO)・アノテーション・マルチモーダル解析へ発展させやすい一方、実用上 GPU が必須です。
📌 どちらを選ぶ?(詳しくは 全体像の記事)
– 小〜中規模/生物シグナルが明瞭/GPU が無い・素早く回したい → 本記事の Harmony
– 大規模/強いバッチ効果/GPU が使え、発展解析に繋げたい → scVI(別記事)
⚠️ つまずきポイントまとめ
use_rep="X_pca_harmony"の指定忘れ(最重要)→ 補正が反映されず、サンプルで割れる。- Harmony は埋め込みを補正するだけ → 発現量を使う解析(DEG 等)は元の正規化発現を使う。
harmonypy 2.0の向き問題 →run_harmonyを直接呼ぶ堅牢版で回避。regress_outを入れない(今は既定で非推奨)。- 過補正(消しすぎ) → 「重なった=成功」ではない。必ず評価する。
まとめ
- 統合のゴールは「バッチ差を消し、生物学的な違いは残す」の両立。消しすぎ(過補正)に注意し、必ず評価する。
- Harmony は軽量・普通のPCで完結。ポイントは3つ:
neighborsでuse_rep="X_pca_harmony"を必ず指定/補正されるのは埋め込み(発現量解析は元の発現を使う)/regress_outは使わない。 - GPU が使え、強いバッチ・大規模・発展性を求めるなら scVI(別記事) を検討。
次の記事:ダブレットの検出 ― scrublet(GPU不要) — 2細胞が1つに化けた人工物を、普通のPCで完結する古典的手法で検出・除去します。
関連記事
- 📖 scRNA-seqの前処理 入門 ─ 全体像と進め方 … このシリーズの全体像(ハブ)
- 📖 データの読み込みと品質管理(QC) … 本記事の前段(入力データ)
- 📖 サンプル統合:scVI(AI・GPU) … 統合のもう一方の選択肢
- 📖 ダブレット検出:scrublet(古典) … 次の記事
- 📖 AnnDataのデータ構造 / 📖 scvi-tools の全体像 … 土台・AI手法の概観
参考文献
- Korsunsky, I., Millard, N., Fan, J., et al. (2019). Fast, sensitive and accurate integration of single-cell data with Harmony. Nature Methods, 16, 1289–1296. doi:10.1038/s41592-019-0619-0
- Luecken, M. D., Büttner, M., Chaichoompu, K., et al. (2022). Benchmarking atlas-level data integration in single-cell genomics. Nature Methods, 19, 41–50. doi:10.1038/s41592-021-01336-8
- Heumos, L., Schaar, A. C., Lance, C., et al. (2023). Best practices for single-cell analysis across modalities. Nature Reviews Genetics, 24, 550–572. doi:10.1038/s41576-023-00586-w
- Single-cell best practices(統合の章): https://www.sc-best-practices.org/cellular_structure/integration.html


コメント