イメージング型 空間トランスクリプトーム:転写産物レベルの QC ─ 2つの対照で故障箇所を切り分ける

Spatial transcriptome
📚 この記事について
細胞に切る前、まだ1行が1分子の段階で行う QC を扱います。対照には複数の種類があり、それぞれ別の失敗を測っています。その違いを使うと、問題が化学にあるのか撮像にあるのかを切り分けられます。
🔙 前の記事:イメージング型 空間トランスクリプトーム:データ読み込みとデータ構造
🔜 次の記事:イメージング型 空間トランスクリプトーム:セグメンテーション手法の選び方
📌 前提scRNA-seq解析:データの読み込みと品質管理(QC)前処理の全体像

空間トランスクリプトーム解析(spatial transcriptomics)のうちイメージング型(Xenium・CosMx・MERSCOPE など顕微鏡で1分子ずつ数える方式)は、scRNA-seq には存在しない QC の工程を持っています。細胞に切る前、まだ分子を扱っている段階で、「その分子は本当に存在したのか」を確かめる工程です。各社は、生体内に存在しないものを狙った対照をパネルに混ぜて出荷しており、それが光った割合が、そのままデータの信頼性を表します。

1. 対照は1種類ではない


図:対照は1種類ではない ─ それぞれ別の失敗を測る
図:対照は1種類ではない ─ それぞれ別の失敗を測る

装置によって呼び方は違いますが、対照はおおむね次の種類に分かれます。

種類 何か 光ったときに疑うもの
対照プローブ 生体内に存在しない配列を狙ったプローブ 化学(非特異的な結合)と 撮像(読み取り誤り)の両方
対照コードワード どのプローブにも対応しない符号 撮像・復号のみ(プローブが存在しないので化学ではありえない)
未使用コードワード パネル内のどのプローブも生成しない符号 同上
非推奨コードワード 解析パイプラインで使われない符号
💡 測る範囲が違うから、大小関係が決まっている
対照プローブは化学の失敗と読み取りの失敗の両方で光ります。対照コードワードは読み取りの失敗でしか光りません。そもそもそれを生む物理的なプローブが存在しないからです。したがって、対照プローブの率は、対照コードワードの率以上になります。10x のドキュメントも、この大小関係が常に成り立つことを明記しています。

2. 2つの率を並べると、故障箇所が分かる


大小関係が決まっているということは、その差の大きさが情報を持っているということです。

図:2つの対照を並べると、どこが壊れているか分かる
図:2つの対照を並べると、どこが壊れているか分かる
観察 疑うところ 確認すること
どちらも低い 問題なし そのまま進んでよい
対照コードワードが高い 撮像・復号の側 撮像の周期やチャネル。自家蛍光が乗っていないか
対照プローブだけが高い 化学の側 非特異的なハイブリダイゼーションやライゲーションの条件
一部の対照プローブだけが高い そのプローブ固有の問題 少数なら許容範囲のことが多い

10x のドキュメントも、この読み分けを明示しています。対照コードワードの率が高い場合は撮像の問題、たとえば復号の周期やチャネルに自家蛍光が乗っている可能性を挙げ、RNA 画像を確認するよう促しています。一方、対照プローブの品質とカウントがそろって高い場合は、アッセイの工程の問題、たとえば非特異的なプローブのハイブリダイゼーションやライゲーション条件を疑うよう述べています。

python
import numpy as np

# フィーチャごとの分子数を数える
cnt = tx["feature_name"].value_counts().compute()
names = cnt.index.astype(str)

# 対照を種類ごとに分ける(命名は装置によって違うので、必ず一覧を目で見る)
is_probe = names.str.contains("NegControlProbe|NegPrb|BLANK", case=False)
is_code  = names.str.contains("NegControlCodeword|NegCode", case=False)
is_unas  = names.str.contains("Unassigned|Deprecated", case=False)
is_gene  = ~(is_probe | is_code | is_unas)

# 率は「実シグナルに対する割合」として計算する
gene_n = cnt[is_gene].sum()
print("対照プローブ率  :", round(float(cnt[is_probe].sum() / gene_n) * 100, 4), "%")
print("対照コードワード率:", round(float(cnt[is_code].sum() / gene_n) * 100, 4), "%")
print("実シグナルの分子数:", int(gene_n))
⚠️ 率だけで判断しない
対照の率は分数です。分母は検出された実シグナルの量です。つまり、非特異的な結合が増えていなくても、実シグナルの検出が少ないだけで率は上がります。10x のドキュメントも、率が高い原因として「遺伝子の転写産物の検出が低いこと」を明示的に挙げています。率を見るときは、実シグナルの分子数も必ず一緒に見てください。率が高く、かつ分子数も少ないなら、それは背景ノイズの問題ではなく検出感度の問題です。

3. 品質スコアでふるう


