Volver a la lista

Puntuación de guía CRISPR on/off-target: clasificación de 100 candidatos en 30 segundos antes del experimento de KO de BRCA1

Como ejemplo de un escenario de eliminación de exones (KO) del gen BRCA1, se clasifican en 30 segundos entre 100 y 500 candidatos de gRNA para la línea celular K562 utilizando un conjunto de aprendices profundos (CRISPRon, DeepHF, CRISPRon-BE). Se aplica un filtro doble de eficiencia on-target y riesgo off-target (CFD, base de datos CRISPOR), junto con el paquete Bioconductor crisprScore mediante el puente rpy2, hasta la generación automática del informe de pedido para el laboratorio.

Intermedio
|
35min
|
Verificado (2026-07)
Progreso0/15 (0%)

Puntuación de on-target y off-target de CRISPR guide RNA — Clasificación de 100 candidatos en 30 segundos antes de un experimento de BRCA1 KO

La mitad del éxito de un experimento CRISPR-Cas9 se decide desde la selección del gRNA (guide RNA). Una buena selección debe cortar eficientemente el gen objetivo (eficiencia on-target) y evitar cortes erróneos en otros genes (evasión de off-target). Sin embargo, sintetizar y validar cada gRNA candidato individualmente en el laboratorio cuesta varios días y decenas de miles de wones por unidad. Este artículo establece un pipeline completo y avanzado que utiliza un ensamble de scoreadores de deep learning para clasificar entre 100 y 500 candidatos de gRNA en 30 segundos, permitiendo pasar solo los 5 mejores a la síntesis real, tomando como ejemplo un escenario práctico de KO del gen BRCA1 en la línea celular K562.

📚 Recomendación de conocimientos previos (Altamente recomendado)

Este artículo es una profundización avanzada de AI×Bio. Antes de comenzar, se recomienda encarecidamente revisar los siguientes episodios de DryBench:

Si se accede sin los conocimientos previos, será difícil seguir el ritmo, ya que se procederá directamente al código práctico sin reexplicar los principios de aprendizaje de los scoreadores de secuencias basados en 1D CNN·RNN, la codificación one-hot, ni los patrones prácticos de carga y de inferencia de modelos preentrenados tratados en este episodio.


Ya aprendimos esto en DryBench

En DryBench ai-native #2, aprendimos que las redes neuronales capturan patrones locales sobre una secuencia de entrada mediante filtros CNN, y que las RNN aprenden la dependencia de la secuencia. En el #13, vimos que HuggingFace es el ecosistema que estandariza los pesos, el tokenizador y la API de inferencia de modelos preentrenados, permitiendo su carga con pocas líneas de código.

La puntuación de CRISPR gRNA es el punto donde estos dos principios se encuentran en la práctica de una manera sorprendente. Para una entrada muy corta de 20bp protospacer + 3bp PAM (total 23bp = one-hot de 92 dimensiones), las características de contexto aprendidas por una 1D CNN relativamente pequeña correlacionan con la eficiencia experimental real y un Spearman ρ de más de 0.7. Este artículo presenta un pipeline que refuerza esa capacidad predictiva mediante un ensamble de múltiples scoreadores y cuantifica incluso el riesgo de off-target para convertirlo en una herramienta de optimización de diseño experimental real.

Definición del problema avanzado

Escenario práctico: BRCA1 KO in K562

Para estudiar la reparación del daño del ADN, se inactiva el gen BRCA1 (Breast Cancer 1) en K562, una línea celular humana de leucemia mieloide crónica, mediante el sistema SpCas9-HF1. BRCA1 tiene 24 exones, un ARNm total de unos 7,2 kb y una región codificante de unos 5,6 kb. Para mejorar el éxito y la reproducibilidad del experimento de knockout se necesita lo siguiente.

  • Buscar candidatos de gRNA en varios exones, priorizando los exones del extremo 5′ para favorecer la degradación mediada por codones sin sentido.
  • Cuantificar la eficiencia on-target y el riesgo off-target de cada candidato.
  • Encargar la síntesis únicamente de los cinco mejores, por ejemplo a Twist Bioscience o IDT, con un coste aproximado de 30–100 USD cada uno.
  • Confirmar posteriormente la eficiencia observada mediante un ensayo T7E1 o secuenciación profunda de amplicones.

