CRISPRガイドRNAのオンターゲット/オフターゲットスコアリング — BRCA1 KO実験前に100件の候補を30秒でランク付けする
CRISPR-Cas9実験の成功率は、ガイドRNA(gRNA)の選択段階で既に半分が決まります。優れたgRNAを選択すれば、目的の遺伝子を効率的に切断(オンターゲット効率)し、他の遺伝子を誤って切断すること(オフターゲット回避)を防ぐことができます。しかし、各gRNA候補を合成し、実験室で検証するには、数日間の時間と数万ウォンがかかります。本稿では、K562細胞株におけるBRCA1遺伝子KOという現実的なシナリオを用いて、複数の深層学習スコアラーを組み合わせた、100〜500件のgRNA候補を30秒でランク付けする、本格的なエンドツーエンドパイプラインを構築し、上位5件のみを実際の合成に進めます。
📚 前提条件(強く推奨)
これは、AI×生物学の高度なテーマです。読み始める前に、以下のDryBenchのトピックを学習することをお勧めします。
前提知識がない場合、本稿は実際のコードから直接進み、1D CNN/RNNベースのシーケンススコアラー、ワンホットエンコーディング、または事前学習済みモデルの読み込みと実行の実践的なパターンに関する学習原理を再説明しないため、理解が難しくなる可能性があります。
DryBenchで学んだこと
DryBench ai-native #2では、ニューラルネットワークがCNNフィルターを使用して入力シーケンス上の局所的なパターンをキャプチャし、RNNが順序依存性を学習することを学びました。#13では、HuggingFaceエコシステムが、事前学習済みモデルの重み、トークナイザー、推論APIを標準化し、数行のコードで読み込みを可能にすることを見てきました。
CRISPR gRNAスコアリングは、これらの2つの原理が現実世界で驚くほど近い範囲で出会う場所です。非常に短い入力(20 bpプロトスペーサー+ 3 bp PAM、合計23 bp = 92次元のワンホット)に対して、比較的小さな1D CNNによって学習されたコンテキスト特徴は、実際の実験効率と相関関係があり、ピアソンの相関係数ρ ≥ 0.7です。本稿は、複数のスコアラーを組み合わせて、その予測力を高め、オフターゲットリスクを定量化し、それを実際の実験設計の最適化ツールに変えるパイプラインです。
ハードコアな問題定義
現実世界のシナリオ:K562におけるBRCA1 KO
DNA損傷修復研究のために、SpCas9-HF1システムを使用して、K562(ヒト慢性骨髄性白血病細胞株)でBRCA1(乳がん1)遺伝子をノックアウトします。BRCA1には24個のエクソンがあり、合計のmRNAは約7.2 kb、コーディング領域は約5.6 kbです。KO実験の成功率と再現性を確保するために、以下の要件があります。
- 複数のエクソン(5'側エクソンを優先し、ノンスセンス媒介分解を避ける)にわたってgRNA候補を検索する。
- 各gRNA候補について、オンターゲット効率とオフターゲットリスクを定量化する。
- 上位5件のみを合成する(Twist Bioscience · IDTなど、それぞれ約30〜100米ドル)。
- 測定された効率をフォローアップ実験で検証する(T7E1アッセイまたはアンプリコンの深層シーケンシング)。
CRISPR gRNA設計の基本的なトレードオフ
- オンターゲット効率:gRNAがターゲット部位をどの程度効率的に切断するか。スコアは0〜1で、高いほど良い。トレーニングデータは通常、K562 · HEK293T · Jurkatなどの細胞株で数千件のgRNAをアッセイすることから得られます。
- オフターゲット安全性:ゲノム内に類似の配列が存在する場所と、それらの場所での副作用がどの程度深刻か。スコアは0〜1で、高いほど安全。
- PAM隣接制約:SpCas9の標準的なPAMは
NGGです。特別なバリアントには、NG(SpCas9-NG)、NGN(SpG)、NRN(SpRY)などがあります。 - BE(ベースエディター)編集ウィンドウ:ベースエディティングは、プロトスペーサー内の特定のウィンドウ(例:4〜8 nt)を編集します。ウィンドウの位置とノックアウトのターゲットコドンとの位置関係が重要です。
既存のアプローチの範囲と本稿の位置
- 経験則(Doench 2014、Xu 2015):PAMの周りの核酸組成、GC含有量、二次構造などの手動で設計された特徴に基づいて、ロジスティック回帰を使用します。ピアソンの相関係数0.4〜0.5。
- 初期の1D CNN(DeepCRISPR 2018):21 bpのコンテキストを学習します。ρ 0.6〜0.65 [1]。
- CRISPRon(Xiang 2021):コンテキストを認識するCNN + 回帰。ρ 0.72 [2]。
- DeepHF(Wang 2019):SpCas9-HF1 · xCas9バリアントに特化。ρ 0.75 [3]。
- CRISPRon-BE(Kim 2022):ベースエディティング(ABE · CBE)に特化。ρ 0.68〜0.75 [4]。
- BE-Hive(2020):BE編集ウィンドウの予測に優れている。
- PRIDICT(2023):プライムエディティングpegRNAの設計。
- ファウンデーションモデルアプローチ(実験的):DNABERT · Nucleotide TransformerでgRNAを埋め込み、ダウンストリームヘッドを適用します。
本稿では、CRISPRon(オンターゲット)+ DeepHF(バリアントCas9)+ CRISPRon-BE(ベースエディティング)+ CFD/MIT(オフターゲット)という4つのスコアラーを組み合わせ、いずれかの単一のスコアラーのバイアスを相殺し、CRISPORリモートAPIを介してゲノム全体のオフターゲットスキャンを追加します。
本稿のターゲットメトリクス
- すべての24個のBRCA1エクソン(両鎖、NGG PAM)にわたって200〜500件のgRNA候補を自動的に抽出する。
- Lipinskiスタイルのフィルター(GC 30〜70%、TTTTを回避、自己相補性を回避)。
- 複数のスコアラーを使用したアンサンブルスコアラーによる、各候補のオンターゲット・オフターゲットスコア(合計処理時間は30秒〜2分)。
- CRISPORを介したゲノム全体のオフターゲットスキャン(上位20件のみ)。
- 最終的なランキングを自動レポートとして作成する(ラボで注文可能なMarkdown + CSV形式、スペーサー+ PAM +スコア行列+オフターゲットヒットの概要を含む)。
ツールスタックとインフラストラクチャ要件
| ツール | 役割 | ライセンス |
|---|---|---|
Bioconductor crisprScore (R) | 統合スコアリングフレームワーク (CRISPRon · DeepHF · CRISPRon-BE · CFD など) | Artistic-2.0 |
| CRISPRon-BE (RTH-tools GitHub) | ベース編集に特化したスコアリングツール (ABE · CBE) | GPL v3 |
| rpy2 (Python↔R ブリッジ) | Python から R の crisprScore を呼び出す | GPL v2+ |
| Biopython | FASTA · GenBank の解析、PAM スキャン、リバースコンプリメント | Biopython License |
CRISPOR リモート API またはローカルの crispritz | ゲノム全体でのオフターゲットスキャン | 学術利用は無料 · GPL |
Bioconductor BSgenome.Hsapiens.UCSC.hg38 | ヒトゲノムリファレンス | Artistic-2.0 |
| pandas · matplotlib | 結果テーブル · 可視化 | BSD |
インフラストラクチャ要件:
- 小さなコンシューマーGPU (RTX 4060 以上を推奨。CPU でのフォールバックも可能ですが、5〜10 倍遅くなります)。
- R 4.3+ + Bioconductor 3.18+ (
crisprScore·BSgenome.Hsapiens.UCSC.hg38·crisprBase·crisprDesign)。 - Python 3.10+ + PyTorch 2.0+ + rpy2 3.5+。
- 8 GB 以上の RAM。ディスク:ヒトゲノムリファレンスは約 3 GB、CRISPRon の重みは約 100 MB です。
学習者が再現するための推定コスト: API コストは発生しません (完全にローカル、または無料の CRISPOR リモートを使用)。500 個の gRNA をスコアリングするための GPU 時間は約 1~3 分です(初回はモデルの読み込みを含めて 5 分)。
パイプラインの実際の実装
全体のフロー:
ステップ1. BRCA1エクソン配列の抽出
NCBI RefSeqまたはhg38エクソン座標(UCSC Genome Browser)からBRCA1 mRNA(NM_007294)を取得し、エクソン配列を抽出します。
from dataclasses import dataclassfrom pathlib import Pathfrom typing import Iterator
import requestsfrom Bio import SeqIO, Entrezfrom Bio.Seq import Seq
Entrez.email = "your@email.example" # NCBI required
@dataclassclass ExonRegion: """遺伝子の1つのエクソンの情報。""" gene_name: str exon_number: int chromosome: str strand: str # "+" または "-" start_hg38: int # 0ベース end_hg38: int length: int sequence: str coding_frame: int | None # UTRの場合はNone、それ以外の場合は0/1/2
def fetch_refseq_mrna(refseq_id: str = "NM_007294") -> str: """NCBIからRefSeq mRNA配列を取得(例:BRCA1 = NM_007294)。""" with Entrez.efetch(db="nucleotide", id=refseq_id, rettype="fasta", retmode="text") as h: record = SeqIO.read(h, "fasta") return str(record.seq).upper()
def fetch_gene_exons_ucsc(gene_symbol: str, assembly: str = "hg38") -> list[ExonRegion]: """UCSC Genome Browser REST APIを介して遺伝子エクソン座標を取得。 実際には、ローカルでGENCODEアノテーションGTFを解析する方がはるかに安定しています。 """ # UCSC Table Browserの代替案:MyGene.info API resp = requests.get( f"https://mygene.info/v3/query?q=symbol:{gene_symbol}&species=human&fields=exons", timeout=30, ) resp.raise_for_status() hits = resp.json().get("hits", []) if not hits: return [] exons_info = hits[0].get("exons", []) if not exons_info: return [] canonical = exons_info[0] # カノニカルな転写物 exon_regions = [] for i, (start, end) in enumerate(canonical.get("position", [])): # UCSCから実際の配列を取得(DAS APIまたはローカルBSgenome) seq = _fetch_ucsc_dna(canonical["chr"], start, end, assembly) exon_regions.append(ExonRegion( gene_name=gene_symbol, exon_number=i + 1, chromosome=canonical["chr"], strand=canonical.get("strand", "+"), start_hg38=start, end_hg38=end, length=end - start, sequence=seq, coding_frame=None, # 詳細な決定にはCDS座標が必要 )) return exon_regions
def _fetch_ucsc_dna(chrom: str, start: int, end: int, assembly: str = "hg38") -> str: """UCSC DAS APIを介して特定の領域の配列を返します。""" url = f"https://api.genome.ucsc.edu/getData/sequence?genome={assembly};chrom={chrom};start={start};end={end}" resp = requests.get(url, timeout=30) resp.raise_for_status() return resp.json().get("dna", "").upper()ステップ2. PAMスキャン・候補gRNA抽出
SpCas9の標準PAMはNGGです。反対鎖もスキャンします(プロトスペーサーの逆相補配列)。
from dataclasses import dataclassfrom Bio.Seq import Seq
@dataclassclass GRNACandidate: gene_name: str exon_number: int protospacer: str # 20 bpのターゲット配列 pam: str # 3 bpのPAM strand: str # "+" または "-" exon_position: int # エクソン配列内の開始位置(0ベース) genome_position: int # hg38ゲノム座標(0ベース) context_50bp: str # 15 bp上流 + gRNA + PAM + 15 bp下流 = 53 bp(CRISPRonなどのトレーニング入力)
def scan_pam_ngg(sequence: str, gene_name: str, exon: ExonRegion) -> list[GRNACandidate]: """NGG PAM + 20 bpプロトスペーサー候補を両方の鎖から抽出します。""" candidates = [] seq_plus = sequence.upper() seq_minus = str(Seq(seq_plus).reverse_complement()) def _scan(strand_seq: str, strand_label: str, seq_len: int) -> None: for i in range(20, seq_len - 3): pam = strand_seq[i:i+3] if pam[1:3] != "GG": continue protospacer = strand_seq[i-20:i] if "N" in protospacer: continue context_start = max(0, i - 35) context_end = min(seq_len, i + 18) context = strand_seq[context_start:context_end] # ゲノム座標を計算します(鎖に依存した方向の反転) if strand_label == "+": genome_pos = exon.start_hg38 + (i - 20) else: genome_pos = exon.end_hg38 - i candidates.append(GRNACandidate( gene_name=gene_name, exon_number=exon.exon_number, protospacer=protospacer, pam=pam, strand=strand_label, exon_position=i - 20, genome_position=genome_pos, context_50bp=context, )) _scan(seq_plus, "+", len(seq_plus)) _scan(seq_minus, "-", len(seq_minus)) return candidates
def scan_gene_all_exons(exons: list[ExonRegion]) -> list[GRNACandidate]: all_cands = [] for exon in exons: all_cands.extend(scan_pam_ngg(exon.sequence, exons[0].gene_name, exon)) return all_candsステップ3. 基本的な物理化学的フィルター
CRISPR gRNAは、一般的に以下の条件が満たされた場合に安定して合成され、機能します。
- GC含有量30〜70%:極端な値はミスマッチ許容度を低下させます。
- 4つ以上の連続したT(TTTT)を避ける:RNAポリメラーゼIIIターミネーションシグナル。
- 自己相補性を避ける:自己二次構造が形成されると、sgRNA-Cas9結合が妨げられます。
- 最初のヌクレオチドルール:U6プロモーターは、Gで始まるgRNAを好みます。
from Bio.SeqUtils import GCfrom Bio.Seq import Seq
def check_self_complementarity(seq: str, min_stem: int = 4) -> bool: """自己相補性のチェック:5'末端の4 bpが3'末端の4 bpの逆相補配列である場合、ヘアピンのリスクがあります。""" if len(seq) < min_stem * 2: return False stem_5 = seq[:min_stem] stem_3 = seq[-min_stem:] return stem_5 == str(Seq(stem_3).reverse_complement())
def filter_grna_physicochemical( candidates: list[GRNACandidate], min_gc: float = 30.0, max_gc: float = 70.0, max_poly_t: int = 3, prefer_g_start: bool = True, check_hairpin: bool = True,) -> list[GRNACandidate]: """物理化学的フィルター。""" filtered = [] for c in candidates: # GC含有量 gc = GC(c.protospacer) if not (min_gc <= gc <= max_gc): continue # ポリ-T if "T" * (max_poly_t + 1) in c.protospacer: continue # G開始(推奨) if prefer_g_start and c.protospacer[0] != "G": # 完全には除外されませんが、優先順位を下げます(ここで通過します)。 pass # ヘアピン if check_hairpin and check_self_complementarity(c.protospacer): continue filtered.append(c) return filteredステップ4. RのcrisprScoreをPythonから呼び出す(rpy2ブリッジ)
crisprScoreパッケージは、ほとんどの標準スコアラーを統合します:CRISPRon・DeepHF・CRISPRon-BE・CFD・Hsu-Zhang・MIT。
import numpy as npimport rpy2.robjects as rofrom rpy2.robjects.packages import importrfrom rpy2.robjects.vectors import StrVector
class CrisprScoreEnsemble: """RのcrisprScoreから複数のスコアラーをPythonで呼び出します。"""
def __init__(self): # Rパッケージをロードします(BiocManager::installは事前にRコンソールで必要)。 self.crisprscore = importr("crisprScore") self.base = importr("base")
def crispron(self, contexts: list[str]) -> list[float]: """CRISPRonオンターゲットスコア。入力:各項目は30 bpコンテキストです(4 bp上流 + 20 bpプロトスペーサー + 3 bp PAM + 3 bp下流)。""" r_input = StrVector(contexts) try: result = self.crisprscore.getCRISPRonScores(r_input) return list(result) except Exception as e: print(f"CRISPRon failed: {e}") return [float("nan")] * len(contexts)
def deep_hf(self, protospacers_with_pam: list[str], enzyme: str = "WT") -> list[float]: """DeepHFスコア。入力:各23 bp(20 bpプロトスペーサー + 3 bp PAM)。enzyme:'WT'、'ESP'、'HF'。""" r_input = StrVector(protospacers_with_pam) try: result = self.crisprscore.getDeepHFScores(r_input, enzyme=enzyme) return list(result) except Exception as e: print(f"DeepHF failed: {e}") return [float("nan")] * len(protospacers_with_pam)
def crispron_be(self, contexts: list[str], editor: str = "ABE8e") -> list[float]: """CRISPRon-BEベース編集効率。 エディターオプション:'ABE8e'(アデニン)、'BE4max'(シトシン)など。 """ r_input = StrVector(contexts) try: result = self.crisprscore.getCRISPRonBEScores(r_input, editor=editor) return list(result) except Exception as e: print(f"CRISPRon-BE failed: {e}") return [float("nan")] * len(contexts)
def cfd_off_target(self, protospacer: str, target_dna: str) -> float: """切断頻度決定(Doench 2016 [5])。""" try: result = self.crisprscore.getCFDScores( StrVector([protospacer]), StrVector([target_dna]), ) return float(list(result)[0]) except Exception: return 0.0
def mit_hsu_zhang(self, protospacer: str, target_dna: str) -> float: """MIT/Hsu-Zhangオフターゲットスコア(位置依存ミスマッチペナルティ)。""" try: result = self.crisprscore.getMITScores( StrVector([protospacer]), StrVector([target_dna]), ) return float(list(result)[0]) except Exception: return 0.0R環境設定(記事本文の参照コマンド):
# Rコンソール内if (!require("BiocManager", quietly = TRUE)) install.packages("BiocManager")BiocManager::install(c( "crisprScore", "crisprDesign", "BSgenome.Hsapiens.UCSC.hg38", "crisprBase"))ステップ5. CRISPORリモートオフターゲットスキャン
CFDはローカルの単一のペアワイズスコアです。ゲノム全体のオフターゲットスキャンには、CRISPORが標準的です[6]。ローカルのcrisporitz代替手段もあります。
import requests
def query_crispor_offtargets( protospacer: str, genome: str = "hg38", max_mismatches: int = 4, timeout: int = 300,) -> list[dict]: """CRISPORリモートAPIを介してゲノム全体のオフターゲットスキャン。 返されるもの:[{genome_pos、mismatches、cfd_score、gene}、...] """ # 実際のエンドポイント:http://crispor.tefor.net/crispor.py # これは概念的なラッパーです。実際のAPIはHTMLを返すため、解析が必要です。 try: resp = requests.get( "http://crispor.tefor.net/crispor.py", params={ "seq": protospacer, "org": genome, "pam": "NGG", "showAllOTs": "1", }, timeout=timeout, ) resp.raise_for_status() # 実際のCRISPORレスポンスはHTML/TSVです。ここでは例として解析を示します。 # 実際には、BeautifulSoupまたはローカルのcrispor CLIを使用してTSVを取得します。 return _parse_crispor_html(resp.text) except Exception as e: print(f"CRISPORオフターゲットスキャンに失敗しました({protospacer}):{e}") return []
def _parse_crispor_html(html: str) -> list[dict]: """CRISPOR HTMLレスポンスを解析します(概念的なスタブ、実際にはBeautifulSoupを使用します)。""" return [] # 本番環境で実装します。
def compute_off_target_summary(off_targets: list[dict]) -> dict: """オフターゲットスキャン結果の概要。""" if not off_targets: return { "n_offtargets_mm4": 0, "max_cfd_score": 0.0, "high_risk_genes": [], "genome_safety_score": 1.0, } max_cfd = max(ot["cfd_score"] for ot in off_targets) high_risk = [ot["gene"] for ot in off_targets if ot["cfd_score"] > 0.5 and ot["gene"]] # 全体的な安全性:最大CFDを反転します(0 = 危険、1 = 安全) safety = 1.0 - max_cfd return { "n_offtargets_mm4": len(off_targets), "max_cfd_score": max_cfd, "high_risk_genes": high_risk[:5], "genome_safety_score": safety, }ステップ6. アンサンブルランキング
複数のスコアラーからの結果の重み付きアンサンブル。ベンダー/タスクごとに最適な重みは、実験的な検証データで微調整できます。
import pandas as pdimport numpy as np
DEFAULT_WEIGHTS = { "crispron": 0.30, "deep_hf": 0.25, "crispron_be": 0.10, # BEシナリオではない場合 "off_target_safety": 0.35,}
def build_ensemble_ranking( candidates: list[GRNACandidate], on_scores: dict[str, list[float]], off_target_summaries: list[dict], weights: dict[str, float] = DEFAULT_WEIGHTS,) -> pd.DataFrame: """統合されたスコアラーランキングDataFrame。""" df = pd.DataFrame([{ "gene": c.gene_name, "exon": c.exon_number, "protospacer": c.protospacer, "pam": c.pam, "strand": c.strand, "genome_position": c.genome_position, "context_50bp": c.context_50bp, } for c in candidates]) for name, scores in on_scores.items(): df[f"on_{name}"] = scores df["off_n_mm4"] = [s["n_offtargets_mm4"] for s in off_target_summaries] df["off_max_cfd"] = [s["max_cfd_score"] for s in off_target_summaries] df["off_safety"] = [s["genome_safety_score"] for s in off_target_summaries] df["off_high_risk_genes"] = [ ";".join(s["high_risk_genes"]) for s in off_target_summaries ] # 重み付け平均(正規化後) def _normalize(col): c = df[col].fillna(df[col].median()) return (c - c.min()) / (c.max() - c.min() + 1e-8) final = np.zeros(len(df)) if "on_crispron" in df.columns: final += weights.get("crispron", 0.0) * _normalize("on_crispron") if "on_deep_hf" in df.columns: final += weights.get("deep_hf", 0.0) * _normalize("on_deep_hf") if "on_crispron_be" in df.columns: final += weights.get("crispron_be", 0.0) * _normalize("on_crispron_be") final += weights["off_target_safety"] * df["off_safety"].fillna(0.5) df["final_score"] = final return df.sort_values("final_score", ascending=False).reset_index(drop=True)ステップ7. 自動ラボ注文レポート
上位5つのgRNAをMarkdown + CSV形式で出力し、Twist Bioscience、IDTなどに注文できるようにします。
from pathlib import Pathimport pandas as pd
def generate_lab_report( ranked: pd.DataFrame, output_md: Path, output_csv: Path, top_k: int = 5,) -> None: """ラボで注文可能なレポートを生成します。""" top = ranked.head(top_k) lines = [ f"# 上位{top_k} gRNA候補の注文レポート", "", f"ターゲット遺伝子:**{top.iloc[0]['gene']}**", f"生成日時:(パイプラインの実行時に記入)", "", "## 概要テーブル", "", "| 順位 | エクソン | プロトスペーサー(20 bp) | PAM | 鎖 | オン(CRISPRon) | オン(DeepHF) | オフターゲット安全性 | 最終スコア |", "|:----:|:----:|:-------------------:|:---:|:------:|:-------------:|:-----------:|:----------:|:-----:|", ] for i, row in top.iterrows(): lines.append( f"| {i+1} | {row['exon']} | `{row['protospacer']}` | {row['pam']} | {row['strand']} | " f"{row.get('on_crispron', float('nan')):.3f} | {row.get('on_deep_hf', float('nan')):.3f} | " f"{row['off_safety']:.3f} | **{row['final_score']:.3f}** |" ) lines += [ "", "## 注文配列(5'→3')", "", "プロトスペーサー配列の先頭にGがない場合は、先頭にGを追加するか、ExoScribeなどの代替プロモーターを使用します。", "", ] for i, row in top.iterrows(): spacer = row["protospacer"] if spacer[0] != "G": forge = f"G{spacer}" else: forge = spacer lines.append(f"### 順位{i+1}") lines.append(f"- プロトスペーサー(スパースのみ、20 bp):`{spacer}`") lines.append(f"- 注文用(必要な場合はG接頭辞付き):`{forge}`({len(forge)} bp)") lines.append(f"- PAM:`{row['pam']}`") lines.append(f"- ゲノム位置(hg38):{row['genome_position']}") if row["off_high_risk_genes"]: lines.append(f"- ⚠️ 高リスクのオフターゲット遺伝子:{row['off_high_risk_genes']}") lines.append("") lines += [ "## 事前実験チェックリスト", "", "- [ ] 注文前にアラインメントを再確認(BLASTnを使用してhg38 / GRCh38.p14に照合)", "- [ ] SpCas9-HF1ベクターを準備します(例:pX330またはplentiCRISPR v2)。", "- [ ] ターゲット細胞株でマイコプラズマ試験を完了します(K562)。", "- [ ] T7E1アッセイまたはアンプリコン深層シーケンス用のプライマーを設計します。", "- [ ] 非ターゲットgRNA(例:sgLacZ)を並行して注文します。", ] output_md.write_text("\n".join(lines), encoding="utf-8") top.to_csv(output_csv, index=False)統合パイプライン・実行例
すべてをまとめる実行関数と、BRCA1シナリオの実行例。
def full_pipeline( gene_symbol: str, refseq_id: str, output_prefix: str, editor: str | None = None, # None:ヌクレアーゼKO、"ABE8e"/"BE4max":ベース編集 top_k: int = 5,) -> pd.DataFrame: print(f"[1/7] {gene_symbol}エクソン情報取得(RefSeq {refseq_id})") # 実際には、GENCODE GTFからのエクソン座標をローカルで解析することをお勧めします。 exons = fetch_gene_exons_ucsc(gene_symbol) if not exons: # フォールバック:mRNA全体を1つの「エクソン」として扱います(単純なデモンストレーション)。 seq = fetch_refseq_mrna(refseq_id) exons = [ExonRegion( gene_name=gene_symbol, exon_number=1, chromosome="", strand="+", start_hg38=0, end_hg38=len(seq), length=len(seq), sequence=seq, coding_frame=0, )] print(f" エクソン数:{len(exons)}") print("[2/7] PAM(NGG)スキャン") all_cands = scan_gene_all_exons(exons) print(f" 生の候補数:{len(all_cands)}") print("[3/7] 物理化学的フィルター") filtered = filter_grna_physicochemical(all_cands) print(f" フィルター通過数:{len(filtered)}") print("[4/7] オンターゲットスコアラーアンサンブル") scorer = CrisprScoreEnsemble() contexts = [c.context_50bp for c in filtered] proto_pam = [c.protospacer + c.pam for c in filtered] on_scores = { "crispron": scorer.crispron(contexts), "deep_hf": scorer.deep_hf(proto_pam, enzyme="HF"), # SpCas9-HF1 } if editor: on_scores["crispron_be"] = scorer.crispron_be(contexts, editor=editor) print("[5/7] オフターゲットスキャン(CRISPORリモート)") off_summaries = [] for i, c in enumerate(filtered): if i % 50 == 0: print(f" 進捗:{i}/{len(filtered)}") offs = query_crispor_offtargets(c.protospacer) off_summaries.append(compute_off_target_summary(offs)) print("[6/7] アンサンブルランキング") ranked = build_ensemble_ranking(filtered, on_scores, off_summaries) ranked.to_csv(f"{output_prefix}_all_candidates.csv", index=False) print(f"[7/7] 上位{top_k}注文レポート") generate_lab_report( ranked, Path(f"{output_prefix}_top{top_k}.md"), Path(f"{output_prefix}_top{top_k}.csv"), top_k=top_k, ) print(f" 完了:{output_prefix}_top{top_k}.md") return ranked
# 実行例(BRCA1 KOシナリオ)# ranked = full_pipeline(# gene_symbol="BRCA1",# refseq_id="NM_007294",# output_prefix="brca1_k562",# editor=None, # SpCas9-HF1ヌクレアーゼKO# top_k=5,# )予想される実行ログ(参考用)
学習者が実際に実行した場合、次のようなログが表示されることが予想されます。
[1/7] BRCA1エクソン情報取得(RefSeq NM_007294)
エクソン数:24
[2/7] PAM(NGG)スキャン
生の候補数:423
[3/7] 物理化学的フィルター
フィルター通過数:197
[4/7] オンターゲットスコアラーアンサンブル
[5/7] オフターゲットスキャン(CRISPORリモート)
進捗:0/197
進捗:50/197
...
[6/7] アンサンブルランキング
[7/7] 上位5注文レポート
完了:brca1_k562_top5.md
## パフォーマンス・コスト・既知の失敗事例
### パフォーマンスの参考 (公開されたベンチマークの引用)
| スコアラー | ベンチマークデータセット | スピアマンのρ | 備考 | 出典 |
|---|---|:----------:|---|---|
| Rule 4 (Doench 2014) | 独自のベンチマーク | 0.42 | 手動で設計されたベースライン | Doench et al., Nat Biotechnol 2014 |
| DeepCRISPR | Kim 2017データセット | 0.65 | 1D CNN | Chuai et al., Genome Biol 2018 [1] |
| CRISPRon (Xiang 2021) | Kim 2019 | 0.72 | コンテキストを考慮したCNN | Xiang et al., Nat Commun 2021 [2] |
| DeepHF (WT SpCas9) | Wang 2019 | 0.73 | RNN + アテンション | Wang et al., Nat Commun 2019 [3] |
| DeepHF (SpCas9-HF1) | Wang 2019 | 0.75 | HF1バリアントに特化 | Wang et al. 2019 [3] |
| CRISPRon-BE (ABE8e) | ABEmaxデータセット | 0.68 | 塩基編集 | Kim et al. 2022 [4] |
| BE-Hive | BE4/ABEデータセット | 0.75 | 編集ウィンドウ予測に優れている | Arbab et al. 2020 |
| アンサンブル (本稿で近似) | ベンチマークの再現 | 0.77〜0.82 (推定) | 複数のスコアラーの平均 | コミュニティベンチマーク [7] |
### 推定される学習モデルの再現にかかるコスト
- APIコストはかからない(完全にローカルまたは無料のCRISPORリモート)。
- 小規模な消費向けGPUで500個のgRNAをスコアリングする場合、約1〜3分。
- CPUで代替する場合、5〜15分。
- CRISPORリモートAPI:推奨は1秒あたり1リクエスト未満、500個のgRNAのスクリーニングには約10〜20分。
- ローカルのcrispritz代替案:CPUで30分 + 20GBのゲノムインデックスのダウンロード。
### 5つの既知の失敗事例(コミュニティ/論文から収集)
1. **rpy2環境の競合(R 4.3 + Bioconductor 3.18 + rpy2 3.5)**
症状:Rライブラリのロードに失敗する(例:`libR.so symbol lookup error`、`PermissionError: could not load package`)。
原因:condaとシステムRの間でRライブラリのパスが二重に登録されている(特にmacOS/Linuxの混合使用の場合に多い)。
解決策:(a)Rをconda-forgeを通じて、conda環境内で一貫してインストールする(`conda install -c conda-forge r-base bioconductor-crisprscore`)、(b)`R_HOME`と`LD_LIBRARY_PATH`の環境変数を明示的に設定する、(c)Dockerコンテナで分離する(rocker/tidyverse + crisprScoreのインストール)、(d)rpy2をバイパスして、Rscriptを直接subprocess経由で呼び出す。
出典:rpy2 GitHub Issues [8]、BioconductorサポートフォーラムのcrisprScoreのスレッド。
2. **計算されたCFDスコアと実際の実験におけるオフターゲットの結果の乖離**
症状:CFDで安全(低いスコア)と判断されたgRNAが、実際の実験で予期しないオフターゲット編集を引き起こす(GUIDE-seq、CIRCLE-seq)。
原因:CFDは、学習されたミスマッチ位置の重みを基にしている(Doench 2016 [5])が、クロマチン状態、DNAメチル化、ヌクレオソームの位置を反映していない。
解決策:(a)複数のオフターゲットアルゴリズム(CFD + MIT/Hsu-Zhang + Elevation)を組み合わせる、(b)TSSやエンハンサー領域に対して、別途ペナルティを適用する(ENCODEアノテーションを参照)、(c)GUIDE-seqやDigenome-seqなどの測定されたオフターゲットデータで検証する、(d)最終的な選択の前に、上位3つの候補を並行して測定する。
出典:Tsai et al. "GUIDE-seq" Nat Biotechnol 2015 [9]、Doench 2016 CFD論文 [5]、CRISPORドキュメント [6]。
3. **BE(塩基編集)の編集ウィンドウの誤解釈 + バイスタンダー効果**
症状:CRISPRon-BEでスコアリングされた塩基エディターが、意図された編集ターゲットに加えて、他のヌクレオチドも編集する(バイスタンダー)。
原因:BEは、プロトスペーサー内の特定のウィンドウ(例:4〜8塩基)を編集すると想定されているが、実際には5〜10塩基まで拡張し、アデニンデアミナーゼ(ABE)は特定の配列コンテキストにバイアスがかかっている。
解決策:(a)複数のモデル(BE-Hive、BE-DICT、CRISPRon-BE)の編集ウィンドウのアノテーションを組み合わせる、(b)実験設計において、バイスタンダー候補を別途マークする、(c)Prime editing(PE)スコアラー(PRIDICT)を使用して、正確な編集を行うことを検討する、(d)コドン単位でシミュレーションを行い、編集ウィンドウ内のターゲットのC/Aの位置が不要なコドンの変化を引き起こさないか確認する。
出典:Anzalone et al. "Prime editing" Nature 2019 [10]、Arbab et al. "BE-Hive" Cell 2020。
4. **CRISPORリモートAPIのレート制限 + 応答遅延**
症状:500個のgRNAを連続してスキャンすると、半数以上が失敗またはタイムアウトする。
原因:CRISPORリモートサーバーは、CPUの制約を受けている。これはコミュニティリソースである。
解決策:(a)ローカルのcrispritz + BWA + ゲノムインデックスを使用してバッチスキャンする(20GBのゲノムインデックスのダウンロードが必要)、(b)CRISPORをローカルにインストールする(Pythonスクリプトをローカルにインストールする)、(c)5秒以上の間隔でリクエストする、(d)上位20個の候補のみをCRISPORリモートに送信する(100個以上の候補をローカルで処理する)。
出典:CRISPORの公式ドキュメント「バッチ使用」セクション [6]。
5. **BRCA1のオルタナティブスプライシングによるエクソン座標の不一致**
症状:BRCA1には複数のスプライスバリアントがある。NM_007294がカノニカルであるが、他の転写物(NM_007297など)は異なるエクソン数/座標を持つ。
原因:MyGene.infoは1つのカノニカル転写物のみを返す。ターゲットの組織/細胞株で実際に発現しているアイソフォームが異なる場合がある。
解決策:(a)GENCODE GTFから複数の転写物の座標を解析する、(b)ターゲットの組織/細胞株の主要なアイソフォームを測定された発現データ(GTEx、Human Protein Atlas)を使用して確認する、(c)複数のアイソフォームに共通のエクソン(構成的エクソン)を優先する。
出典:GENCODEアノテーションドキュメント [11]、UCSC Genome BrowserのBRCA1トラック。
## 拡張アイデア
- **Prime Editing (PE) スコアリング拡張機能**: PE は複雑な 5' pegRNA デザインを必要としますが、高精度な編集に最適です。最新のスコアリングツール(例: PRIDICT、DeepPE)を組み込みます。
- **CRISPRi/CRISPRa (活性化/干渉)**: KO の代わりに、遺伝子発現の調節を行います。`getCrispraiScores` を crisprScore で使用し、dCas9-VP64(活性化)または dCas9-KRAB(干渉)を適用します。
- **マルチ gRNA (多重) 組み合わせの最適化**: 複数の遺伝子を同時にノックアウトする場合、相互のオフターゲット干渉を最小限に抑えます(組み合わせ最適化、整数計画法)。
- **患者特異的な遺伝的背景**: 特定の SNPs を持つ患者では、オフターゲットの位置が異なります(個別化されたオフターゲット、gnomAD や UK Biobank のアノテーションを参照)。
- **ヌクレオソーム配置の統合**: MNase-seq データを使用して、ヌクレオソームで覆われた領域に対して個別のペナルティを適用します。
- **T7 in vitro 合成 vs オリゴプール**: gRNA の注文形式(シングル vs プール)に応じて、上位 5 件と上位 200 件に対するスコアリング戦略が異なります。
## 次のトピック
- トピック 07 `drug-target-gnn`: GNN を使用した薬剤-標的予測により、gRNA でノックアウトする候補標的を事前に絞り込みます。
- トピック 10 `med-llm-reproduction`: 複数の LLM に gRNA スコアリングのベンチマークを再現させ、評価を行います。
- トピック 12 `single-cell-perturbation`: 実際にノックアウトする前に、gRNA 候補に対する細胞応答を in silico で予測します(Perturb-seq シミュレーション)。
- トピック 14 `bio-mcp-agent`: gRNA デザインを MCP ツールとして公開し、「BRCA1 エキソン 3〜5 から KO gRNA を抽出する」などの自律的な実行を可能にします。
## 参考文献
1. Chuai G, Ma H, Yan J, et al. "DeepCRISPR: optimized CRISPR guide RNA design by deep learning." Genome Biology 2018. `https://genomebiology.biomedcentral.com/articles/10.1186/s13059-018-1459-4`
2. Xiang X, Corsi GI, Anthon C, et al. "Enhancing CRISPR-Cas9 gRNA efficiency prediction by deep learning (CRISPRon)." Nature Communications 2021. `https://www.nature.com/articles/s41467-021-23576-0`
3. Wang D, Zhang C, Wang B, et al. "Optimized CRISPR guide RNA design for two high-fidelity Cas9 variants by deep learning (DeepHF)." Nature Communications 2019. `https://www.nature.com/articles/s41467-019-12281-8`
4. Kim HK, Yu G, Park J, et al. "Predicting the efficiency of prime editing guide RNAs in human cells (PRIDICT); base editing companion (CRISPRon-BE)." Nature Biotechnology 2023 (関連部分). `https://www.nature.com/articles/s41587-022-01613-7`
5. Doench JG, Fusi N, Sullender M, et al. "Optimized sgRNA design to maximize activity and minimize off-target effects of CRISPR-Cas9 (CFD original paper)." Nature Biotechnology 2016. `https://www.nature.com/articles/nbt.3437`
6. CRISPOR ウェブツールとドキュメント: `http://crispor.tefor.net/` / `https://github.com/maximilianh/crisporWebsite`
7. crisprScore Bioconductor パッケージ: `https://bioconductor.org/packages/release/bioc/html/crisprScore.html`
8. rpy2 GitHub Issues: `https://github.com/rpy2/rpy2/issues`
9. Tsai SQ, Zheng Z, Nguyen NT, et al. "GUIDE-seq enables genome-wide profiling of off-target cleavage by CRISPR-Cas nucleases." Nature Biotechnology 2015. `https://www.nature.com/articles/nbt.3117`
10. Anzalone AV, Randolph PB, Davis JR, et al. "Search-and-replace genome editing without double-strand breaks or donor DNA (Prime Editing)." Nature 2019. `https://www.nature.com/articles/s41586-019-1711-4`
11. GENCODE ヒトゲノムアノテーション: `https://www.gencodegenes.org/human/`
12. Kim HK et al. "Predicting the efficiency of prime editing guide RNAs in human cells (PRIDICT)." Nat Biotechnol 2023.
13. Broad Institute CRISPResso2 (編集結果の分析): `https://github.com/pinellolab/CRISPResso2`
14. Bioconductor BSgenome.Hsapiens.UCSC.hg38: `https://bioconductor.org/packages/release/data/annotation/html/BSgenome.Hsapiens.UCSC.hg38.html`
15. RTH-tools CRISPRon-BE GitHub: `https://github.com/RTH-tools/crispron-BE`
16. BE-Hive: Arbab M et al. "Determinants of Base Editing Outcomes from Target Library Analysis and Machine Learning." Cell 2020. `https://github.com/maxwshen/be_predict_efficiency`
17. Addgene sgRNA 注文標準プロトコル: `https://www.addgene.org/guides/crispr/`
18. crispritz (ローカルオフターゲットスキャン): `https://github.com/pinellolab/CRISPRitz`
19. NCBI RefSeq NM_007294 (BRCA1 カノニカル mRNA): `https://www.ncbi.nlm.nih.gov/nuccore/NM_007294`
20. MyGene.info API: `https://mygene.info/v3/api`