個々の分子には、その読み取りがどれだけ確からしいかを表すスコアが付いています。Xenium ではこれを Q-Score と呼び、対照コードワードを使って較正されています。「対照が光った頻度から、どのくらい信用してよいかを逆算する」という設計です。

python
# 品質スコアの分布を見てから、閾値を決める
qv = tx["qv"].compute()
print(np.percentile(qv, [1, 5, 25, 50, 75]))

# 閾値ごとに、どれだけ残るかを確かめる
for thr in [15, 20, 30]:
    keep = (qv >= thr).mean()
    print(thr, "->", round(float(keep), 3))

# 既定の閾値を使うなら 20。装置の行列もこの水準で作られている
tx_q = tx[tx["qv"] >= 20]

# 対照そのものは、率を記録したうえで外す
ctrl_names = cnt[is_probe | is_code | is_unas].index.tolist()
tx_use = tx_q[~tx_q["feature_name"].isin(ctrl_names)]
print("解析に使う分子:", len(tx_use))
💡 既定の閾値は 20
Xenium の cell-feature matrix は、Q-Score 20 以上の分子だけで作られています。閾値を通らなかった分子は行列には入りませんが、転写産物のファイルには残っています。自分でセグメンテーションをやり直す場合は、この閾値を自分で当て直す必要があります。10x が公開している Baysor 向けの前処理スクリプトも、Q-Score 20 を既定値にしています。
⚠️ 閾値を上げれば良いわけではない
閾値を厳しくすれば偽陽性は減りますが、同時に本物の分子も落ちます。細胞あたりの分子数が減れば、細胞型を決める力も落ちます。既定より厳しくするなら、残る分子の割合と、対照の率がどう変わるかを両方見てから決めてください。

4. 対照を外す ─ ただし記録してから


対照は品質を測るための道具であって、遺伝子ではありません。クラスタリングや細胞型アノテーションに入れてはいけないので、解析の前に外します。

⚠️ 外す前に、必ず率を記録する
外してしまえば、あとから率を計算し直すことはできません。対照プローブ率・対照コードワード率・実シグナルの分子数の3つを、サンプルごとに記録しておいてください。複数サンプルを比べるとき、「このサンプルだけ結果が違うのは、品質の差ではないか」を検証する材料になります。実験ノートに書くのと同じ感覚で、解析ログに残します。

細胞レベルでも、対照の割合を見ておくと役に立ちます。装置の出力する細胞ごとの表には、対照のカウントが列として入っていることがあります。

python
# 細胞ごとの対照の割合も見る。外れ値の細胞が見つかる
obs = adata.obs
obs["ctrl_frac"] = (
    obs["control_probe_counts"] + obs["control_codeword_counts"]
) / obs["total_counts"].clip(lower=1)

print(obs["ctrl_frac"].describe())

# 対照の割合が高い細胞は、実シグナルがそもそも少ないことが多い
bad = obs["ctrl_frac"] > 0.05
print("該当細胞:", int(bad.sum()), "/ その細胞の分子数中央値:",
      float(obs.loc[bad, "total_counts"].median()))

対照の割合が極端に高い細胞は、多くの場合実シグナルがそもそも少ない細胞です。分母が小さいので割合が跳ね上がります。この段階では細胞を落とさず、細胞レベルの QCでまとめて判断するのが素直です。

5. 数字ではなく、組織の上で見る


ここまでは全体を1つの数字にまとめてきました。しかし全体が良好でも、組織の一部だけが壊れていることがあります

図:数字ではなく、組織の上で見て初めて分かること
図:数字ではなく、組織の上で見て初めて分かること
python
import matplotlib.pyplot as plt

# 分子の密度を、粗い格子に集計して地図にする
df = tx_use[["x", "y"]].compute()
Hh, xe, ye = np.histogram2d(df["x"], df["y"], bins=200)

plt.imshow(Hh.T, origin="lower", aspect="auto")
plt.colorbar(label="分子数")
# 見るべきもの
#   直線的な段差 -> 視野の連結の問題
#   丸く抜けた領域 -> 焦点や気泡
#   組織の形に沿った濃淡 -> 生物学(正常)