Compromiso fundamental en el diseño de gRNA para CRISPR

  • Eficiencia on-target: mide con qué eficacia corta el gRNA en la posición objetivo. La puntuación va de 0 a 1 y un valor mayor es mejor. Los datos de entrenamiento suelen proceder de ensayos de miles de gRNA en líneas como K562, HEK293T y Jurkat.
  • Seguridad off-target: considera dónde aparecen secuencias similares en el genoma y la gravedad potencial de sus efectos. La puntuación va de 0 a 1 y un valor mayor indica mayor seguridad.
  • Restricción adyacente al PAM: el PAM estándar de SpCas9 es NGG; variantes especiales reconocen NG (SpCas9-NG), NGN (SpG) o NRN (SpRY).
  • Ventana de edición de BE (base editor): la edición de bases actúa en una ventana concreta del protospaciador, por ejemplo de 4 a 8 nt. Es importante alinear la ventana con el codón que se desea inactivar.

Espectro de enfoques existentes y posición de esta unidad

  • Reglas empíricas (Doench 2014, Xu 2015): regresión logística basada en características diseñadas manualmente, como la composición de nucleótidos alrededor del PAM, el contenido GC y la estructura secundaria. Spearman ρ 0,4–0,5.
  • Primeras CNN 1D (DeepCRISPR 2018): aprendizaje de un contexto de 21 bp. ρ 0,6–0,65 [1].
  • CRISPRon (Xiang 2021): context-aware CNN + regression. ρ 0.72 [2].
  • DeepHF (Wang 2019): especializado en las variantes SpCas9-HF1 y xCas9. ρ 0,75 [3].
  • CRISPRon-BE (Kim 2022): especializado en edición de bases ABE y CBE. ρ 0,68–0,75 [4].
  • BE-Hive (2020): especialmente potente para predecir ventanas de edición de bases.
  • PRIDICT (2023): Diseño de pegRNA para edición primaria.
  • Enfoque de modelo fundacional (experimental): Embedding de gRNA mediante DNABERT · Nucleotide Transformer seguido de un downstream head.

Esta sección realiza un ensamblado de 4 evaluadores: CRISPRon (on-target) + DeepHF (variant Cas9) + CRISPRon-BE (base editing) + CFD/MIT (off-target) para compensar el sesgo de un evaluador único, y añade un escaneo de off-target en todo el genoma mediante la API remota de CRISPOR.

Métricas objetivo de esta sección

  • Extracción automática de 200~500 candidatos de gRNA en la totalidad de los 24 exones de BRCA1 (ambas hebras, PAM NGG).
  • Filtro estilo Lipinski (GC 30~70%, evitar TTTT, evitar complementariedad propia).
  • Cálculo de puntuaciones on-target · off-target para cada candidato mediante el evaluador ensamblado (tiempo total de ejecución de 30 segundos a 2 minutos).
  • Escaneo de off-target en todo el genoma con CRISPOR (solo los 20 principales).
  • Reporte automático de los 5 primeros en el ranking final (formato Markdown + CSV listo para pedidos de laboratorio, que incluye spacer + PAM + matriz de puntuación + resumen de hits off-target).

Stack de herramientas y requisitos de infraestructura

