BioPlayground

🧬
목록으로

CRISPR guide RNA 온·오프타겟 스코어링 — BRCA1 KO 실험 전 100개 후보를 30초에 랭킹

BRCA1 exon KO 시나리오를 예시로, K562 세포주 대상 gRNA 후보 100~500개를 딥러닝 스코어러 앙상블(CRISPRon·DeepHF·CRISPRon-BE)로 30초에 랭킹. 온타겟 효율 + 오프타겟 위험(CFD·CRISPOR DB) 이중 필터, crisprScore Bioconductor 패키지 rpy2 브릿지, 실험실 발주 리포트 자동 생성까지.

중급
|
35
|
검증 완료 (2026-07)
진행률0/15 (0%)

CRISPR guide RNA 온·오프타겟 스코어링 — BRCA1 KO 실험 전 100개 후보를 30초에 랭킹

CRISPR-Cas9 실험의 성공률은 gRNA(guide RNA) 선택에서 이미 절반 결정됩니다. 잘 골라야 표적 유전자를 효율적으로 자르고(온타겟 효율), 다른 유전자를 잘못 자르지 않습니다(오프타겟 회피). 그런데 후보 gRNA를 실험실에서 하나씩 합성·검증하려면 개당 며칠·수만 원이 듭니다. 이 편은 BRCA1 유전자 KO를 K562 세포주에서 진행하는 실전 시나리오를 예시로, 후보 gRNA 100~500개를 딥러닝 스코어러 앙상블로 30초에 랭킹해 상위 5개만 실제 합성으로 넘기는 완결 파이프라인을 하드코어로 구축합니다.

📚 선수 편 권고 (강력 권고)

이 편은 AI×바이오 하드코어 심화 편입니다. 진입 전에 DryBench의 다음 편들을 먼저 듣고·보시길 강력히 권고드립니다.

선수 편 없이 진입할 경우, 이 편에서 다루는 1D CNN·RNN 기반 서열 스코어러의 학습 원리, one-hot 인코딩, pretrained 모델 로딩·추론 실전 패턴에 대한 재설명 없이 실전 코드부터 진행하므로 따라가기 어렵습니다.


우리 DryBench에서 이거 배웠잖아

DryBench ai-native #2에서 신경망이 입력 시퀀스 위 로컬 패턴을 CNN 필터로 잡고, RNN이 순서 의존성을 학습한다는 것을 배웠습니다. #13에서 HuggingFace가 pretrained 모델의 가중치·토크나이저·추론 API를 표준화해 몇 줄로 로딩 가능하게 만든 생태계라는 것을 봤습니다.

CRISPR gRNA 스코어링은 이 두 원리가 놀랍게 가까운 곳에서 실전으로 만나는 지점입니다. 20bp protospacer + 3bp PAM이라는 매우 짧은 입력(총 23bp = 92 dimensional one-hot)에 대해 상대적으로 작은 1D CNN이 학습한 컨텍스트 특성이 실제 실험 효율과 Spearman ρ 0.7 이상 상관됩니다. 이 편은 그 예측력을 여러 스코어러 앙상블로 강화하고, 오프타겟 위험까지 정량화해서 실제 실험 설계 최적화 도구로 만드는 파이프라인입니다.

하드코어 문제 정의

실전 시나리오: BRCA1 KO in K562