# 対照の分子だけを地図にすると、偽陽性の偏りが見える
dc = tx_q[tx_q["feature_name"].isin(ctrl_names)][["x", "y"]].compute()
Hc, _, _ = np.histogram2d(dc["x"], dc["y"], bins=[xe, ye])
plt.imshow((Hc / np.maximum(Hh, 1)).T, origin="lower")
# 特定の場所だけ偽陽性が高いなら、そこは撮像の問題
地図に見えるもの 疑うもの
直線的な段差、格子状の境目 視野の連結の問題
丸く抜けた領域 焦点の外れ、気泡、組織の欠け
局所的に分子が密すぎる領域 分離できずに取りこぼしている可能性
対照の割合が特定の場所だけ高い その領域の撮像の問題
組織の形に沿った濃淡 生物学(正常)
💡 対照だけの地図を作る
実シグナルの密度地図と、対照だけの密度地図を並べてください。対照が組織全体に一様に散らばっているなら、それは背景ノイズです。特定の場所に固まっているなら、そこは撮像の問題です。自家蛍光の強い領域(血管、色素、壊死巣)で起きやすい現象です。その領域を解析から外すか、少なくとも結果を解釈するときに考慮します。
⚠️ 壊れた領域は、切る前に外す
分子が足りない領域でセグメンテーションを行うと、そこの細胞は分子が足りないまま行列に入ります。あとから細胞レベルの QC で「分子数が少ない細胞」として気づくことはできますが、それが撮像の問題なのか生物学なのかは、その時点では分かりません。分子の段階で領域ごと外しておけば、この曖昧さは生じません。

6. 複数サンプルを比べるとき


サンプル間で結果が違うとき、それが生物学なのか品質の差なのかを見分ける必要があります。そのために、次の指標をサンプルごとにそろえて比べます。

  • 対照プローブ率 と 対照コードワード率
  • 実シグナルの分子数(総数、および面積あたり)
  • 品質スコアの分布
  • 分子密度の空間的な一様さ
⚠️ 品質の差は、バッチ補正では直らない
サンプルによって検出感度が違えば、細胞あたりの分子数も変わります。バッチ補正は発現空間での系統差を扱いますが、そもそも取れていない分子を取り戻すことはできません。感度の差が大きいサンプルは、統合するのではなく、別々に解析して結論を比べるほうが安全なこともあります。パネルの拡大が検出効率を下げうることも報告されており(Cervilla et al., Genome Biology, 2026)、パネルの違うデータを直接比べるときはとくに注意が要ります。

7. 転写産物レベル QC のチェックリスト


  • フィーチャの一覧を目で見て、対照の命名を確認したか
  • 対照プローブ率と対照コードワード率を別々に計算したか
  • 率と一緒に、実シグナルの分子数も見たか
  • 品質スコアの分布を見てから、閾値を決めたか
  • 自分で切り直すなら、フィルタを自分で当てたか
  • 外す前に率を記録したか
  • 分子密度を組織の上に描いて確認したか
  • 対照だけの密度地図を作って、偏りを見たか
  • 壊れた領域を、切る前に外したか

まとめ


  • イメージング型には、分子の段階で行う QC という scRNA-seq に無い工程がある。
  • 対照は1種類ではない。対照プローブは化学と撮像の両方対照コードワードは撮像・復号だけを測る。
  • 測る範囲が違うので、対照プローブ率 ≧ 対照コードワード率 が常に成り立つ。その差が、故障箇所を切り分ける手がかりになる。
  • コードワードが高い → 撮像を疑う(自家蛍光など)。プローブだけが高い → 化学を疑う(非特異的結合、反応条件)。
  • 率だけで判断しない。実シグナルが少ないだけでも率は上がる。分子数と併せて見る。
  • 品質スコアは対照コードワードで較正されている。既定の閾値は 20。厳しくすれば本物も落ちる。
  • 対照は外す。ただし外す前に率を記録する。
  • 全体の数字が良好でも、組織の一部だけが壊れていることがある。分子密度と対照の密度を、必ず地図にする。
  • 壊れた領域は切る前に外す。あとからでは原因が分からなくなる。

関連記事


参考文献


  • Janesick, A., Shelansky, R., Gottscho, A. D., et al. (2023). High resolution mapping of the tumor microenvironment using integrated single-cell, spatial and in situ analysis. Nature Communications, 14(1), 8353. doi:10.1038/s41467-023-43458-x
  • Cervilla, S., Grases, D., Perez, E., et al. (2026). A technical comparison of spatial transcriptomics platforms across six cancer types. Genome Biology, 27, 22. doi:10.1186/s13059-026-03937-y
  • Petukhov, V., Xu, R. J., Soldatov, R. A., et al. (2022). Cell segmentation in imaging-based spatial transcriptomics. Nature Biotechnology, 40(3), 345–354. doi:10.1038/s41587-021-01044-w
  • Marconato, L., Palla, G., Yamauchi, K. A., et al. (2025). SpatialData: an open and universal data framework for spatial omics. Nature Methods, 22(1), 58–62. doi:10.1038/s41592-024-02212-x
  • Palla, G., Spitzer, H., Klein, M., et al. (2022). Squidpy: a scalable framework for spatial omics analysis. Nature Methods, 19(2), 171–178. doi:10.1038/s41592-021-01358-2
  • Wolf, F. A., Angerer, P., & Theis, F. J. (2018). SCANPY: large-scale single-cell gene expression data analysis. Genome Biology, 19, 15. doi:10.1186/s13059-017-1382-0

コメント

タイトルとURLをコピーしました