HerramientaFunciónLicencia
Bioconductor crisprScore (R)Framework de evaluador integrado (CRISPRon·DeepHF·CRISPRon-BE·CFD, etc.)Artistic-2.0
CRISPRon-BE (RTH-tools GitHub)Evaluador especializado en edición de bases (ABE · CBE)GPL v3
rpy2 (Puente Python↔R)Llamada a crisprScore de R desde PythonGPL v2+
BiopythonParsing de FASTA · GenBank, búsqueda de PAM, complemento inversoLicencia Biopython
API remota de CRISPOR o crispritz localEscaneo de off-target en todo el genomaGratuito para fines académicos · GPL
Bioconductor BSgenome.Hsapiens.UCSC.hg38Referencia del genoma humanoArtistic-2.0
pandas · matplotlibTablas de resultados · VisualizaciónBSD

Requisitos de infraestructura:

  • GPU de consumo pequeña (se recomienda RTX 4060 o superior; es posible el uso de CPU como fallback, pero es entre 5~10 veces más lento).
  • R 4.3+ + Bioconductor 3.18+ (crisprScore · BSgenome.Hsapiens.UCSC.hg38 · crisprBase · crisprDesign).
  • Python 3.10+ + PyTorch 2.0+ + rpy2 3.5+.
  • RAM 8 GB o más. Disco: referencia del genoma humano, aproximadamente 3 GB; pesos de CRISPRon, aproximadamente 100 MB.

Coste estimado para la reproducción por parte del usuario: coste de la API: 0 (totalmente local o CRISPOR remoto gratuito). Tiempo de GPU: puntuación de 500 gRNA, aproximadamente 1 a 3 minutos (la primera vez, incluyendo la carga del modelo, 5 minutos).

Implementación práctica de la canalización

Flujo general:

mermaid

Paso 1. Extracción de la secuencia del exón BRCA1

Obtenga la secuencia del exón BRCA1 a partir de NCBI RefSeq (BRCA1 mRNA, NM_007294) o del navegador del genoma UCSC (coordenadas del exón hg38).

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" # Obligatorio para NCBI
@dataclass
class ExonRegion:
"""Información de un exón de un gen."""
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:
"""Obtiene de NCBI una secuencia de mRNA RefSeq, por ejemplo 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]:
"""Obtiene coordenadas de exones mediante la API REST de UCSC Genome Browser.
En producción es mucho más estable analizar localmente un GTF de anotación GENCODE.
"""
# Alternativa a UCSC Table Browser: API de MyGene.info
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", [])):
# Obtiene la secuencia real de UCSC mediante la API DAS o un BSgenome local
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, # La determinación exacta requiere coordenadas CDS
))
return exon_regions
def _fetch_ucsc_dna(chrom: str, start: int, end: int, assembly: str = "hg38") -> str:
"""Devuelve la secuencia de una región mediante la API DAS de UCSC."""
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()

Paso 2. Escaneo de PAM · Extracción de gRNA candidatos

PAM estándar de SpCas9 NGG. También se escanea la cadena opuesta (complemento inverso del protospaciador).

python
@dataclass
class GRNACandidate:
gene_name: str
exon_number: int
protospacer: str # Secuencia diana de 20 bp
pam: str # 3bp PAM
strand: str # "+" or "-"
exon_position: int # Posición inicial en el exón, base 0
genome_position: int # Coordenada genómica hg38, base 0
context_50bp: str # 15 bp anteriores + gRNA + PAM + 15 bp posteriores = 53 bp
def scan_pam_ngg(sequence: str, gene_name: str, exon: ExonRegion) -> list[GRNACandidate]:
"""Extrae candidatos con PAM NGG y protospacer de 20 bp en ambas hebras."""
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]
# Calcula la coordenada genómica e invierte la dirección según la hebra
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

Paso 3. Filtros fisicoquímicos básicos

Los gRNA de CRISPR suelen sintetizarse y funcionar de forma estable cuando cumplen estas condiciones:

  • Contenido GC del 30–70%: los valores extremos reducen la tolerancia a desajustes.
  • Evitar cuatro o más T consecutivas (TTTT): constituyen una señal de terminación para la ARN polimerasa III.
  • Evitar la autocomplementariedad: el plegamiento en estructuras secundarias propias puede dificultar la unión sgRNA–Cas9.
  • Regla del primer nucleótido: el promotor U6 favorece los gRNA que comienzan por G.
python
from Bio.SeqUtils import GC
def check_self_complementarity(seq: str, min_stem: int = 4) -> bool:
"""Comprueba autocomplementariedad: si los 4 bp del extremo 5' son reverso complementario de los del 3', puede formarse un 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]:
"""Filtro fisicoquímico."""
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
# Inicio con G (preferido)
if prefer_g_start and c.protospacer[0] != "G":
# No se excluye por completo, pero recibe menor prioridad; aquí se acepta
pass
# Hairpin
if check_hairpin and check_self_complementarity(c.protospacer):
continue
filtered.append(c)
return filtered