DNA damage repair 연구를 위해 K562 (인간 만성 골수성 백혈병 세포주)에서 BRCA1 (Breast Cancer 1) 유전자를 SpCas9-HF1 시스템으로 knockout합니다. BRCA1은 exon 24개, 총 mRNA 약 7.2kb, 코딩 영역 약 5.6kb. KO 실험 성공률·재현성을 위해 다음이 필요합니다.

  • 여러 exon에서 gRNA 후보 탐색 (nonsense-mediated decay 회피 위해 5'-side exon 우선).
  • 후보 gRNA 각각에 대한 온타겟 효율·오프타겟 위험 정량.
  • 상위 5개만 합성 발주 (Twist Bioscience · IDT 등, 하나 당 약 30~100 USD).
  • 실험 검증 (T7E1 assay or amplicon deep sequencing)로 실측 효율 후속 검증.

CRISPR gRNA 설계의 근본 트레이드오프

  • 온타겟 효율(on-target efficiency): gRNA가 표적 위치를 얼마나 효율적으로 자르는가. 스코어 0~1, 높을수록 좋음. 학습 데이터는 대개 K562 · HEK293T · Jurkat 등 세포주에서 수천 gRNA를 assay한 결과.
  • 오프타겟 안전성(off-target safety): 유사한 서열이 게놈 어디에 있는지 + 그 위치가 얼마나 심각한 부작용을 유발하는지. 스코어 0~1, 높을수록 안전.
  • PAM 인접 제약: SpCas9 표준 PAM은 NGG. 특수 variant는 NG (SpCas9-NG), NGN (SpG), NRN (SpRY) 등.
  • BE (base editor) 편집 창: base editing은 protospacer 안 특정 창(예: 4-8 nt) 편집. 창 위치와 넉아웃 목표 codon 정합이 중요.

기존 접근 스펙트럼과 이 편의 위치

  • 경험 규칙 (Doench 2014, Xu 2015): PAM 앞뒤 nucleotide composition · GC content · secondary structure 등 손공학 feature 기반 로지스틱 회귀. Spearman ρ 0.4~0.5.
  • 1D CNN 초기 (DeepCRISPR 2018): 21bp 컨텍스트 학습. ρ 0.6~0.65 [1].
  • CRISPRon (Xiang 2021): context-aware CNN + regression. ρ 0.72 [2].
  • DeepHF (Wang 2019): SpCas9-HF1·xCas9 variant 특화. ρ 0.75 [3].
  • CRISPRon-BE (Kim 2022): Base editing (ABE · CBE) 특화. ρ 0.68~0.75 [4].
  • BE-Hive (2020): BE 편집 창 예측 강력.
  • PRIDICT (2023): Prime editing pegRNA 설계.
  • Foundation model 접근 (실험적): DNABERT · Nucleotide Transformer로 gRNA 임베딩 후 downstream head.

이 편은 CRISPRon (온타겟) + DeepHF (variant Cas9) + CRISPRon-BE (base editing) + CFD/MIT (오프타겟) 4개 스코어러를 앙상블해 단일 스코어러의 편향을 상쇄하고, CRISPOR 원격 API로 게놈 전체 오프타겟 스캔을 추가합니다.

이 편의 목표 지표

  • BRCA1 exon 24개 전체에서 gRNA 후보 200~500개 자동 추출 (양쪽 가닥, NGG PAM).
  • Lipinski-style filter (GC 30~70%, TTTT 회피, self-complementarity 회피).
  • 앙상블 스코어러로 각 후보의 온타겟 · 오프타겟 스코어 산출 (총 wall-clock 30초~2분).
  • CRISPOR로 게놈 전체 오프타겟 스캔 (상위 20개만).
  • 최종 랭킹 상위 5개 자동 리포트 (실험실 발주 가능한 Markdown + CSV 포맷, spacer + PAM + score matrix + off-target hit summary 포함).

도구 스택과 인프라 요구

도구역할라이선스
Bioconductor crisprScore (R)통합 스코어러 프레임워크 (CRISPRon·DeepHF·CRISPRon-BE·CFD 등)Artistic-2.0
CRISPRon-BE (RTH-tools GitHub)Base editing 특화 스코어러 (ABE · CBE)GPL v3
rpy2 (Python↔R 브릿지)Python에서 R crisprScore 호출GPL v2+
BiopythonFASTA · GenBank 파싱, PAM 탐색, reverse complementBiopython License
CRISPOR 원격 API or 로컬 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+.
  • RAM 8GB 이상. 디스크: 인간 게놈 참조 약 3GB, CRISPRon 가중치 약 100MB.

학습자 재현 예상 비용: API 비용 0(완전 로컬 또는 CRISPOR 무료 원격). GPU 시간 gRNA 500개 스코어링 약 1~3분 (모델 로딩 포함 첫 회 5분).

파이프라인 실전 구현

전체 흐름:

mermaid

Step 1. BRCA1 exon 서열 추출

NCBI RefSeq에서 BRCA1 mRNA (NM_007294) 또는 UCSC Genome Browser에서 hg38 exon 좌표 취득 후 exon 서열 추출.

python
from dataclasses import dataclass
from pathlib import Path
from typing import Iterator
import requests
from Bio import SeqIO, Entrez
from Bio.Seq import Seq
Entrez.email = "your@email.example" # NCBI 필수
@dataclass
class ExonRegion:
"""유전자 exon 하나의 정보."""
gene_name: str
exon_number: int
chromosome: str
strand: str # "+" or "-"
start_hg38: int # 0-based
end_hg38: int
length: int
sequence: str
coding_frame: int | None # None if UTR, 0/1/2 otherwise
def fetch_refseq_mrna(refseq_id: str = "NM_007294") -> str:
"""NCBI에서 RefSeq mRNA 서열 fetch (예: 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로 유전자 exon 좌표 fetch.
실전에서는 GENCODE annotation 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] # canonical transcript
exon_regions = []
for i, (start, end) in enumerate(canonical.get("position", [])):
# UCSC에서 실제 서열 fetch (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()

Step 2. PAM 스캔 · 후보 gRNA 추출

SpCas9 표준 PAM NGG. 반대 가닥도 스캔 (protospacer의 reverse complement).

python
@dataclass
class GRNACandidate:
gene_name: str
exon_number: int
protospacer: str # 20bp 표적 서열
pam: str # 3bp PAM
strand: str # "+" or "-"
exon_position: int # exon 서열 상 시작 위치 (0-based)
genome_position: int # hg38 게놈 좌표 (0-based)
context_50bp: str # 앞 15bp + gRNA + PAM + 뒤 15bp = 53bp (CRISPRon 등 학습 입력)
def scan_pam_ngg(sequence: str, gene_name: str, exon: ExonRegion) -> list[GRNACandidate]:
"""양쪽 가닥에서 NGG PAM + 20bp protospacer 후보 추출."""
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]
# 게놈 좌표 계산 (strand별 방향 반전)
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

Step 3. 기본 물리·화학 필터

CRISPR gRNA는 다음 조건 만족 시 대체로 안정적으로 합성·기능:

  • GC 함량 30~70%: 극단은 mismatch tolerance 저하.
  • 연속 4개 이상 T (TTTT) 회피: RNA Polymerase III 종결 신호.
  • self-complementarity 회피: 자체 secondary structure로 접힘 → sgRNA-Cas9 결합 저해.
  • 첫 nucleotide 규칙: U6 promoter는 G로 시작하는 gRNA 우선.
python
from Bio.SeqUtils import GC
def check_self_complementarity(seq: str, min_stem: int = 4) -> bool:
"""자체 상보성 검사: 5' end 4bp가 3' end 4bp의 reverse complement이면 hairpin 우려."""
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 content
gc = GC(c.protospacer)
if not (min_gc <= gc <= max_gc):
continue
# Poly-T
if "T" * (max_poly_t + 1) in c.protospacer:
continue
# G start (선호)
if prefer_g_start and c.protospacer[0] != "G":
# 완전 제외는 아니지만 후순위 (여기서는 통과)
pass
# Hairpin
if check_hairpin and check_self_complementarity(c.protospacer):
continue
filtered.append(c)
return filtered

Step 4. R crisprScore를 Python에서 호출 (rpy2 브릿지)

crisprScore 패키지는 CRISPRon · DeepHF · CRISPRon-BE · CFD · Hsu-Zhang · MIT 등 대부분의 표준 스코어러를 통합해 제공합니다.

python
import numpy as np
import rpy2.robjects as ro
from rpy2.robjects.packages import importr
from rpy2.robjects.vectors import StrVector
class CrisprScoreEnsemble:
"""R crisprScore의 여러 스코어러를 Python에서 호출."""
def __init__(self):
# R 패키지 로딩 (사전에 R 콘솔에서 BiocManager::install 필요)
self.crisprscore = importr("crisprScore")
self.base = importr("base")
def crispron(self, contexts: list[str]) -> list[float]:
"""CRISPRon 온타겟 스코어. 입력: 각 항목 30bp context (4bp upstream + 20bp protospacer + 3bp PAM + 3bp downstream)."""
r_input = StrVector(contexts)
try:
result = self.crisprscore.getCRISPRonScores(r_input)
return list(result)
except Exception as e:
print(f"CRISPRon 실패: {e}")
return [float("nan")] * len(contexts)
def deep_hf(self, protospacers_with_pam: list[str], enzyme: str = "WT") -> list[float]:
"""DeepHF 스코어. 입력: 각 23bp (20 protospacer + 3 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 실패: {e}")
return [float("nan")] * len(protospacers_with_pam)
def crispron_be(self, contexts: list[str], editor: str = "ABE8e") -> list[float]:
"""CRISPRon-BE base editing 효율.
editor 옵션: 'ABE8e' (adenine), 'BE4max' (cytosine), etc.
"""
r_input = StrVector(contexts)
try:
result = self.crisprscore.getCRISPRonBEScores(r_input, editor=editor)
return list(result)
except Exception as e:
print(f"CRISPRon-BE 실패: {e}")
return [float("nan")] * len(contexts)
def cfd_off_target(self, protospacer: str, target_dna: str) -> float:
"""Cutting Frequency Determination (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 오프타겟 스코어 (position-dependent mismatch penalty)."""
try:
result = self.crisprscore.getMITScores(
StrVector([protospacer]),
StrVector([target_dna]),
)
return float(list(result)[0])
except Exception:
return 0.0

R 환경 셋업 (편 본문 참고 명령):

bash
# R 콘솔에서
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install(c(
"crisprScore", "crisprDesign",
"BSgenome.Hsapiens.UCSC.hg38",
"crisprBase"
))

Step 5. CRISPOR 원격 오프타겟 스캔

CFD는 로컬 단일 pairwise 스코어. 게놈 전체 오프타겟 스캔에는 CRISPOR가 표준 [6]. crispritz 로컬 대안도 있음.

python
def query_crispor_offtargets(
protospacer: str,
genome: str = "hg38",
max_mismatches: int = 4,
timeout: int = 300,
) -> list[dict]:
"""CRISPOR 원격 API로 게놈 전체 오프타겟 스캔.
Returns: [{genome_pos, mismatches, cfd_score, gene}, ...]
"""
# 실전 endpoint: http://crispor.tefor.net/crispor.py
# 여기서는 개념적 wrapper. 실제 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,
}

Step 6. 앙상블 랭킹

여러 스코어러 결과를 가중 앙상블. 벤더·태스크별 최적 가중치는 실험 검증 데이터로 fine-tune 가능.

python
import pandas as pd
DEFAULT_WEIGHTS = {
"crispron": 0.30,
"deep_hf": 0.25,
"crispron_be": 0.10, # BE 시나리오 아닐 때는 0으로
"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)

Step 7. 실험실 발주 리포트 자동 생성

상위 5개 gRNA를 Twist Bioscience · IDT 등에서 즉시 발주 가능한 Markdown + CSV로.

python
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"# gRNA 후보 상위 {top_k}개 발주 리포트",
"",
f"대상 유전자: **{top.iloc[0]['gene']}**",
f"생성 시각: (파이프라인 실행 시각으로 채우기)",
"",
"## 요약 표",
"",
"| Rank | Exon | Protospacer (20bp) | PAM | Strand | On (CRISPRon) | On (DeepHF) | Off Safety | Final |",
"|:----:|:----:|:------------------:|:---:|:------:|:-------------:|:-----------:|:----------:|:-----:|",
]
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')",
"",
"Spacer 서열에 U6 promoter 요구 G가 없으면 앞에 G 추가 or ExoScribe 등 alternative promoter 활용.",
"",
]
for i, row in top.iterrows():
spacer = row["protospacer"]
if spacer[0] != "G":
forge = f"G{spacer}"
else:
forge = spacer
lines.append(f"### Rank {i+1}")
lines.append(f"- Protospacer (spacer only, 20bp): `{spacer}`")
lines.append(f"- Order-ready (with G prefix if needed): `{forge}` ({len(forge)}bp)")
lines.append(f"- PAM: `{row['pam']}`")
lines.append(f"- Genome position (hg38): {row['genome_position']}")
if row["off_high_risk_genes"]:
lines.append(f"- ⚠️ High-risk off-target genes: {row['off_high_risk_genes']}")
lines.append("")
lines += [
"## 사전 실험 체크리스트",
"",
"- [ ] 발주 전 alignment 재확인 (BLASTn to hg38 · GRCh38.p14)",
"- [ ] SpCas9-HF1 vector 준비 (예: pX330 or plentiCRISPR v2)",
"- [ ] 대상 세포주 (K562)의 mycoplasma 검사 완료",
"- [ ] T7E1 assay 또는 amplicon deep sequencing primer 설계",
"- [ ] 대조군 gRNA (non-targeting, e.g. sgLacZ) 병행 발주",
]
output_md.write_text("\n".join(lines), encoding="utf-8")
top.to_csv(output_csv, index=False)

