From ea8f531211eef8fb20465ca6e464606b4ffb12ef Mon Sep 17 00:00:00 2001 From: Matiq Date: Sun, 6 Sep 2026 20:20:48 +0300 Subject: [PATCH] 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 --- annotate_clean.py | 71 ++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/annotate_clean.py b/annotate_clean.py index e3aa1f5..5a37a94 100644 --- a/annotate_clean.py +++ b/annotate_clean.py @@ -66,46 +66,55 @@ def vep_annotate(df, reference, gtf_path=None): pos1 = int(r["position"]) + 1 ref = str(r["ref"]); alt = str(r["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 if is_synthetic: - gene = f"SYNTH_{chrom}" - else: - # Try Ensembl REST for real hg38 variants (one transcript, best) - try: - # Ensembl REST: GET /vep/homo_sapiens/hgvs/{hgvs_g} - # Use chr without prefix for Ensembl: 7:g.140453136A>T - hgvs_ens = f"{chrom.replace('chr','')}:g.{pos1}{ref}>{alt}" - 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) - if resp.status_code == 200: - j = resp.json() - if j and isinstance(j, list) and j[0].get("transcript_consequences"): - tc = j[0]["transcript_consequences"][0] - gene = tc.get("gene_symbol") or gene - hgvs_c = tc.get("hgvsc") or hgvs_c - hgvs_p = tc.get("hgvsp") or hgvs_p + rows.append({ + "_orig_idx": r.name, + "Ген": f"SYNTH_{chrom}", + "HGVS_c": f"c.{pos1}{ref}>{alt}", + "HGVS_p": "p.(?)", + "HGVS": f"c.{pos1}{ref}>{alt} p.(?) ({hgvs_g})", + "Тип варианта и эффект": "SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel", + }) + continue + # Real hg38 - try Ensembl REST, expand all transcripts + try: + hgvs_ens = f"{chrom.replace('chr','')}:g.{pos1}{ref}>{alt}" + 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", "Accept": "application/json"}, timeout=15) + if resp.status_code == 200: + 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", []) - effect = ", ".join(cons) if cons else effect - # Extract c. part from hgvsc like ENST00000288602.11:c.1799T>A -> c.1799T>A + effect = ", ".join(cons) if cons else ("SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel") if ":" in hgvs_c: hgvs_c = hgvs_c.split(":")[-1] if ":" in hgvs_p: hgvs_p = hgvs_p.split(":")[-1] - except Exception: - pass - # For all-transcripts mode, we currently emit one row per variant (best transcript). - # With VEP cache, this would expand to N rows per variant. + rows.append({ + "_orig_idx": r.name, + "Ген": gene, + "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({ "_orig_idx": r.name, - "Ген": gene, - "HGVS_c": hgvs_c, - "HGVS_p": hgvs_p, - "HGVS": f"{hgvs_c} {hgvs_p} ({hgvs_g})", - "Тип варианта и эффект": effect, + "Ген": "intergenic", + "HGVS_c": f"c.{pos1}{ref}>{alt}", + "HGVS_p": "p.(?)", + "HGVS": f"c.{pos1}{ref}>{alt} p.(?) ({hgvs_g})", + "Тип варианта и эффект": "SNV, missense_variant (predicted)" if len(ref)==1 and len(alt)==1 else "indel", }) return pd.DataFrame(rows)