Paso 4. Llamar a crisprScore de R desde Python mediante rpy2

El paquete crisprScore integra la mayoría de los métodos de puntuación estándar, incluidos CRISPRon, DeepHF, CRISPRon-BE, CFD, Hsu-Zhang y 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:
"""Invoca desde Python varios modelos de puntuación de R crisprScore."""
def __init__(self):
# Carga paquetes de R; antes deben instalarse con BiocManager::install en R
self.crisprscore = importr("crisprScore")
self.base = importr("base")
def crispron(self, contexts: list[str]) -> list[float]:
"""Puntuación on-target CRISPRon. Cada entrada es un contexto de 30 bp: 4 upstream + 20 protospacer + 3 PAM + 3 downstream."""
r_input = StrVector(contexts)
try:
result = self.crisprscore.getCRISPRonScores(r_input)
return list(result)
except Exception as e:
print(f"Falló CRISPRon: {e}")
return [float("nan")] * len(contexts)
def deep_hf(self, protospacers_with_pam: list[str], enzyme: str = "WT") -> list[float]:
"""Puntuación DeepHF. Cada entrada tiene 23 bp: 20 de protospacer + 3 de PAM. Enzima: 'WT', 'ESP' o '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"Falló DeepHF: {e}")
return [float("nan")] * len(protospacers_with_pam)
def crispron_be(self, contexts: list[str], editor: str = "ABE8e") -> list[float]:
"""Eficiencia de edición de bases CRISPRon-BE.
Opciones de editor: 'ABE8e' (adenina), 'BE4max' (citosina), etc.
"""
r_input = StrVector(contexts)
try:
result = self.crisprscore.getCRISPRonBEScores(r_input, editor=editor)
return list(result)
except Exception as e:
print(f"Falló 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:
"""Puntuación off-target MIT/Hsu-Zhang con penalización de mismatch dependiente de la posición."""
try:
result = self.crisprscore.getMITScores(
StrVector([protospacer]),
StrVector([target_dna]),
)
return float(list(result)[0])
except Exception:
return 0.0

Configuración del entorno de R (consulte los comandos del texto principal):

bash
# En la consola de R
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install(c(
"crisprScore", "crisprDesign",
"BSgenome.Hsapiens.UCSC.hg38",
"crisprBase"
))

Paso 5. Exploración remota de off-target con CRISPOR

CFD produce una puntuación local para un solo par. CRISPOR es una opción estándar para explorar off-target en todo el genoma [6]; CRISPRitz es una alternativa local.

python
def query_crispor_offtargets(
protospacer: str,
genome: str = "hg38",
max_mismatches: int = 4,
timeout: int = 300,
) -> list[dict]:
"""Escanea off-targets en todo el genoma mediante la API remota de CRISPOR.
Returns: [{genome_pos, mismatches, cfd_score, gene}, ...]
"""
# Endpoint de producción: http://crispor.tefor.net/crispor.py
# Aquí es un wrapper conceptual; la API real devuelve HTML y requiere análisis
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()
# La respuesta real de CRISPOR es HTML/TSV; aquí solo se representa el análisis
# En producción se recomienda BeautifulSoup o la CLI local de crispor para obtener TSV
return _parse_crispor_html(resp.text)
except Exception as e:
print(f"Falló el escaneo off-target de CRISPOR ({protospacer}): {e}")
return []
def _parse_crispor_html(html: str) -> list[dict]:
"""Analiza la respuesta HTML de CRISPOR; stub conceptual, usar BeautifulSoup en producción."""
return [] # Completar en una implementación de producción
def compute_off_target_summary(off_targets: list[dict]) -> dict:
"""Resume los resultados del escaneo off-target."""
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"]]
# Seguridad global: invierte el CFD máximo (0 = peligro, 1 = seguro)
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,
}

Paso 6. Clasificación mediante ensemble

Los resultados de varios métodos se combinan en un ensemble ponderado. Los pesos óptimos para cada proveedor y tarea pueden ajustarse con datos de validación experimental.

python
import pandas as pd
DEFAULT_WEIGHTS = {
"crispron": 0.30,
"deep_hf": 0.25,
"crispron_be": 0.10, # Usar 0 cuando no sea un escenario de edición de bases
"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 de ranking que integra los modelos de puntuación."""
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
]
# Media ponderada después de normalizar
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)