통합 파이프라인 · 실행 예시

전체를 하나로 묶은 실행 함수와 BRCA1 시나리오 실행 예시.

python
def full_pipeline(
gene_symbol: str,
refseq_id: str,
output_prefix: str,
editor: str | None = None, # None: nuclease KO, "ABE8e"/"BE4max": base editing
top_k: int = 5,
) -> pd.DataFrame:
print(f"[1/7] {gene_symbol} exon 정보 fetch (RefSeq {refseq_id})")
# 실전에서는 exon 좌표를 GENCODE GTF에서 로컬 파싱 권장
exons = fetch_gene_exons_ucsc(gene_symbol)
if not exons:
# fallback: mRNA 전체를 단일 "exon"으로 처리 (간단 시연)
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" exon {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 nuclease KO
# top_k=5,
# )

예상 실행 로그 (참고용)

학습자가 실제 실행 시 이런 로그를 예상:

text
[1/7] BRCA1 exon 정보 fetch (RefSeq NM_007294)
      exon 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

성능·비용·알려진 실패 케이스

성능 참고 (공개 벤치 인용)

스코어러벤치 데이터셋Spearman ρ특이 사항출처
Rule 4 (Doench 2014)자체 벤치0.42손공학 baselineDoench et al., Nat Biotechnol 2014
DeepCRISPRKim 2017 dataset0.651D CNNChuai et al., Genome Biol 2018 [1]
CRISPRon (Xiang 2021)Kim 20190.72context-aware CNNXiang et al., Nat Commun 2021 [2]
DeepHF (WT SpCas9)Wang 20190.73RNN + attentionWang et al., Nat Commun 2019 [3]
DeepHF (SpCas9-HF1)Wang 20190.75HF1 variant 특화Wang et al. 2019 [3]
CRISPRon-BE (ABE8e)ABEmax dataset0.68Base editingKim et al. 2022 [4]
BE-HiveBE4/ABE dataset0.75편집 창 예측 강력Arbab et al. 2020
앙상블 (본 편 근사)벤치 재현0.77~0.82 (추정)다중 스코어러 평균커뮤니티 벤치 [7]

