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:
- DryBench ai-native #2 Fundamentos de redes neuronales
- DryBench ai-native #13 HuggingFace y APIs comerciales
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 reconocenNG(SpCas9-NG),NGN(SpG) oNRN(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
| Herramienta | Función | Licencia |
|---|---|---|
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 Python | GPL v2+ |
| Biopython | Parsing de FASTA · GenBank, búsqueda de PAM, complemento inverso | Licencia Biopython |
| API remota de CRISPOR o crispritz local | Escaneo de off-target en todo el genoma | Gratuito para fines académicos · GPL |
Bioconductor BSgenome.Hsapiens.UCSC.hg38 | Referencia del genoma humano | Artistic-2.0 |
| pandas · matplotlib | Tablas de resultados · Visualización | BSD |
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:
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).
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" # Obligatorio para NCBI
@dataclassclass 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).
@dataclassclass 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_candsPaso 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.
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 filteredPaso 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.
import numpy as npimport rpy2.robjects as rofrom rpy2.robjects.packages import importrfrom 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.0Configuración del entorno de R (consulte los comandos del texto principal):
# En la consola de Rif (!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.
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.
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.
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.
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:
[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.mdRendimiento · Costo · Casos de fallo conocidos
Referencia de rendimiento (citando benchmarks públicos)
| Scorrer | Dataset de benchmark | Spearman ρ | Observaciones | Fuente |
|---|---|---|---|---|
| Rule 4 (Doench 2014) | Benchmark propio | 0.42 | Baseline de ingeniería manual | Doench et al., Nat Biotechnol 2014 |
| DeepCRISPR | Kim 2017 dataset | 0.65 | 1D CNN | Chuai et al., Genome Biol 2018 [1] |
| CRISPRon (Xiang 2021) | Kim 2019 | 0.72 | CNN sensible al contexto | Xiang et al., Nat Commun 2021 [2] |
| DeepHF (WT SpCas9) | Wang 2019 | 0.73 | RNN + attention | Wang et al., Nat Commun 2019 [3] |
| DeepHF (SpCas9-HF1) | Wang 2019 | 0.75 | Especializado en variante HF1 | Wang et al. 2019 [3] |
| CRISPRon-BE (ABE8e) | ABEmax dataset | 0.68 | Edición de bases | Kim et al. 2022 [4] |
| BE-Hive | BE4/ABE dataset | 0.75 | Predicción fuerte de la ventana de edición | Arbab et al. 2020 |
| Ensemble (aprox. de este artículo) | Replicación de benchmark | 0.77~0.82 (estimado) | Promedio de múltiples scorers | Benchmark 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)
-
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 entornoR_HOMEyLD_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. -
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].
-
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.
-
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].
-
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
getCrispraiScoresde 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
- 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 - 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 - 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 - 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 - 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 - Herramienta web CRISPOR y documentación:
http://crispor.tefor.net//https://github.com/maximilianh/crisporWebsite - Paquete Bioconductor crisprScore:
https://bioconductor.org/packages/release/bioc/html/crisprScore.html - Problemas de GitHub de rpy2:
https://github.com/rpy2/rpy2/issues - 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 - 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 - Anotación del genoma humano de GENCODE:
https://www.gencodegenes.org/human/ - Kim HK et al. "Predicting the efficiency of prime editing guide RNAs in human cells (PRIDICT)." Nat Biotechnol 2023.
- Broad Institute CRISPResso2 (análisis de resultados de la edición):
https://github.com/pinellolab/CRISPResso2 - Bioconductor BSgenome.Hsapiens.UCSC.hg38:
https://bioconductor.org/packages/release/data/annotation/html/BSgenome.Hsapiens.UCSC.hg38.html - RTH-tools CRISPRon-BE GitHub:
https://github.com/RTH-tools/crispron-BE - 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 - Protocolo estándar de Addgene para el pedido de sgRNA:
https://www.addgene.org/guides/crispr/ - crispritz (escaneo local de efectos no deseados):
https://github.com/pinellolab/CRISPRitz - NCBI RefSeq NM_007294 (ARNm canónico de BRCA1):
https://www.ncbi.nlm.nih.gov/nuccore/NM_007294 - API de MyGene.info:
https://mygene.info/v3/api