Paso 7. Generación automática del informe de pedido para el laboratorio

Los cinco mejores gRNA se exportan en Markdown y CSV listos para solicitar a proveedores como Twist Bioscience o IDT.

python
def generate_lab_report(
ranked: pd.DataFrame,
output_md: Path,
output_csv: Path,
top_k: int = 5,
) -> None:
"""Genera un informe apto para realizar pedidos de laboratorio."""
top = ranked.head(top_k)
lines = [
f"# Informe para pedir los {top_k} mejores gRNA candidatos",
"",
f"Gen objetivo: **{top.iloc[0]['gene']}**",
f"Hora de generación: (completar con la hora de ejecución del pipeline)",
"",
"## Tabla resumen",
"",
"| 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 += [
"",
"## Secuencias para pedido (5' → 3')",
"",
"Si la secuencia espaciadora no contiene la G requerida por el promotor U6, añada una G al inicio o utilice un promotor alternativo como ExoScribe.",
"",
]
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 += [
"## Lista de comprobación previa al experimento",
"",
"- [ ] Volver a comprobar el alineamiento antes del pedido (BLASTn contra hg38 · GRCh38.p14)",
"- [ ] Preparar el vector SpCas9-HF1 (por ejemplo, pX330 o lentiCRISPR v2)",
"- [ ] Completar la prueba de micoplasma de la línea celular objetivo (K562)",
"- [ ] Diseñar los cebadores para el ensayo T7E1 o la secuenciación profunda de amplicones",
"- [ ] Pedir en paralelo un gRNA de control (no dirigido, por ejemplo sgLacZ)",
]
output_md.write_text("\n".join(lines), encoding="utf-8")
top.to_csv(output_csv, index=False)

Canalización integrada y ejemplo de ejecución