학습자 재현 예상 비용

  • API 비용 0 (완전 로컬 또는 CRISPOR 무료 원격).
  • 소형 소비자 GPU 기준 gRNA 500개 스코어링 약 1~3분.
  • CPU 폴백 시 5~15분.
  • CRISPOR 원격 API: 초당 1 req 이하 권장, 500개 스캔 약 10~20분.
  • 로컬 crispritz 대안 시 CPU 30분 + 게놈 인덱스 20GB 다운로드.

알려진 실패 케이스 5건 (커뮤니티·논문 수집형)

  1. rpy2 환경 충돌 (R 4.3 + Bioconductor 3.18 + rpy2 3.5)
    증상: libR.so symbol lookup error · PermissionError: could not load package 등 R 라이브러리 로드 실패.
    원인: R 라이브러리 경로가 conda vs system R 이중 등록, 특히 macOS/Linux 혼용 시 흔함.
    회피: (a) conda 환경 안에서 R까지 conda-forge로 통일 설치 (conda install -c conda-forge r-base bioconductor-crisprscore), (b) R_HOME · LD_LIBRARY_PATH 환경변수 명시적 지정, (c) Docker 컨테이너로 격리 (rocker/tidyverse + crisprScore install), (d) rpy2 대신 subprocess로 Rscript 직접 호출 (rpy2 우회).
    출처: rpy2 GitHub Issues [8]; Bioconductor Support Forum crisprScore 스레드.

  2. CFD 스코어 계산과 실제 실험 오프타겟 결과 불일치
    증상: CFD 상 안전 (스코어 낮음)한 gRNA가 실제 실험(GUIDE-seq · CIRCLE-seq)에서 예상 밖 오프타겟 편집 유발.
    원인: CFD는 mismatch position 가중치 학습 (Doench 2016 [5])이지만 chromatin state · DNA methylation · nucleosome positioning은 반영 안 함.
    회피: (a) 여러 오프타겟 알고리즘 앙상블 (CFD + MIT/Hsu-Zhang + Elevation), (b) TSS · enhancer 지역은 별도 페널티 (ENCODE annotation 참조), (c) GUIDE-seq · Digenome-seq 등 실측 오프타겟 데이터로 사후 검증, (d) 상위 후보 3개 이상을 병렬 실측 후 최종 선택.
    출처: Tsai et al. "GUIDE-seq" Nat Biotechnol 2015 [9]; Doench 2016 CFD 논문 [5]; CRISPOR docs [6].

  3. BE (Base Editing) 편집 창 오해석 + bystander effect
    증상: CRISPRon-BE로 스코어링한 base editor가 실제 편집 대상 뉴클레오티드 이외 위치를 함께 편집 (bystander).
    원인: BE는 protospacer 안 특정 창(예: 4-8 nt) 편집한다는 가정이 있으나 실제로는 5-10 nt 확장, adenine deaminase (ABE)는 특정 sequence context 편향.
    회피: (a) 편집 창 표기를 여러 모델 예측으로 앙상블 (BE-Hive · BE-DICT · CRISPRon-BE), (b) 실험 설계에 bystander 후보 별도 표시, (c) 정밀 편집 필요 시 Prime editing (PE) 스코어러 (PRIDICT) 고려, (d) 편집 창 안 target C/A 위치에 원치 않는 codon 변화 없는지 codon-wise 시뮬레이션.
    출처: Anzalone et al. "Prime editing" Nature 2019 [10]; Arbab et al. "BE-Hive" Cell 2020.

  4. CRISPOR 원격 API rate limit + 응답 지연
    증상: 500개 gRNA를 연속 스캔하면 절반 이상 실패 or timeout.
    원인: CRISPOR 원격 서버는 CPU 리소스 제약. 커뮤니티 자원.
    회피: (a) 로컬 crispritz + BWA + 게놈 인덱스로 배치 스캔 (게놈 인덱스 20GB 다운로드 필요), (b) CRISPOR 로컬 실행 (Python 스크립트 로컬 설치), (c) 요청 간격 5초 이상, (d) 상위 후보 20개만 CRISPOR 원격 스캔 (100+개는 로컬).
    출처: CRISPOR 공식 documentation "batch use" 섹션 [6].

  5. BRCA1 alternative splicing으로 exon 좌표 불일치
    증상: BRCA1은 여러 splice variant가 있음. NM_007294가 canonical이나 다른 transcript (NM_007297 등)에서는 exon 수 · 좌표 다름.
    원인: MyGene.info는 canonical 하나만 반환. 실제 조직·세포주에서 발현되는 isoform이 다를 수 있음.
    회피: (a) GENCODE GTF에서 여러 transcript 좌표 파싱, (b) 표적 조직·세포주의 실측 발현 데이터 (GTEx · Human Protein Atlas)로 우세 isoform 확인, (c) 여러 isoform에 공통인 exon (constitutive exon)만 우선.
    출처: GENCODE annotation docs [11]; UCSC Genome Browser BRCA1 track.

