Variant Analysis using AlphaGenome Prerequisites 1. : Read the skill and follow its Setup instructions to ensure is installed and on PATH. 2. User Notification : If .licenses/alphagenome single variant analysis LICENSE.txt does not already exist in the workspace root directory then (1) prominently notify the user to check the terms at https://deepmind.google.com/science/alphagenome/, then (2) create the file recording the notification text and timestamp. 3. file : Make sure the file exists in your home directory. Create one if it does not exist. 4. : This skill requires an API key to function…

) # Remove leading $\n if name and name.lower() != 'nan':\n # Only add name if it provides new info (not just a duplicate of target)\n if name not in parts:\n parts.append(name)\n\n parts.append(label_suffix)\n return ' '.join(parts).strip()\n\n\ndef filter_preference(\n track: track_data.TrackData | junction_data.JunctionData,\n) -> track_data.TrackData | junction_data.JunctionData:\n \"\"\"Prefers 'total RNA-seq' over 'polyA plus RNA-seq' if both exist.\n\n Also aggressively dedups if 'Assay title' is not present but 'biosample_type'\n or implicit duplicates exist.\n\n Args:\n track: The track to filter.\n\n Returns:\n The filtered track.\n \"\"\"\n if track is None or len(track.metadata) \u003c 2:\n return track\n\n cols = track.metadata.columns\n # Check for 'Assay title' or 'Assay Title'\n title_col = None\n if 'Assay title' in cols:\n title_col = 'Assay title'\n elif 'Assay Title' in cols:\n title_col = 'Assay Title'\n\n if title_col:\n titles = track.metadata[title_col].unique()\n # Case insensitive check\n titles_lower = [t.lower() for t in titles if isinstance(t, str)]\n\n has_total = any('total rna-seq' in t for t in titles_lower)\n has_polya = any('polya plus rna-seq' in t for t in titles_lower)\n\n if has_total and has_polya:\n # Filter to total RNA-seq only\n mask = track.metadata[title_col].str.contains(\n 'total RNA-seq', case=False, na=False\n )\n return track.filter_tracks(np.flatnonzero(mask.values))\n\n return track\n\n\ndef get_deduplicated_indices(\n track: track_data.TrackData, label_suffix: str = '', max_tracks: int = 5\n) -> list[int]:\n \"\"\"Returns indices of tracks with unique labels.\n\n When multiple tracks produce the same label (via `dynamic_label`), only the\n index of the first track with that label is kept.\n\n Args:\n track: The track to deduplicate.\n label_suffix: Suffix to append to the label.\n max_tracks: Maximum number of tracks to keep.\n\n Returns:\n List of integer indices to keep.\n \"\"\"\n if track is None or track.metadata.empty:\n return []\n\n keep_indices = []\n seen_labels = set()\n\n for i in range(len(track.metadata)):\n sub_track = track.filter_tracks([i])\n label = dynamic_label(sub_track, label_suffix)\n\n if label not in seen_labels:\n seen_labels.add(label)\n keep_indices.append(i)\n\n if len(keep_indices) > max_tracks:\n print(\n f'Warning: Limiting {len(keep_indices)} tracks to top {max_tracks}'\n ' for clarity.'\n )\n keep_indices = keep_indices[:max_tracks]\n\n return keep_indices\n\n\ndef deduplicate_tracks(\n track: track_data.TrackData, label_suffix: str = '', max_tracks: int = 5\n) -> track_data.TrackData:\n \"\"\"Deduplicates tracks based on their generated label.\"\"\"\n keep_indices = get_deduplicated_indices(track, label_suffix, max_tracks)\n return track.filter_tracks(keep_indices)\n\n\ndef filter_and_create_overlay(\n ref_track: track_data.TrackData,\n alt_track: track_data.TrackData,\n ontology_curie: str,\n strand: str | None = None,\n label_suffix: str = '',\n color_ref: str = COLOR_REF,\n color_alt: str = COLOR_ALT,\n target_tf: str | None = None,\n) -> list[plot_components.OverlaidTracks] | None:\n \"\"\"Filters tracks by ontology and strand and returns OverlaidTracks.\"\"\"\n if ref_track is None or ref_track.metadata.empty:\n return []\n\n # Start with all True\n valid_mask = np.ones(len(ref_track.metadata), dtype=bool)\n\n # Filter by Ontology if column exists\n if 'ontology_curie' in ref_track.metadata.columns:\n # Robust comparison: cast to string and strip\n vals = ref_track.metadata['ontology_curie'].astype(str).str.strip().values\n target = str(ontology_curie).strip()\n valid_mask = valid_mask & (vals == target)\n\n # Filter by TF if provided\n if target_tf:\n target_cols = [\n 'target',\n 'antibody',\n 'bio_target',\n 'experiment_target',\n 'target_label',\n ]\n tf_cols = [c for c in target_cols if c in ref_track.metadata.columns]\n if tf_cols:\n target = str(target_tf).strip().lower()\n # Succinct pandas-based row-wise check across TF columns\n tf_mask = (\n ref_track.metadata[tf_cols]\n .astype(str)\n .apply(lambda s: s.str.contains(target, case=False, na=False))\n .any(axis=1)\n .values\n )\n valid_mask = valid_mask & tf_mask\n\n # Filter by Strand if provided and column exists\n if strand and 'strand' in ref_track.metadata.columns:\n strand_mask = (ref_track.metadata['strand'] == strand).values\n # Fallback: if strand filtering removes all tracks, ignore it\n if np.sum(valid_mask & strand_mask) > 0:\n valid_mask = valid_mask & strand_mask\n\n if np.sum(valid_mask) == 0:\n return []\n\n ref_filt = ref_track.filter_tracks(valid_mask)\n alt_filt = alt_track.filter_tracks(valid_mask)\n\n # Apply Preference Filter (Total > PolyA)\n ref_filt = filter_preference(ref_filt)\n alt_filt = filter_preference(alt_filt)\n\n # Deduplicate by label. We skip deduplicate_tracks() here and inline the\n # logic so we can filter BOTH ref and alt by the same indices, preserving\n # Ref/Alt pairing required by OverlaidTracks.\n if ref_filt is None or ref_filt.metadata.empty:\n return []\n\n keep_indices = get_deduplicated_indices(ref_filt, label_suffix)\n\n ref_filt = ref_filt.filter_tracks(keep_indices)\n alt_filt = alt_filt.filter_tracks(keep_indices)\n\n if ref_filt.metadata.empty:\n return []\n\n components = []\n # Create individual component for each unique track to ensure correct labeling\n for i in range(len(ref_filt.metadata)):\n # Extract single track (row)\n r_track = ref_filt.filter_tracks([i])\n a_track = alt_filt.filter_tracks([i])\n\n # Generate specific label for this track\n label = dynamic_label(r_track, label_suffix)\n\n try:\n comp = plot_components.OverlaidTracks(\n tdata={'REF': r_track, 'ALT': a_track},\n colors={'REF': color_ref, 'ALT': color_alt},\n ylabel_template=label,\n )\n components.append(comp)\n except (ValueError, RuntimeError) as e:\n print(f'Warning: OverlaidTracks init failed for {label}: {e}')\n\n return components\n\n\ndef compute_splicing_zoom(\n chrom: str,\n variant_start: int,\n ref_allele: str,\n gene_gtf: pd.DataFrame,\n ref_sashimi: ColoredSashimi,\n alt_sashimi: ColoredSashimi,\n clamp_interval: genome.Interval,\n) -> genome.Interval | None:\n \"\"\"Computes a zoom interval that includes flanking exons and junctions.\n\n Expands the view to show the nearest upstream and downstream exons relative\n to the variant, plus any significant splice junctions overlapping the region.\n\n Args:\n chrom: Chromosome string.\n variant_start: Variant start position.\n ref_allele: Reference allele string (used to compute variant end).\n gene_gtf: GTF DataFrame filtered to the target gene.\n ref_sashimi: Reference ColoredSashimi component.\n alt_sashimi: Alternate ColoredSashimi component.\n clamp_interval: Outer interval to clamp results within.\n\n Returns:\n A genome.Interval for the zoom view, or None if exons are unavailable.\n \"\"\"\n feature_col = 'Feature' if 'Feature' in gene_gtf.columns else 'feature'\n if feature_col not in gene_gtf.columns:\n return None\n\n exons = gene_gtf[gene_gtf[feature_col] == 'exon'].copy()\n if exons.empty:\n return None\n\n exons = exons.sort_values('Start')\n v_start = variant_start\n v_end = variant_start + len(ref_allele)\n\n # Start with a small window around the variant.\n new_start = v_start - 200\n new_end = v_end + 200\n\n # Extend to the nearest flanking exons.\n upstream_exons = exons[exons['End'] \u003c v_start]\n if not upstream_exons.empty:\n new_start = min(new_start, upstream_exons.iloc[-1]['Start'] - 100)\n\n downstream_exons = exons[exons['Start'] > v_end]\n if not downstream_exons.empty:\n new_end = max(new_end, downstream_exons.iloc[0]['End'] + 100)\n\n # Include any exons overlapping the variant itself.\n overlapping = exons[(exons['Start'] \u003c= v_end) & (exons['End'] >= v_start)]\n if not overlapping.empty:\n new_start = min(new_start, overlapping['Start'].min() - 100)\n new_end = max(new_end, overlapping['End'].max() + 100)\n\n # Expand to include significant splice junctions.\n all_juncs = []\n try:\n all_juncs.extend(ref_sashimi.get_junctions(clamp_interval))\n all_juncs.extend(alt_sashimi.get_junctions(clamp_interval))\n except (ValueError, IndexError, AttributeError) as e:\n print(f'Warning: Failed to fetch junctions for zoom: {e}')\n\n if all_juncs:\n max_score = max(j.k for j in all_juncs)\n base_interval_obj = genome.Interval(chrom, new_start, new_end)\n significant_juncs = [\n j\n for j in all_juncs\n if (j.k >= 5 or (max_score > 0 and j.k / max_score > 0.1))\n and genome.Interval(chrom, j.start, j.end).overlaps(base_interval_obj)\n ]\n\n if significant_juncs:\n j_min = min(j.start for j in significant_juncs)\n j_max = max(j.end for j in significant_juncs)\n new_start = min(new_start, j_min)\n new_end = max(new_end, j_max)\n\n # Snap to exon boundaries near junction anchors.\n up_anchor = exons[\n (exons['End'] >= j_min - 100) & (exons['End'] \u003c= j_min + 100)\n ]\n if not up_anchor.empty:\n new_start = min(new_start, up_anchor['Start'].min() - 100)\n\n down_anchor = exons[\n (exons['Start'] >= j_max - 100) & (exons['Start'] \u003c= j_max + 100)\n ]\n if not down_anchor.empty:\n new_end = max(new_end, down_anchor['End'].max() + 100)\n\n # Clamp to the prediction interval to prevent out-of-bounds errors.\n new_start = max(new_start, clamp_interval.start)\n new_end = min(new_end, clamp_interval.end)\n\n return genome.Interval(chrom, new_start, new_end)\n\n\ndef resolve_tracks_argument(tracks_arg: str | None) -> dict[str, Any]:\n \"\"\"Resolves --tracks argument to list of OutputTypes and flags.\"\"\"\n if not tracks_arg:\n # Default: RNA_SEQ and DNASE if nothing specified\n return {\n 'outputs': [dna_client.OutputType.RNA_SEQ, dna_client.OutputType.DNASE],\n 'show_splicing': False,\n 'show_regulatory': True,\n }\n\n arg_list = [t.strip().lower() for t in tracks_arg.split(',')]\n outputs = set()\n show_splicing = False\n show_regulatory = False\n\n # Macros\n if 'all' in arg_list:\n arg_list = ['splicing', 'regulatory']\n\n if 'splicing' in arg_list:\n outputs.update([\n dna_client.OutputType.RNA_SEQ,\n dna_client.OutputType.SPLICE_JUNCTIONS,\n dna_client.OutputType.SPLICE_SITE_USAGE,\n dna_client.OutputType.SPLICE_SITES,\n ])\n show_splicing = True\n\n if 'regulatory' in arg_list:\n outputs.update([\n dna_client.OutputType.DNASE,\n dna_client.OutputType.CHIP_TF,\n dna_client.OutputType.CHIP_HISTONE,\n ])\n show_regulatory = True\n\n # Individual output type overrides (e.g. --tracks=dnase,chip_tf)\n if 'rna_seq' in arg_list or 'expression' in arg_list:\n outputs.add(dna_client.OutputType.RNA_SEQ)\n if 'dnase' in arg_list:\n outputs.add(dna_client.OutputType.DNASE)\n show_regulatory = True\n if 'chip' in arg_list or 'chip_tf' in arg_list:\n outputs.add(dna_client.OutputType.CHIP_TF)\n show_regulatory = True\n if 'chip_histone' in arg_list:\n outputs.add(dna_client.OutputType.CHIP_HISTONE)\n show_regulatory = True\n\n return {\n 'outputs': list(outputs),\n 'show_splicing': show_splicing,\n 'show_regulatory': show_regulatory,\n }\n\n\ndef load_target_gene_info(\n gtf: pd.DataFrame, gene_name: str, interval_1mb: genome.Interval\n) -> tuple[\n genome.Interval, str | None, list[transcript_utils.Transcript], pd.DataFrame\n]:\n \"\"\"Loads gene info from GTF and returns interval, strand, transcripts, and filtered GTF.\"\"\"\n gene_gtf = gtf[gtf['gene_name'] == gene_name]\n if gene_gtf.empty:\n print(f'Gene {gene_name} not found.')\n gene_interval = interval_1mb\n strand = None\n transcripts = []\n else:\n gene_interval = gene_annotation.get_gene_interval(\n gene_gtf, gene_symbol=gene_name\n )\n strand = gene_gtf['Strand'].iloc[0]\n print(f'Target Gene: {gene_name} ({strand})')\n\n # Extract transcripts\n print('Filtering to longest transcripts...')\n longest_gene_gtf = gene_annotation.filter_to_longest_transcript(gene_gtf)\n transcript_extractor = transcript_utils.TranscriptExtractor(\n longest_gene_gtf\n )\n transcripts = transcript_extractor.extract(\n gene_interval.resize(gene_interval.width + 10000)\n )\n # Filter transcripts to gene strand\n transcripts = [t for t in transcripts if t.strand == strand]\n\n if not transcripts:\n print(\n 'WARNING: No transcripts found for gene on the expected strand.'\n ' Transcript track will be missing.'\n )\n\n return gene_interval, strand, transcripts, gene_gtf\n\n\ndef get_rna_seq_components(\n prediction: dna_client.VariantOutput,\n requested_outputs: list[dna_client.OutputType],\n ontology: str,\n strand: str | None,\n pretty_id: str,\n) -> list[plot_components.OverlaidTracks]:\n \"\"\"Returns RNA-seq overlay components.\"\"\"\n if dna_client.OutputType.RNA_SEQ not in requested_outputs:\n return []\n rna_comps = filter_and_create_overlay(\n prediction.reference.rna_seq,\n prediction.alternate.rna_seq,\n ontology,\n strand=strand,\n label_suffix=f'\\n{pretty_id}\\nRNA',\n )\n return rna_comps or []\n\n\ndef add_splicing_components(\n prediction: dna_client.VariantOutput,\n requested_outputs: list[dna_client.OutputType],\n show_splicing: bool,\n variant: genome.Variant,\n variant_args: argparse.Namespace,\n gene_gtf: pd.DataFrame,\n interval_1mb: genome.Interval,\n strand: str | None,\n pretty_id: str,\n components: list[plot_components.AbstractComponent],\n zoom_interval: genome.Interval,\n) -> genome.Interval:\n \"\"\"Adds splicing components and updates zoom interval.\"\"\"\n if not show_splicing:\n return zoom_interval\n\n # WHOLE GENE ZOOM\n if variant_args.view == 'whole_gene':\n if not gene_gtf.empty:\n gene_interval = gene_annotation.get_gene_interval(\n gene_gtf, gene_symbol=variant_args.gene\n )\n padding = max(1000, int(gene_interval.width * 0.05))\n zoom_interval = gene_interval.resize(gene_interval.width + padding * 2)\n print(f'Zooming to Whole Gene: {zoom_interval}')\n else:\n print(\n 'WARNING: --view whole_gene requested but gene not found in GTF.'\n ' Falling back to default zoom.'\n )\n\n # Pre-calculate Sashimi for Zoom\n try:\n if (\n 'ontology_curie'\n in prediction.reference.splice_junctions.metadata.columns\n ):\n ref_junc = prediction.reference.splice_junctions.filter_by_ontology(\n variant_args.ontology\n )\n alt_junc = prediction.alternate.splice_junctions.filter_by_ontology(\n variant_args.ontology\n )\n else:\n print(\n 'WARNING: Splicing tracks missing \"ontology_curie\" metadata.'\n ' Skipping ontology filtering.'\n )\n ref_junc = prediction.reference.splice_junctions\n alt_junc = prediction.alternate.splice_junctions\n except (KeyError, ValueError, AttributeError) as e:\n print(\n f'WARNING: Splicing ontology filtering failed: {e}. Using all'\n ' returned junctions.'\n )\n ref_junc = prediction.reference.splice_junctions\n alt_junc = prediction.alternate.splice_junctions\n\n ref_junc = filter_preference(ref_junc)\n alt_junc = filter_preference(alt_junc)\n\n ref_sashimi = ColoredSashimi(\n ref_junc,\n ylabel_template=f\"Ref Junc\\n{pretty_id}\\n({strand if strand else ''})\",\n color=COLOR_REF,\n normalize_values=False,\n forced_strand=strand,\n )\n alt_sashimi = ColoredSashimi(\n alt_junc,\n ylabel_template=f\"Alt Junc\\n{pretty_id}\\n({strand if strand else ''})\",\n color=COLOR_ALT,\n normalize_values=False,\n forced_strand=strand,\n )\n\n # Dynamic zoom: expand the zoom interval to include flanking exons\n # and significant splice junctions around the variant.\n if not gene_gtf.empty:\n computed_zoom = compute_splicing_zoom(\n chrom=variant_args.chrom,\n variant_start=variant.start,\n ref_allele=variant_args.ref,\n gene_gtf=gene_gtf,\n ref_sashimi=ref_sashimi,\n alt_sashimi=alt_sashimi,\n clamp_interval=interval_1mb,\n )\n if computed_zoom is not None:\n zoom_interval = computed_zoom\n\n # Splice Usage\n if dna_client.OutputType.SPLICE_SITE_USAGE in requested_outputs:\n usage_comps = filter_and_create_overlay(\n prediction.reference.splice_site_usage,\n prediction.alternate.splice_site_usage,\n variant_args.ontology,\n strand=strand,\n label_suffix=f'\\n{pretty_id}\\nUsage',\n )\n if usage_comps:\n components.extend(usage_comps)\n\n # Splice Sites\n if dna_client.OutputType.SPLICE_SITES in requested_outputs:\n ref_sites = filter_preference(prediction.reference.splice_sites)\n alt_sites = filter_preference(prediction.alternate.splice_sites)\n\n def split_sites(\n track: track_data.TrackData | None, pattern: str\n ) -> track_data.TrackData | None:\n if track is None or track.metadata.empty:\n return None\n mask = np.zeros(len(track.metadata), dtype=bool)\n if 'name' in track.metadata.columns:\n mask |= track.metadata['name'].str.contains(\n pattern, case=False, na=False\n )\n if 'Assay title' in track.metadata.columns:\n mask |= track.metadata['Assay title'].str.contains(\n pattern, case=False, na=False\n )\n return (\n track.filter_tracks(np.flatnonzero(mask.values))\n if np.any(mask)\n else None\n )\n\n ref_donor = split_sites(ref_sites, 'Donor')\n alt_donor = split_sites(alt_sites, 'Donor')\n if ref_donor:\n donor_comps = filter_and_create_overlay(\n ref_donor,\n alt_donor,\n variant_args.ontology,\n strand=strand,\n label_suffix=f'\\n{pretty_id}\\nSites (Donor, {strand})',\n )\n if donor_comps:\n components.extend(donor_comps)\n\n ref_acceptor = split_sites(ref_sites, 'Acceptor')\n alt_acceptor = split_sites(alt_sites, 'Acceptor')\n if ref_acceptor:\n acc_comps = filter_and_create_overlay(\n ref_acceptor,\n alt_acceptor,\n variant_args.ontology,\n strand=strand,\n label_suffix=f'\\n{pretty_id}\\nSites (Acceptor, {strand})',\n )\n if acc_comps:\n components.extend(acc_comps)\n\n components.append(ref_sashimi)\n components.append(alt_sashimi)\n\n return zoom_interval\n\n\ndef get_regulatory_components(\n prediction: dna_client.VariantOutput,\n requested_outputs: list[dna_client.OutputType],\n show_regulatory: bool,\n variant_args: argparse.Namespace,\n pretty_id: str,\n) -> list[plot_components.OverlaidTracks]:\n \"\"\"Returns regulatory overlay components (DNase, ChIP-TF).\"\"\"\n if not show_regulatory:\n return []\n\n components: list[plot_components.OverlaidTracks] = []\n if dna_client.OutputType.DNASE in requested_outputs:\n dnase_comp = filter_and_create_overlay(\n prediction.reference.dnase,\n prediction.alternate.dnase,\n variant_args.ontology,\n strand=None,\n label_suffix=f'\\n{pretty_id}\\nDNASE',\n )\n if dnase_comp:\n components.extend(dnase_comp)\n\n if dna_client.OutputType.CHIP_TF in requested_outputs:\n chip_tf_comps = filter_and_create_overlay(\n prediction.reference.chip_tf,\n prediction.alternate.chip_tf,\n variant_args.ontology,\n strand=None,\n label_suffix=f'\\n{pretty_id}\\nCHIP_TF',\n target_tf=variant_args.tf,\n )\n if chip_tf_comps:\n components.extend(chip_tf_comps)\n\n return components\n\n\ndef render_plots(\n variant_args: argparse.Namespace,\n prediction: dna_client.VariantOutput,\n components: list[plot_components.AbstractComponent],\n zoom_interval: genome.Interval,\n variant: genome.Variant,\n requested_outputs: list[dna_client.OutputType],\n gene_gtf: pd.DataFrame,\n gene_interval: genome.Interval,\n) -> None:\n \"\"\"Renders default, detail, and whole-gene plots.\"\"\"\n print('Generating plot...')\n plot_path = os.path.join(\n variant_args.output_dir,\n f\"plot_{variant_args.tissue.replace(' ', '_')}\"\n f'_{variant_args.gene}_effects.png',\n )\n\n max_diff = 0.0\n if (\n prediction.reference.rna_seq is not None\n and not prediction.reference.rna_seq.metadata.empty\n and prediction.alternate.rna_seq is not None\n and not prediction.alternate.rna_seq.metadata.empty\n ):\n try:\n diff = np.max(\n np.abs(\n prediction.alternate.rna_seq.values\n - prediction.reference.rna_seq.values\n )\n )\n max_diff = max(max_diff, diff)\n except (ValueError, TypeError) as e:\n print(f'Warning: Could not compute RNA diff summary: {e}')\n\n summary_text = (\n variant_args.description\n if variant_args.description\n else f'Max DNASE/RNA Diff: {max_diff:.2f}'\n )\n\n try:\n plot_components.plot(\n components=components,\n interval=zoom_interval, # Tighter zoom or dynamic zoom\n annotations=[plot_components.VariantAnnotation([variant], alpha=0.8)],\n )\n plt.suptitle(\n f'{variant_args.gene} {variant_args.tissue}'\n f' {variant_args.chrom}:{variant_args.pos}'\n f':{variant_args.ref}>{variant_args.alt}'\n f'\\n{summary_text}\\nZoom:'\n f' {zoom_interval}'\n )\n plt.savefig(plot_path, dpi=150, bbox_inches='tight')\n plt.close()\n print(f'Saved plot to {plot_path}')\n except (ValueError, RuntimeError, OSError) as e:\n print(f'Failed to plot: {e}')\n\n print('Generating Detail View (+/- 50bp)...')\n detail_path = os.path.join(\n variant_args.output_dir,\n f\"plot_{variant_args.tissue.replace(' ', '_')}\"\n f'_{variant_args.gene}_detail.png',\n )\n\n detail_interval = genome.Interval(\n variant_args.chrom, variant_args.pos - 50, variant_args.pos + 50\n )\n\n try:\n plot_components.plot(\n components=components,\n interval=detail_interval,\n annotations=[plot_components.VariantAnnotation([variant], alpha=0.8)],\n )\n plt.suptitle(\n f'{variant_args.gene} {variant_args.tissue}'\n f' {variant_args.chrom}:{variant_args.pos} Detail View (+/- 50bp)'\n )\n plt.savefig(detail_path, dpi=150, bbox_inches='tight')\n plt.close()\n print(f'Saved detail plot to {detail_path}')\n except (ValueError, RuntimeError, OSError) as e:\n print(f'Failed to plot detail view: {e}')\n\n if dna_client.OutputType.RNA_SEQ in requested_outputs and not gene_gtf.empty:\n print('Generating Whole-Gene View (RNA-seq requested)...')\n filename = (\n f\"plot_{variant_args.tissue.replace(' ', '_')}\"\n f'_{variant_args.gene}_wholegene.png'\n )\n wholegene_path = os.path.join(variant_args.output_dir, filename)\n\n pad = int(gene_interval.width * 0.1)\n wholegene_interval = gene_interval.resize(gene_interval.width + pad)\n\n try:\n plot_components.plot(\n components=components,\n interval=wholegene_interval,\n annotations=[plot_components.VariantAnnotation([variant], alpha=0.8)],\n )\n plt.suptitle(\n f'{variant_args.gene} {variant_args.tissue} Whole Gene'\n f' View\\n{summary_text}'\n )\n plt.savefig(wholegene_path, dpi=150, bbox_inches='tight')\n plt.close()\n print(f'Saved whole-gene plot to {wholegene_path}')\n except (ValueError, RuntimeError, OSError) as e:\n print(f'Failed to plot whole-gene view: {e}')\n\n\ndef visualize_variant_effects(variant_args: argparse.Namespace) -> None:\n \"\"\"Main function to visualize variant effects.\"\"\"\n # Setup Output\n os.makedirs(variant_args.output_dir, exist_ok=True)\n\n # Initialize Client\n api_key = os.environ.get('ALPHAGENOME_API_KEY')\n if not api_key:\n raise ValueError('ALPHAGENOME_API_KEY environment variable not set.')\n\n dna_model = dna_client.create(\n api_key=api_key,\n address=API_ADDRESS,\n )\n\n # Variant Setup\n variant = genome.Variant(\n variant_args.chrom, variant_args.pos, variant_args.ref, variant_args.alt\n )\n\n # Context Interval (1Mb)\n seq_length = 2**20\n interval_1mb = genome.Interval(\n variant_args.chrom,\n variant_args.pos - seq_length // 2,\n variant_args.pos + seq_length // 2,\n )\n\n # Determine requested tracks\n track_config = resolve_tracks_argument(variant_args.tracks)\n requested_outputs = track_config['outputs']\n show_splicing = track_config['show_splicing']\n show_regulatory = track_config['show_regulatory']\n\n print(\n f'Predicting variant effects for {variant_args.tissue}'\n f' ({variant_args.ontology})...'\n )\n print(f'Requested Outputs: {[o.name for o in requested_outputs]}')\n\n # Load GTF for gene info.\n gtf = load_gtf(getattr(variant_args, 'gtf_path', None))\n\n gene_interval, strand, transcripts, gene_gtf = load_target_gene_info(\n gtf, variant_args.gene, interval_1mb\n )\n\n # Predict\n prediction = dna_model.predict_variant(\n interval=interval_1mb,\n variant=variant,\n requested_outputs=requested_outputs,\n ontology_terms=[variant_args.ontology],\n )\n\n # --- Plotting ---\n components: list[plot_components.AbstractComponent] = []\n\n # 1. Transcripts (Verified: User wants these AT THE TOP)\n if transcripts:\n components.append(\n plot_components.TranscriptAnnotation(\n transcripts, label_name='gene_name'\n )\n )\n\n # Formatted Label for User Readability\n pretty_id = f'{variant_args.tissue}\\n({variant_args.ontology})'\n\n # 2. RNA-seq (Stranded)\n components.extend(\n get_rna_seq_components(\n prediction,\n requested_outputs,\n variant_args.ontology,\n strand,\n pretty_id,\n )\n )\n\n # Default Zoom: +/- 1000bp around variant (total 2000bp window).\n zoom_interval = variant.reference_interval.resize(2000)\n\n # 3. Splicing Tracks (Conditional)\n zoom_interval = add_splicing_components(\n prediction,\n requested_outputs,\n show_splicing,\n variant,\n variant_args,\n gene_gtf,\n interval_1mb,\n strand,\n pretty_id,\n components,\n zoom_interval,\n )\n # 4. Regulatory Tracks (DNASE, CHIP_TF, CHIP_HISTONE)\n components.extend(\n get_regulatory_components(\n prediction,\n requested_outputs,\n show_regulatory,\n variant_args,\n pretty_id,\n )\n )\n\n render_plots(\n variant_args,\n prediction,\n components,\n zoom_interval,\n variant,\n requested_outputs,\n gene_gtf,\n gene_interval,\n )\n\n\ndef main(argv: list[str] | None = None) -> None:\n \"\"\"Main entry point for the variant effects visualization CLI tool.\"\"\"\n dotenv.load_dotenv(os.path.expanduser('~/.env'))\n parser = argparse.ArgumentParser(\n description='Visualize variant effects using AlphaGenome predictions.'\n )\n parser.add_argument(\n '--chrom',\n required=True,\n help='Chromosome (e.g., chr17).',\n )\n parser.add_argument(\n '--pos',\n type=int,\n required=True,\n help='Position (1-based).',\n )\n parser.add_argument(\n '--ref',\n required=True,\n help='Reference allele.',\n )\n parser.add_argument(\n '--alt',\n required=True,\n help='Alternate allele.',\n )\n parser.add_argument(\n '--gene',\n required=True,\n help='Gene symbol.',\n )\n parser.add_argument(\n '--tissue',\n required=True,\n help='Tissue name for labeling.',\n )\n parser.add_argument(\n '--ontology', required=True, help='Ontology CURIE (e.g., UBERON:0002107).'\n )\n parser.add_argument('--output_dir', required=True, help='Output directory.')\n parser.add_argument(\n '--tracks',\n default='regulatory',\n help='Comma-separated track types: splicing, regulatory, all.',\n )\n parser.add_argument(\n '--tf', default=None, help='TF name to filter CHIP_TF tracks.'\n )\n parser.add_argument(\n '--view',\n choices=['default', 'whole_gene'],\n default='default',\n help=\"Zoom level. 'whole_gene' plots the entire gene interval.\",\n )\n parser.add_argument(\n '--description',\n default=None,\n help='Natural language summary of the variant effect.',\n )\n parser.add_argument(\n '--gtf_path',\n type=str,\n default=None,\n help=(\n 'Path or URL to the GTF feather file (defaults to'\n ' $ALPHAGENOME_GTF_PATH or the public GCS URL).'\n ),\n )\n args = parser.parse_args(argv)\n\n visualize_variant_effects(args)\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":37866,"content_sha256":"b8c22d2cd017265a8e616e2a7080789e4a9c725830ebfc687b95e68b0bfb9d3c"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Variant Analysis using AlphaGenome","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"uv","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": Read the ","type":"text"},{"text":"uv","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill and follow its Setup instructions to ensure ","type":"text"},{"text":"uv","type":"text","marks":[{"type":"code_inline"}]},{"text":" is installed and on PATH.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User Notification","type":"text","marks":[{"type":"strong"}]},{"text":": If .licenses/alphagenome_single_variant_analysis_LICENSE.txt does not already exist in the workspace root directory then (1) prominently notify the user to check the terms at https://deepmind.google.com/science/alphagenome/, then (2) create the file recording the notification text and timestamp.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" file","type":"text","marks":[{"type":"strong"}]},{"text":": Make sure the ","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" file exists in your home directory. Create one if it does not exist.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ALPHAGENOME_API_KEY","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": This skill requires an API key to function.","type":"text"}]},{"type":"paragraph","content":[{"text":"You can register for a key at https://deepmind.google.com/science/alphagenome/. You ","type":"text"},{"text":"MUST","type":"text","marks":[{"type":"strong"}]},{"text":" use the safe credentials protocol in the ","type":"text"},{"text":"credentials","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill to check for and request this key if this skill looks relevant to the user's request.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ALPHAGENOME_GTF_PATH","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (Optional)","type":"text","marks":[{"type":"strong"}]},{"text":": Accelerate gene/transcript lookup by pointing to a local copy of the GTF feather file instead of downloading from GCS:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"echo \"ALPHAGENOME_GTF_PATH=/path/to/local/gencode.v46.annotation.gtf.gz.feather\" >> ~/.env","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NEVER run ","type":"text","marks":[{"type":"strong"}]},{"text":"python3","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" or ","type":"text","marks":[{"type":"strong"}]},{"text":"python3 -c","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" directly.","type":"text","marks":[{"type":"strong"}]},{"text":" The system Python does not necessarily have pandas, numpy, and other key dependencies. ALWAYS use ","type":"text"},{"text":"uv run","type":"text","marks":[{"type":"code_inline"}]},{"text":" to run ALL Python code — including scripts, ad-hoc analysis files, and one-liners. Do not attempt to ","type":"text"},{"text":"pip install","type":"text","marks":[{"type":"code_inline"}]},{"text":" or create new venvs — ","type":"text"},{"text":"uv","type":"text","marks":[{"type":"code_inline"}]},{"text":" manages an isolated environment automatically.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Offline Only","type":"text","marks":[{"type":"strong"}]},{"text":": NEVER use external APIs (e.g., MyGene.info, Ensembl REST) for gene/transcript lookup. Use ","type":"text"},{"text":"lookup_gene_info.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the local GTF. If it fails, fix the environment/paths, do not switch to external APIs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"API Key is required","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"ALPHAGENOME_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":" must be set before running any script.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Notification","type":"text","marks":[{"type":"strong"}]},{"text":": If this skill is used, ensure this is mentioned in the output.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Report Format","type":"text","marks":[{"type":"strong"}]},{"text":": Always use the templates in ","type":"text"},{"text":"docs/report-templates.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for generating analysis reports, and ensure to include the table of top hits from the discovery scan.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Environment Setup & Troubleshooting","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Python Environment","type":"text"}]},{"type":"paragraph","content":[{"text":"All scripts must be executed using ","type":"text"},{"text":"uv run","type":"text","marks":[{"type":"code_inline"}]},{"text":", which manages an isolated virtual environment with the correct dependencies via ","type":"text"},{"text":"uv","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"uv run \u003cscript_name> [args...]","type":"text"}]},{"type":"paragraph","content":[{"text":"For ad-hoc scripts (e.g., inline analysis code saved to a temp file), pass the full path instead of a short name:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"uv run --project $SKILL_DIR /tmp/my_analysis.py --arg1 val1","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"[!NOTE] The first invocation resolves and installs dependencies (~10s). Subsequent runs use the cached environment and start instantly. The cache lives in ","type":"text"},{"text":"~/.cache/uv/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common Issues","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Column Names","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"tidy_scores","type":"text","marks":[{"type":"code_inline"}]},{"text":" and metadata often use ","type":"text"},{"text":"gene_name","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"gene_symbol","type":"text","marks":[{"type":"code_inline"}]},{"text":") and ","type":"text"},{"text":"output_type","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"modality","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Always inspect ","type":"text"},{"text":"df.columns","type":"text","marks":[{"type":"code_inline"}]},{"text":" before filtering.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Large Genes","type":"text","marks":[{"type":"strong"}]},{"text":": Genes > 500kb (e.g., ","type":"text"},{"text":"USH2A","type":"text","marks":[{"type":"code_inline"}]},{"text":") break the ","type":"text"},{"text":"whole_gene","type":"text","marks":[{"type":"code_inline"}]},{"text":" view. Use ","type":"text"},{"text":"--view detail","type":"text","marks":[{"type":"code_inline"}]},{"text":" or manual regional windows instead.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sashimi Strand Error","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"plot_components.Sashimi","type":"text","marks":[{"type":"code_inline"}]},{"text":" does NOT accept a ","type":"text"},{"text":"strand","type":"text","marks":[{"type":"code_inline"}]},{"text":" argument directly. Filter input tracks instead.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"KeyError: 'ontology_curie'","type":"text","marks":[{"type":"strong"}]},{"text":": Not all tracks have ","type":"text"},{"text":"ontology_curie","type":"text","marks":[{"type":"code_inline"}]},{"text":". Check ","type":"text"},{"text":"track.metadata.columns","type":"text","marks":[{"type":"code_inline"}]},{"text":" before filtering.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python Path","type":"text","marks":[{"type":"strong"}]},{"text":": If ","type":"text"},{"text":"exec: \"python\": executable file not found","type":"text","marks":[{"type":"code_inline"}]},{"text":" occurs, ensure you are using ","type":"text"},{"text":"uv run","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of bare ","type":"text"},{"text":"python","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"python3","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NotImplementedError (pandas)","type":"text","marks":[{"type":"strong"}]},{"text":": \"iLocation based boolean indexing on an integer type is not available\". This occurs when using boolean masks with ","type":"text"},{"text":".iloc","type":"text","marks":[{"type":"code_inline"}]},{"text":" on integer-indexed DataFrames in newer pandas versions. ","type":"text"},{"text":"Fix","type":"text","marks":[{"type":"strong"}]},{"text":": Convert boolean masks to integer indices using ","type":"text"},{"text":"np.flatnonzero(mask)","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GTF Feather Case Sensitivity","type":"text","marks":[{"type":"strong"}]},{"text":": The AlphaGenome GTF Feather file uses ","type":"text"},{"text":"Capitalized","type":"text","marks":[{"type":"strong"}]},{"text":" column names (","type":"text"},{"text":"Feature","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Start","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"End","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Strand","type":"text","marks":[{"type":"code_inline"}]},{"text":") unlike standard GTF files. Always check ","type":"text"},{"text":"df.columns","type":"text","marks":[{"type":"code_inline"}]},{"text":" if getting KeyErrors.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"score_variant","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" ontology filtering","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"score_variant","type":"text","marks":[{"type":"code_inline"}]},{"text":" does NOT accept ","type":"text"},{"text":"ontology_terms","type":"text","marks":[{"type":"code_inline"}]},{"text":" as an argument. You must filter the returned AnnData objects manually by inspecting ","type":"text"},{"text":"adata.var","type":"text","marks":[{"type":"code_inline"}]},{"text":" columns. In contrast, ","type":"text"},{"text":"predict_variant","type":"text","marks":[{"type":"code_inline"}]},{"text":" DOES accept ","type":"text"},{"text":"ontology_terms","type":"text","marks":[{"type":"code_inline"}]},{"text":" directly.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sashimi Zoom Logic","type":"text","marks":[{"type":"strong"}]},{"text":": To ensure \"skipping\" arcs are visible, expand the zoom to include the ","type":"text"},{"text":"flanking exons","type":"text","marks":[{"type":"strong"}]},{"text":" rather than relying on junction overlap alone.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Junction Scores","type":"text","marks":[{"type":"strong"}]},{"text":": Raw ","type":"text"},{"text":"Junction","type":"text","marks":[{"type":"code_inline"}]},{"text":" objects from ","type":"text"},{"text":"prediction","type":"text","marks":[{"type":"code_inline"}]},{"text":" may be simple Intervals. Use ","type":"text"},{"text":"junction_data.get_junctions_to_plot(predictions=..., name=...)","type":"text","marks":[{"type":"code_inline"}]},{"text":" to retrieve objects with the ","type":"text"},{"text":".k","type":"text","marks":[{"type":"code_inline"}]},{"text":" (abundance/score) attribute.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"uv","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" Not Found","type":"text","marks":[{"type":"strong"}]},{"text":": If ","type":"text"},{"text":"exec: uv: not found","type":"text","marks":[{"type":"code_inline"}]},{"text":", follow the installation instructions in ","type":"text"},{"text":"Prerequisites","type":"text","marks":[{"type":"link","attrs":{"href":"#prerequisites","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Registry Authentication Error (401)","type":"text","marks":[{"type":"strong"}]},{"text":": If ","type":"text"},{"text":"uv","type":"text","marks":[{"type":"code_inline"}]},{"text":" fails with 401 Unauthorized for a private registry, set ","type":"text"},{"text":"UV_INDEX_URL=https://pypi.org/simple","type":"text","marks":[{"type":"code_inline"}]},{"text":" before running the script.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"alphagenome-api.md","type":"text","marks":[{"type":"link","attrs":{"href":"docs/alphagenome-api.md","title":null}}]},{"text":" — API reference and code patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"interpretation-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"docs/interpretation-guide.md","title":null}}]},{"text":" — Interpretation guide, score magnitude rules, ISM, and checklist.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"report-templates.md","type":"text","marks":[{"type":"link","attrs":{"href":"docs/report-templates.md","title":null}}]},{"text":" — Full report templates","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/visualize_variant_effects.py","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/visualize_variant_effects.py","title":null}},{"type":"code_inline"}]},{"text":" — Single-variant visualization template (Ref/Alt comparisons, Splicing).","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Splicing Zoom Strategy","type":"text","marks":[{"type":"strong"}]},{"text":": Uses a ","type":"text"},{"text":"Hybrid Approach","type":"text","marks":[{"type":"strong"}]},{"text":" for optimal visibility:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Base Interval","type":"text","marks":[{"type":"strong"}]},{"text":": Variant +/- 1 downstream and upstream exon (Structural Context).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Junction Expansion","type":"text","marks":[{"type":"strong"}]},{"text":": Expands to include the full span of any ","type":"text"},{"text":"significant splicing junction","type":"text","marks":[{"type":"strong"}]},{"text":" (e.g., exon skipping events that span multiple exons).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Anchor Enforcement","type":"text","marks":[{"type":"strong"}]},{"text":": Ensures the exons ","type":"text"},{"text":"anchoring","type":"text","marks":[{"type":"em"}]},{"text":" these long junctions are fully visible. ","type":"text"},{"text":"Lesson","type":"text","marks":[{"type":"em"}]},{"text":": Simple fixed windows (e.g., 2kb) or nearest-exon logic often fail for skipping events. Always use the ","type":"text"},{"text":"observed junction data","type":"text","marks":[{"type":"em"}]},{"text":" to drive zoom levels.","type":"text"}]}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/splicing/","type":"text","marks":[{"type":"link","attrs":{"href":"docs/examples/splicing/","title":null}},{"type":"code_inline"}]},{"text":" — Splicing analysis examples","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/model_limitation_RNU4ATAC/","type":"text","marks":[{"type":"link","attrs":{"href":"docs/examples/model_limitation_RNU4ATAC/","title":null}},{"type":"code_inline"}]},{"text":" — ncRNA structure limitation case study","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/polyadenylation_HBA2/","type":"text","marks":[{"type":"link","attrs":{"href":"docs/examples/polyadenylation_HBA2/","title":null}},{"type":"code_inline"}]},{"text":" — 3' UTR / Polyadenylation case study","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/regulatory/","type":"text","marks":[{"type":"link","attrs":{"href":"docs/examples/regulatory/","title":null}},{"type":"code_inline"}]},{"text":" — Regulatory variant examples","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/negative_result_GATA4/","type":"text","marks":[{"type":"link","attrs":{"href":"docs/examples/negative_result_GATA4/","title":null}},{"type":"code_inline"}]},{"text":" — Negative results (mathematical artefact)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/negative_result_TGFB3/","type":"text","marks":[{"type":"link","attrs":{"href":"docs/examples/negative_result_TGFB3/","title":null}},{"type":"code_inline"}]},{"text":" — Negative results (proxies)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/lookup_gene_info.py","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/lookup_gene_info.py","title":null}},{"type":"code_inline"}]},{"text":" — Gene & transcript lookup","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/resolve_ontology_terms.py","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/resolve_ontology_terms.py","title":null}},{"type":"code_inline"}]},{"text":" — Ontology term resolution (UBERON/CL IDs)","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"--------------------------------------------------------------------------------"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Code Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Broad Discovery Scan","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"score_variant","type":"text","marks":[{"type":"code_inline"}]},{"text":" across ","type":"text"},{"text":"differential scorers only","type":"text","marks":[{"type":"strong"}]},{"text":" to discover unexpected tissue effects.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from alphagenome.models import dna_client\nfrom alphagenome.models import variant_scorers\nfrom alphagenome.data import genome\nimport os\nimport pandas as pd\nimport dotenv\n\n# Load environment variables from ~/.env\ndotenv.load_dotenv(os.path.expanduser('~/.env'))\n\n# Setup API Key and Client\ndna_model = dna_client.create(api_key=os.environ.get('ALPHAGENOME_API_KEY'),\n address='dns:///gdmscience.googleapis.com:443')\n\n# Define Variant (example)\nvariant_str = \"chr2:1234:A>C\"\nchrom, pos_str, ref_alt = variant_str.split(':')\nref, alt = ref_alt.split('>')\npos = int(pos_str)\n\n# Use supported sequence length (e.g., 2**20 for optimal performance)\nSEQ_LENGTH = 2**20\ninterval = genome.Interval(chrom, pos - SEQ_LENGTH // 2, pos + SEQ_LENGTH // 2)\nvariant = genome.Variant(chrom, pos, ref, alt)\n\nscorers = [\n variant_scorers.RECOMMENDED_VARIANT_SCORERS[m]\n for m in variant_scorers.RECOMMENDED_VARIANT_SCORERS\n if \"ACTIVE\" not in m and \"CAGE\" not in m and \"PROCAP\" not in m\n]\n\nprint(f\"Scoring variant {variant_str}...\")\nscores_list = dna_model.score_variant(interval=interval, variant=variant, variant_scorers=scorers)\n\n# Process and Display Results\nall_dfs = []\nfor score_adata in scores_list:\n df = variant_scorers.tidy_scores([score_adata], match_gene_strand=True)\n if df is not None:\n all_dfs.append(df)\n\nif all_dfs:\n df = pd.concat(all_dfs)\n significant = df[df['quantile_score'].abs() > 0.995]\n ranked = significant.sort_values('raw_score', key=abs, ascending=False)\n print(\"Top Significant Hits:\")\n print(ranked[['biosample_name', 'gene_name', 'output_type', 'quantile_score', 'raw_score']])","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Extended Search for Disease-Relevant Tissues","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# Define keywords based on disease context\ndisease_keywords = [\"liver\", \"hepatocyte\"]\n\n# Filter for any match\nmask = df['biosample_name'].str.contains('|'.join(disease_keywords), case=False, na=False)\n\nrelevant_hits = df[mask].sort_values('raw_score', key=abs, ascending=False)\nprint(f\"\\n--- Extended Analysis (Keywords: {disease_keywords}) ---\")\nprint(relevant_hits.head(20)[['biosample_name', 'output_type', 'raw_score', 'quantile_score']])","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow Checklist","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Variant Analysis Progress:\n- [ ] Step 0: Review Golden Examples (MANDATORY)\n- [ ] Step 1: Create Output Folder and Setup\n- [ ] Step 2: Parse User Query & Research\n- [ ] Step 3: Resolve Tissues & Modalities\n- [ ] Step 4: Visualize & Save Plots\n- [ ] Step 5: Analyze Predictions (view plots, no code). MANDATORY: Read [interpretation-guide.md](docs/interpretation-guide.md) before interpreting results.\n- [ ] Step 6: Write Report, save it as `report.md` (MANDATORY)\n- [ ] Step 7: Self-Critique (view `report.md` to verify links & claims)\n- [ ] Step 8: Make artifact out of `report.md`","type":"text"}]},{"type":"hr","attrs":{"markup":"--------------------------------------------------------------------------------"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Multi-Variant Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"If multiple variants are specified, spawn sub-agents to run each variant analysis and then synthesize each ","type":"text"},{"text":"report.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" into a single report.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Script Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"lookup_gene_info","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Comprehensive gene and transcript lookup using","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":": : GTF data :","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"resolve_ontology_terms","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Biological terms → UBERON/CL/EFO IDs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"visualize_variant_effects","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"REF/ALT visualization (expression, regulatory,","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":": : splicing) :","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"analyze_ism","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"In-Silico Mutagenesis SeqLogo generation","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"interpret_splicing","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quantitative splicing analysis (delta scores,","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":": : junctions) :","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"visualize_genome_tracks","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Genomic track visualization for a region","type":"text"}]}]}]}]}]},"metadata":{"date":"2026-11-02","name":"alphagenome-single-variant-analysis","author":"@skillopedia","source":{"stars":3384,"repo_name":"science-skills","origin_url":"https://github.com/google-deepmind/science-skills/blob/HEAD/skills/alphagenome_single_variant_analysis/SKILL.md","repo_owner":"google-deepmind","body_sha256":"bd663b06a6b0188342244d7f19259ed993f7bbe33cf6062dcac5d244322b05ee","cluster_key":"b9f7a077335b0905e11648e23335e5bdbc687aea91c09a8ead1777c5ba111f14","clean_bundle":{"format":"clean-skill-bundle-v1","source":"google-deepmind/science-skills/skills/alphagenome_single_variant_analysis/SKILL.md","text_bytes":155089,"source_commit":"4de6786171f1479026da5d3a00f110703c8ba35e","skills_sh_rank":7928,"attachment_count":24,"text_attachments":24,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/alphagenome_single_variant_analysis/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"education-research","category_label":"Education"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"education-research","import_tag":"skills-sh-top20k-2026-11","description":"Analyzes genetic variant effects on gene expression (RNA-seq), chromatin accessibility (DNASE), histone marks (ChIP), and transcription factors using the AlphaGenome API. Use when the user asks about non-coding variant effects, pathogenicity, clinical significance, disease associations, functional effects, gene expression changes, splicing disruption, or regulatory effects in promoters and enhancers. Also use for resolving biological terms to tissue/cell-type ontologies (UBERON/CL) or analyzing variants in chr:pos:ref>alt format.\n"}},"renderedAt":1793666546472}

Variant Analysis using AlphaGenome Prerequisites 1. : Read the skill and follow its Setup instructions to ensure is installed and on PATH. 2. User Notification : If .licenses/alphagenome single variant analysis LICENSE.txt does not already exist in the workspace root directory then (1) prominently notify the user to check the terms at https://deepmind.google.com/science/alphagenome/, then (2) create the file recording the notification text and timestamp. 3. file : Make sure the file exists in your home directory. Create one if it does not exist. 4. : This skill requires an API key to function…