all transcripts via Ensembl REST (expand per variant, pan-cancer)

- vep_annotate now iterates all transcript_consequences
- timeout 15s, graceful fallback to single placeholder row
This commit is contained in:
2026-09-06 20:20:48 +03:00
parent a3589279b9
commit ea8f531211
+40 -31
View File
@@ -66,46 +66,55 @@ def vep_annotate(df, reference, gtf_path=None):
pos1 = int(r["position"]) + 1 pos1 = int(r["position"]) + 1
ref = str(r["ref"]); alt = str(r["alt"]) ref = str(r["ref"]); alt = str(r["alt"])
hgvs_g = f"{chrom}:g.{pos1}{ref}>{alt}" hgvs_g = f"{chrom}:g.{pos1}{ref}>{alt}"
gene = "intergenic"
hgvs_c = f"c.{pos1}{ref}>{alt}"
hgvs_p = f"p.(?)"
effect = "SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel"
is_synthetic = chrom.startswith("chr") and chrom[3:].isdigit() and int(chrom[3:]) <= 3 and pos1 < 6000 is_synthetic = chrom.startswith("chr") and chrom[3:].isdigit() and int(chrom[3:]) <= 3 and pos1 < 6000
if is_synthetic: if is_synthetic:
gene = f"SYNTH_{chrom}" rows.append({
else: "_orig_idx": r.name,
# Try Ensembl REST for real hg38 variants (one transcript, best) "Ген": f"SYNTH_{chrom}",
try: "HGVS_c": f"c.{pos1}{ref}>{alt}",
# Ensembl REST: GET /vep/homo_sapiens/hgvs/{hgvs_g} "HGVS_p": "p.(?)",
# Use chr without prefix for Ensembl: 7:g.140453136A>T "HGVS": f"c.{pos1}{ref}>{alt} p.(?) ({hgvs_g})",
hgvs_ens = f"{chrom.replace('chr','')}:g.{pos1}{ref}>{alt}" "Тип варианта и эффект": "SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel",
url = f"https://rest.ensembl.org/vep/homo_sapiens/hgvs/{hgvs_ens}?content-type=application/json" })
resp = requests.get(url, headers={"Content-Type": "application/json"}, timeout=5) continue
if resp.status_code == 200: # Real hg38 - try Ensembl REST, expand all transcripts
j = resp.json() try:
if j and isinstance(j, list) and j[0].get("transcript_consequences"): hgvs_ens = f"{chrom.replace('chr','')}:g.{pos1}{ref}>{alt}"
tc = j[0]["transcript_consequences"][0] url = f"https://rest.ensembl.org/vep/homo_sapiens/hgvs/{hgvs_ens}?content-type=application/json"
gene = tc.get("gene_symbol") or gene resp = requests.get(url, headers={"Content-Type": "application/json", "Accept": "application/json"}, timeout=15)
hgvs_c = tc.get("hgvsc") or hgvs_c if resp.status_code == 200:
hgvs_p = tc.get("hgvsp") or hgvs_p j = resp.json()
tcs = j[0].get("transcript_consequences", []) if j and isinstance(j, list) and j[0] else []
if tcs:
for tc in tcs:
gene = tc.get("gene_symbol") or "intergenic"
hgvs_c = tc.get("hgvsc") or f"c.{pos1}{ref}>{alt}"
hgvs_p = tc.get("hgvsp") or "p.(?)"
cons = tc.get("consequence_terms", []) cons = tc.get("consequence_terms", [])
effect = ", ".join(cons) if cons else effect effect = ", ".join(cons) if cons else ("SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel")
# Extract c. part from hgvsc like ENST00000288602.11:c.1799T>A -> c.1799T>A
if ":" in hgvs_c: if ":" in hgvs_c:
hgvs_c = hgvs_c.split(":")[-1] hgvs_c = hgvs_c.split(":")[-1]
if ":" in hgvs_p: if ":" in hgvs_p:
hgvs_p = hgvs_p.split(":")[-1] hgvs_p = hgvs_p.split(":")[-1]
except Exception: rows.append({
pass "_orig_idx": r.name,
# For all-transcripts mode, we currently emit one row per variant (best transcript). "Ген": gene,
# With VEP cache, this would expand to N rows per variant. "HGVS_c": hgvs_c,
"HGVS_p": hgvs_p,
"HGVS": f"{hgvs_c} {hgvs_p} ({hgvs_g})",
"Тип варианта и эффект": effect,
})
continue
except Exception:
pass
# Fallback single row
rows.append({ rows.append({
"_orig_idx": r.name, "_orig_idx": r.name,
"Ген": gene, "Ген": "intergenic",
"HGVS_c": hgvs_c, "HGVS_c": f"c.{pos1}{ref}>{alt}",
"HGVS_p": hgvs_p, "HGVS_p": "p.(?)",
"HGVS": f"{hgvs_c} {hgvs_p} ({hgvs_g})", "HGVS": f"c.{pos1}{ref}>{alt} p.(?) ({hgvs_g})",
"Тип варианта и эффект": effect, "Тип варианта и эффект": "SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel",
}) })
return pd.DataFrame(rows) return pd.DataFrame(rows)