확장 아이디어

  • Prime Editing (PE) 스코어러 확장: PE는 5' pegRNA 설계가 복잡하지만 정밀 편집 최적. PRIDICT · DeepPE 등 최신 스코어러 편입.
  • CRISPRi/CRISPRa (activation/interference): KO 대신 발현 조절. crisprScore의 getCrispraiScores 활용, dCas9-VP64 (activation) or dCas9-KRAB (interference).
  • 다중 gRNA (multiplex) 조합 최적화: 유전자 여러 개 동시 KO 시 서로 오프타겟 간섭 최소화 (조합 최적화 · 정수 계획법).
  • 환자별 유전 배경 반영: 특정 SNP 있는 환자에서 오프타겟 위치가 달라짐 (personalized off-target, gnomAD · UK Biobank annotation 참조).
  • Nucleosome positioning 통합: MNase-seq 데이터로 nucleosome-occluded 영역 별도 페널티.
  • T7 in vitro synthesis vs oligo pool: gRNA 발주 형태(단일 vs 풀)에 따라 상위 5개 vs 상위 200개 스코어링 전략 다름.

다음 편

  • 편 07 drug-target-gnn: gRNA로 knockout할 표적 후보를 GNN drug-target 예측으로 미리 압축.
  • 편 10 med-llm-reproduction: gRNA 스코어러 벤치를 여러 LLM에게 재현 시켜 평가.
  • 편 12 single-cell-perturbation: gRNA 후보를 실제 knockout 하기 전에 in silico로 세포 반응 예측 (Perturb-seq 시뮬레이션).
  • 편 14 bio-mcp-agent: gRNA 설계를 MCP tool로 노출해 "BRCA1 exon 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 원논문)." Nature Biotechnology 2016. https://www.nature.com/articles/nbt.3437
  6. CRISPOR web tool 및 documentation: 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 human genome annotation: 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 canonical mRNA): https://www.ncbi.nlm.nih.gov/nuccore/NM_007294
  20. MyGene.info API: https://mygene.info/v3/api