Función que integra todo el flujo y ejemplo de ejecución del escenario de 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] Obtención de la información de exones de {gene_symbol} (RefSeq {refseq_id})")
# En producción se recomienda obtener localmente las coordenadas de exones de un GTF GENCODE
exons = fetch_gene_exons_ucsc(gene_symbol)
if not exons:
# Alternativa: trata todo el mRNA como un único "exón" para una demostración sencilla
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)} exones")
print("[2/7] Escaneo de PAM (NGG)")
all_cands = scan_gene_all_exons(exons)
print(f" {len(all_cands)} candidatos sin filtrar")
print("[3/7] Filtro fisicoquímico")
filtered = filter_grna_physicochemical(all_cands)
print(f" {len(filtered)} candidatos superan el filtro")
print("[4/7] Ensamble de evaluadores on-target")
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] Escaneo off-target (CRISPOR remoto)")
off_summaries = []
for i, c in enumerate(filtered):
if i % 50 == 0:
print(f" Progreso {i}/{len(filtered)}")
offs = query_crispor_offtargets(c.protospacer)
off_summaries.append(compute_off_target_summary(offs))
print("[6/7] Clasificación del ensamble")
ranked = build_ensemble_ranking(filtered, on_scores, off_summaries)
ranked.to_csv(f"{output_prefix}_all_candidates.csv", index=False)
print(f"[7/7] Informe para pedir los {top_k} mejores")
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" Completado: {output_prefix}_top{top_k}.md")
return ranked
# Ejemplo de ejecución: knockout de BRCA1
# ranked = full_pipeline(
# gene_symbol="BRCA1",
# refseq_id="NM_007294",
# output_prefix="brca1_k562",
# editor=None, # SpCas9-HF1 nuclease KO
# top_k=5,
# )

Log de ejecución previsto (para referencia)

El estudiante puede esperar este tipo de log al ejecutarlo:

text
[1/7] Obtención de la información de exones de BRCA1 (RefSeq NM_007294)
      24 exones
[2/7] Escaneo de PAM (NGG)
      423 candidatos sin filtrar
[3/7] Filtro fisicoquímico
      197 candidatos superan el filtro
[4/7] Ensamble de evaluadores on-target
[5/7] Escaneo off-target (CRISPOR remoto)
      Progreso 0/197
      Progreso 50/197
      ...
[6/7] Clasificación del ensamble
[7/7] Informe para pedir los 5 mejores
      Completado: brca1_k562_top5.md

Rendimiento · Costo · Casos de fallo conocidos

Referencia de rendimiento (citando benchmarks públicos)

ScorrerDataset de benchmarkSpearman ρObservacionesFuente
Rule 4 (Doench 2014)Benchmark propio0.42Baseline de ingeniería manualDoench et al., Nat Biotechnol 2014
DeepCRISPRKim 2017 dataset0.651D CNNChuai et al., Genome Biol 2018 [1]
CRISPRon (Xiang 2021)Kim 20190.72CNN sensible al contextoXiang et al., Nat Commun 2021 [2]
DeepHF (WT SpCas9)Wang 20190.73RNN + attentionWang et al., Nat Commun 2019 [3]
DeepHF (SpCas9-HF1)Wang 20190.75Especializado en variante HF1Wang et al. 2019 [3]
CRISPRon-BE (ABE8e)ABEmax dataset0.68Edición de basesKim et al. 2022 [4]
BE-HiveBE4/ABE dataset0.75Predicción fuerte de la ventana de ediciónArbab et al. 2020
Ensemble (aprox. de este artículo)Replicación de benchmark0.77~0.82 (estimado)Promedio de múltiples scorersBenchmark de la comunidad [7]

Costo estimado de replicación para el usuario

  • Costo de API 0 (completamente local o CRISPOR remoto gratuito).
  • Basado en una GPU de consumo pequeña, el scoring de 500 gRNAs toma aprox. 1~3 minutos.
  • En caso de fallback a CPU, 5~15 minutos.
  • API remota de CRISPOR: se recomienda menos de 1 req por segundo, escaneo de 500 elementos toma aprox. 10~20 minutos.
  • Como alternativa local con crispritz: CPU 30 min + descarga de índice genómico de 20GB.

5 Casos de fallo conocidos (recopilados de la comunidad y literatura)

  1. Conflicto de entorno rpy2 (R 4.3 + Bioconductor 3.18 + rpy2 3.5) Síntoma: Fallo en la carga de librerías de R como libR.so symbol lookup error · PermissionError: could not load package. Causa: La ruta de las librerías de R está registrada doblemente entre conda vs R del sistema, común especialmente al mezclar macOS/Linux. Evitar: (a) Instalar todo, incluido R, con conda-forge dentro del entorno conda (conda install -c conda-forge r-base bioconductor-crisprscore), (b) Especificar explícitamente las variables de entorno R_HOME y LD_LIBRARY_PATH, (c) Aislar con un contenedor Docker (rocker/tidyverse + instalación de crisprScore), (d) Llamar a Rscript directamente con subprocess en lugar de rpy2 (evitar rpy2). Fuente: Problemas de GitHub de rpy2 [8]; hilo de crisprScore en el foro de soporte de Bioconductor.

  2. Incongruencia entre la puntuación CFD y los resultados de los efectos fuera del objetivo en experimentos reales Síntoma: Las gRNA que se consideran seguras (puntuación baja) en CFD provocan una edición inesperada fuera del objetivo en experimentos reales (GUIDE-seq y CIRCLE-seq). Causa: CFD aprende los pesos de la posición de desajuste (Doench 2016 [5]), pero no tiene en cuenta el estado de la cromatina, la metilación del ADN o el posicionamiento de los nucleosomas. Evitar: (a) Utilizar un conjunto de varios algoritmos de efectos fuera del objetivo (CFD + MIT/Hsu-Zhang + Elevation), (b) Aplicar una penalización separada a las regiones TSS y potenciadoras (consultar la anotación ENCODE), (c) Validar posteriormente con datos de efectos fuera del objetivo medidos, como GUIDE-seq y Digenome-seq, (d) Medir en paralelo al menos tres de los principales candidatos y seleccionar el final. Fuente: Tsai et al. "GUIDE-seq" Nat Biotechnol 2015 [9]; artículo de CFD de Doench 2016 [5]; documentación de CRISPOR [6].

  3. Interpretación errónea de la ventana de edición de BE (edición de bases) + efecto secundario Síntoma: La base editor con una puntuación obtenida con CRISPRon-BE edita también posiciones distintas al nucleótido objetivo (efecto secundario). Causa: Se supone que BE edita una ventana específica (por ejemplo, 4-8 nt) dentro del espaciador prototípico, pero en realidad se extiende de 5 a 10 nt, y la desaminasa de adenina (ABE) tiene un sesgo hacia un contexto de secuencia específico. Evitar: (a) Utilizar un conjunto de varios modelos de predicción para marcar la ventana de edición (BE-Hive, BE-DICT y CRISPRon-BE), (b) Indicar los posibles efectos secundarios por separado en el diseño experimental, (c) Considerar el puntificador de edición primaria (PE) (PRIDICT) cuando se requiera una edición precisa, (d) Realizar una simulación a nivel de codón para comprobar si hay cambios de codón no deseados en las posiciones C/A objetivo dentro de la ventana de edición. Fuentes: Anzalone et al. "Prime editing" Nature 2019 [10]; Arbab et al. "BE-Hive" Cell 2020.

  4. Límite de velocidad de la API remota de CRISPOR + retraso en la respuesta Síntomas: Si se escanean 500 gRNA de forma continua, más de la mitad fallan o se produce un tiempo de espera excesivo. Causa: El servidor remoto de CRISPOR tiene limitaciones en los recursos de CPU. Es un recurso comunitario. Solución: (a) Escaneo por lotes con crispritz + BWA + índice del genoma (se requiere descargar un índice del genoma de 20 GB), (b) Ejecución de CRISPOR localmente (instalación local del script de Python), (c) Intervalo de solicitud de 5 segundos o más, (d) Escaneo remoto de CRISPOR solo para los 20 mejores candidatos (100+ localmente). Fuente: Documentación oficial de CRISPOR, sección "uso por lotes" [6].

  5. Inconsistencia en las coordenadas de los exones debido al empalme alternativo de BRCA1 Síntomas: BRCA1 tiene múltiples variantes de empalme. NM_007294 es la variante canónica, pero en otras transcripciones (como NM_007297), el número y las coordenadas de los exones son diferentes. Causa: MyGene.info solo devuelve la variante canónica. Las isoformas que se expresan en los tejidos y líneas celulares reales pueden ser diferentes. Solución: (a) Analizar múltiples coordenadas de transcripciones del GTF de GENCODE, (b) Verificar la isoforma predominante mediante datos de expresión medidos de los tejidos y líneas celulares objetivo (GTEx y Human Protein Atlas), (c) Priorizar solo los exones comunes a múltiples isoformas (exones constitutivos). Fuente: Documentación de anotación de GENCODE [11]; Navegador del genoma UCSC, pista de BRCA1.

Ideas para la expansión

  • Expansión del evaluador de Prime Editing (PE): PE tiene un diseño complejo de pegRNA 5', pero es óptimo para la edición de precisión. Incorporar evaluadores más recientes como PRIDICT y DeepPE.
  • CRISPRi/CRISPRa (activación/interferencia): En lugar de KO, regular la expresión. Utilizar getCrispraiScores de crisprScore, dCas9-VP64 (activación) o dCas9-KRAB (interferencia).
  • Optimización de la combinación de múltiples gRNA (multiplex): Al realizar KO simultáneo de múltiples genes, minimizar la interferencia entre los objetivos fuera del objetivo (optimización de la combinación, programación lineal).
  • Reflejar el fondo genético del paciente: La posición del objetivo fuera del objetivo cambia en pacientes con SNP específicos (objetivo fuera del objetivo personalizado, consultar la anotación de gnomAD y UK Biobank).
  • Integración del posicionamiento de nucleosomas: Se aplica una penalización específica para las regiones ocluidas por nucleosomas utilizando datos de MNase-seq.
  • Síntesis in vitro de T7 frente a pool de oligos: La estrategia de puntuación difiere (los 5 primeros frente a los 200 primeros) según el tipo de pedido de gRNA (individual frente a pool).

Próximos episodios

  • Episodio 07 drug-target-gnn: Se preseleccionan los candidatos a objetivos para el knockout mediante gRNA utilizando la predicción de fármacos y objetivos de GNN.
  • Episodio 10 med-llm-reproduction: Se evalúa la reproducibilidad de un evaluador de puntuaciones de gRNA en varios LLM.
  • Episodio 12 single-cell-perturbation: Se predice in silico la respuesta celular de los candidatos a gRNA antes de realizar el knockout real (simulación de Perturb-seq).
  • Episodio 14 bio-mcp-agent: Se expone el diseño de gRNA como una herramienta MCP para ejecutar de forma autónoma la tarea: "Extrae gRNA de KO del exón 3-5 de BRCA1".

Referencias

  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 (sección relacionada con el episodio). 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 (artículo original de CFD)." Nature Biotechnology 2016. https://www.nature.com/articles/nbt.3437
  6. Herramienta web CRISPOR y documentación: http://crispor.tefor.net/ / https://github.com/maximilianh/crisporWebsite
  7. Paquete Bioconductor crisprScore: https://bioconductor.org/packages/release/bioc/html/crisprScore.html
  8. Problemas de GitHub de rpy2: 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. Anotación del genoma humano de 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 (análisis de resultados de la edición): 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. Protocolo estándar de Addgene para el pedido de sgRNA: https://www.addgene.org/guides/crispr/
  18. crispritz (escaneo local de efectos no deseados): https://github.com/pinellolab/CRISPRitz
  19. NCBI RefSeq NM_007294 (ARNm canónico de BRCA1): https://www.ncbi.nlm.nih.gov/nuccore/NM_007294
  20. API de MyGene.info: https://mygene.info/v3/api

💬 Preguntas y comentarios

0 comentarios

Puedes publicar sin iniciar sesión. Los comentarios de invitados no pueden editarse ni eliminarse después.

0/2000

Cargando...