Skip to content

uvar — exhaustive precomputed variant annotation

uvar ("universal variant") is a per-chromosome Parquet database that precomputes annotation for essentially every possible single-nucleotide substitution in GRCh38, plus a large set of enumerated indels in repeat-rich regions — population frequencies, ClinVar classifications, gene consequence, and dozens of in-silico pathogenicity/conservation/regulatory predictors, all joined onto one row per variant. It lives at /mnt/cephfs/hot_nvme/uvar/ as 24 files (chr1.pchr22.p, chrX.p, chrY.p), built with DuckDB (created_by: DuckDB v1.3.2) and readable by any standard Parquet reader — the .p extension is just a naming convention, not a distinct format.

This page documents the data itself — model, categories, scale — and shows how iris reads it, via UvarVariantStore and UvarAnnotator (src/iris/adapters/driven/stores/uvar.py, src/iris/adapters/driven/annotators/uvar.py). Every example on this page ran against the real files.

Scale

24 files, one per chromosome, sorted by position, 416 columns wide:

File Rows Row groups Size
chr1.p 697,723,257 349 44.76 GB
chr2.p 728,249,325 364 46.06 GB
chr3.p 599,657,406 300 37.36 GB
chr4.p 574,493,843 288 34.03 GB
chr5.p 548,653,509 275 33.49 GB
chr6.p 514,950,784 258 31.96 GB
chr7.p 481,465,078 241 29.77 GB
chr8.p 438,236,670 220 27.15 GB
chr9.p 368,668,774 185 22.91 GB
chr10.p 403,649,527 202 25.55 GB
chr11.p 407,232,600 204 25.98 GB
chr12.p 403,205,116 202 26.01 GB
chr13.p 296,684,670 149 17.68 GB
chr14.p 274,216,494 138 17.37 GB
chr15.p 256,275,016 129 16.41 GB
chr16.p 247,862,947 124 16.30 GB
chr17.p 251,209,815 126 16.87 GB
chr18.p 242,369,265 122 14.17 GB
chr19.p 177,368,363 89 12.33 GB
chr20.p 193,559,674 97 12.43 GB
chr21.p 121,352,389 61 6.98 GB
chr22.p 118,627,486 60 7.58 GB
chrX.p 467,920,179 234 21.25 GB
chrY.p 79,283,050 40 1.93 GB
Total 8,892,915,237 ~0.55 TB

Row groups are ~2M rows each and the files are sorted by position, so every query below relies entirely on Parquet row-group statistics pruning — there is no secondary index, and none is needed. UvarVariantStore never loads a whole file; see Access patterns below.

Coverage model: SNVs + repeat-region indels

The name "universal SNV database" (from the adapter's own docstring) is a simplification worth correcting here. Most positions carry exactly 3 rows — one for each possible alternate base — which is the exhaustive-SNV core of the database:

position=6116000  ref=G  alt=A
position=6116000  ref=G  alt=C
position=6116000  ref=G  alt=T

But inside tandem-repeat / short-tandem-repeat stretches, uvar also enumerates plausible indel alleles anchored at that position — real example from chr21.p around position 6,002,800 (a poly-A/poly-T microsatellite):

position=6002800  ref=A     alt=C            # SNV
position=6002800  ref=A     alt=G            # SNV
position=6002800  ref=A     alt=T            # SNV
position=6002800  ref=ATT   alt=A            # 2bp deletion
position=6002800  ref=ATTTTATTTTATTTTATTATTTTATT           alt=A   # 26bp deletion
position=6002800  ref=ATTTTATTTTATTTTATTATTTTATTTTATT       alt=A   # 32bp deletion
position=6002800  ref=ATTTTATTTTATTTTATTATTTTATTTTATTTTATT  alt=A   # 37bp deletion

Row counts per position observed on chr21.p range from 3 (plain SNV site) up to 9 in dense repeat regions. Consumers that assume "3 rows per position" — e.g. to estimate how many rows a region query will return — should instead treat 3 as a floor, not a fixed count, in repeat-dense regions (main.gerp, main.sequence_context and the tandem-repeat annotation itself can be used to detect these).

Identity & coordinate columns

Column Type Meaning
vid uint64 Deterministic variant ID, packed from chromosome/position/allele where the encoding fits.
is_hashed bool True when the allele was too long to pack directly into vid (long indels) — 140,721 such rows on chr21 alone.
hash30 int32 Hash of the allele, populated only when is_hashed; vid alone is not sufficient to distinguish these rows.
chrom_id uint8 Numeric chromosome ID (matches chromosome).
chromosome int64 Chromosome number, no chr prefix (21, not "chr21" or "chr21").
position0 int32 0-based position (position - 1).
position int32 1-based position — the one iris uses everywhere (matches GenomicPosition's convention; see Coordinate convention).
pos_bin_1m int32 Position integer-divided into 1 Mb bins; used directly by query_region_columns(..., aggregate="pos_bin_1m") for genome-scale summaries without materializing row-level data.
ref_vcf / alt_vcf string VCF-style ref/alt (right-trimmed representation). This is what iris matches against Variant.as_key().
ref_annovar / alt_annovar string ANNOVAR-style left-aligned ref/alt — differs from *_vcf for a subset of indels; not used by the iris adapters.
variant_vcf string Precomputed display string, "{chromosome}-{position}-{ref}-{alt}", e.g. "21-6116000-G-A".

Schema by category

54 top-level columns, several deeply nested (416 leaf columns total). Grouped by what they annotate:

Gene / transcript consequence — three independent annotation sets

gencode, ucsc, and refseq are parallel, independently computed annotations against three different transcript catalogs — they can and do disagree on region_type/consequence for the same variant, since they use different transcript models.

Struct Fields
gencode region_type, genes: list<string>, consequence, transcripts: list<{gene, transcript_id, location, hgvsc, hgvsp}>
ucsc region_type, transcripts: list<string>, consequence, exonic_details: list<{transcript_id, location, hgvsc, hgvsp}>
refseq region_type, transcripts: list<string>, consequence, exonic_details: list<{gene, transcript_id, location, hgvsc, hgvsp}>

region_type observed values (from gencode, full-column scan of chr21.p, 121M rows):

intergenic, upstream, downstream, upstream;downstream,
intronic, exonic, exonic;splicing, splicing,
UTR3, UTR5, UTR5;UTR3,
ncRNA_exonic, ncRNA_intronic, ncRNA_exonic;splicing, ncRNA_splicing

consequence observed values (ANNOVAR-style exonic function terms):

nonsynonymous SNV, synonymous SNV, stopgain, stoploss, unknown,
frameshift deletion, frameshift insertion, frameshift substitution,
nonframeshift deletion, nonframeshift insertion, nonframeshift substitution
(null when the variant is non-exonic.)

Identifiers & clinical databases

Struct Fields
dbsnp rsid, rsid_all: list<string>
clinvar clnsig: list<string>, clndn: list<string>, clnrevstat, origin: uint16, origin_decoded: list<string>, geneinfo: list<{symbol, id}>, clndisdb, clnsigincl, clndisdbincl, clndnincl, gene
cosmic gene, transcript, cds, aa, hgvsc, hgvsp, hgvsg, sample_count: int32, is_canonical: bool, tier, so_term

clinvar.clnsig free-text terms (there are more in the raw data than iris maps — see ClinVar significance mapping below for the ones UvarAnnotator/UvarVariantStore actually translate to ClinicalSignificance).

In-silico pathogenicity predictors

Struct Notable fields
dbnsfp revel, mpc, vest4, mvp, gmvp, phactboost, mutformer, mutscore, varity_r, varity_er, interpro_domain, polyphen2_hdiv, polyphen2_hvar, mutation_taster, mutation_assessor, metasvm_pred, plus {score, pred} pairs for bayesdel, clinpred, esm1b, meta_rnn, meta_lr, m_cap, primate_ai, deogen2, mutpred2, sift4g, provean, list_s2, and aloft: {pred, confidence} — ~29 predictors total, mirroring dbNSFP's own column set
spliceai max_ds, symbol, ds_ag/ds_al/ds_dg/ds_dl (delta scores), dp_ag/dp_al/dp_dg/dp_dl (delta positions)
alphamissense max_pathogenicity, predictions: list<{uniprot_id, transcript_id, protein_variant, pathogenicity, class}>
mavedb gene_name, hgvs_pro, scoresets: list<{score, scoreset_urn}> — experimental deep mutational scanning data

Top-level scalar scores (not nested under dbnsfp): linsight, fathmm_xf, gpn_msa_score/gpn_msa_phred, jarvis_score/jarvis_phred, remm_score/remm_phred, ncer_percentile, gnomad_constraint_score/gnomad_cst_phred.

main — conservation, sequence context, and regulatory tracks (CADD-style)

A single wide struct bundling the annotation categories that CADD itself aggregates from:

Sub-struct Fields
sequence_context gc, cpg, gc_phred, cpg_phred
distance min_dist_tss, min_dist_tse
protein_predictions sift_cat, sift_val, polyphen_cat, polyphen_val, grantham
conservation priphcons, mamphcons, verphcons, priphylop, mamphylop, verphylop, bstatistic
chromhmm e1e25 — 25-state ChromHMM chromatin-state model
gerp rs, rs_pval, n, s
encode {raw, phred} for 13 ENCODE marks: h3k4me1/2/3, h3k9ac, h3k9me3, h3k27ac, h3k27me3, h3k36me3, h3k79me2, h4k20me1, h2afz, dnase, total_rna
variant_density {freq,rare,sngl}_{100,1000,10000}bp — 9 fields, local variant density at 3 window sizes
remap overlap_tf, overlap_cl
cadd raw, phred — this is the cadd_phred score UvarAnnotator extracts as a VariantScore

Composite / meta pathogenicity scores

Struct Fields
apc 13 "Annotation Principal Component" scores: conservation_v2, epigenetics{,_active,_repressed,_transcription}, local_nucleotide_diversity_v3, mappability, micro_rna, mutation_density, protein_function_v3, proximity_to_coding_v2, proximity_to_tsstes, transcription_factor
macie {raw, phred} for conserved, regulatory, anyclass, plus region
cv2f {raw, phred} for liver, baseline, mpra, lvs, combined
ncboost score, percentile, region, gene
pgboost list<{gene, score, percentile}>
funseq score: uint8, description
aloft score, description (genome-wide non-coding score; distinct from dbnsfp.aloft, which is the protein-coding LoF-tolerance predictor)

Regulatory elements

Column Fields
ccre ids, accessions, annotations, count: uint8 — ENCODE candidate cis-regulatory elements
genehancer id, feature_score, targets: list<{gene, score}>
cage cage_enhancer, cage_promoter, cage_tc
super_enhancer_ids list<string>

Population frequency

Struct Fields
gnomad_genome, gnomad_exome af, ac, an, nhomalt, filter, grpmax, af_xx/af_xy, ac_xx/an_xx/ac_xy/an_xy, populations: {afr,ami,amr,asj,eas,fin,mid,nfe,sas}: {af, af_xx, af_xy}, faf: {faf95_max, faf95_max_gen_anc, faf99_max, faf99_max_gen_anc}, quality: {qd, inbreeding_coeff, fs, mq, sor}, variant_info: {allele_type, variant_type, n_alt_alleles}, region_flags: {lcr, segdup, non_par}, functional: {revel_max, spliceai_ds_max, pangolin_largest_ds}
bravo bravo_an, bravo_ac, bravo_af, filter_status — TOPMed
tg tg_all, tg_afr, tg_amr, tg_eas, tg_eur, tg_sas — 1000 Genomes

gnomAD is present at both genome and exome cohort granularity as separate structs — a variant can have gnomad_genome.af populated, gnomad_exome.af null, or both.

Genome-wide structural context

Column Fields
recombination_rate scalar float
nucdiv scalar float — nucleotide diversity
mutation_rate mr, ar, mg, mc, pn, filter
mappability {bismap, umap} at read lengths k24, k36, k50, k100

ClinVar significance mapping

UvarAnnotator/UvarVariantStore translate clinvar.clnsig free-text terms into the domain's ClinicalSignificance enum via src/iris/adapters/driven/stores/uvar_columns.toml:

uvar clnsig term ClinicalSignificance
Pathogenic PATHOGENIC
Pathogenic_low_penetrance PATHOGENIC_LOW_PENETRANCE
Likely_pathogenic, Likely_pathogenic_low_penetrance LIKELY_PATHOGENIC
Uncertain_significance, Uncertain_risk_allele UNCERTAIN
Likely_benign LIKELY_BENIGN
Benign BENIGN
Conflicting_classifications_of_pathogenicity, Conflicting_interpretations_of_pathogenicity CONFLICTING
not_provided, not_classified NOT_PROVIDED
drug_response DRUG_RESPONSE
risk_factor RISK_FACTOR
protective PROTECTIVE
association ASSOCIATION
affects AFFECTS
other OTHER

This is the same ACMG/AMP vocabulary ClinvarAnnotator (the standalone ClinVar-TSV adapter) maps, kept in a separate TOML table because it parses uvar's own flattened struct field rather than the ClinVar VCF's CLNSIG INFO column.

Access patterns from iris

UvarVariantStore (stores/uvar.py) and UvarAnnotator (annotators/uvar.py) never load a whole chromosome file — every method pushes a predicate down to pyarrow.dataset, relying on row-group statistics to prune the scan. Column projection works at top-level struct granularity only: requesting "gnomad_genome" returns the entire nested struct (all 9 populations, faf, quality, …), not individual leaf fields — there is no per-leaf-path pushdown in this Parquet layout.

Registering the source

# ~/.iris/config.toml
[hosts.local]
kind = "local"
data_dir = "/mnt/cephfs/hot_nvme"

[sources.uvar]
host = "local"
path = "uvar"
from iris.adapters.driven.annotators.uvar import UvarAnnotator

annotator = UvarAnnotator.from_registry()  # resolves [sources.uvar]

UvarVariantStore.fetch() — region → VariantAnnotation

Full VariantAnnotationStore port conformance, for small (gene-sized) regions. Uses a fixed default column set (ref_vcf, alt_vcf, dbsnp, clinvar, gnomad_genome, dbnsfp, main) — never the full 416-column schema.

from iris.adapters.driven.stores.uvar import UvarVariantStore
from iris.domain.genomics.objects import GenomicPosition

store = UvarVariantStore(data_dir="/mnt/cephfs/hot_nvme/uvar")
region = GenomicPosition(chromosome="21", start=6_116_400, end=6_116_600)
annotations = store.fetch(region)

Live run against chr21.p:

603 VariantAnnotation in 0.854s   # 201 positions × 3 SNVs = 603
6116503 C > T  ClinicalSignificance.PATHOGENIC  (source=uvar_clinvar)

UvarVariantStore.query_region_columns() — bulk path with signal pushdown

uvar is exhaustive: a gene-sized region returns hundreds of thousands to millions of rows, almost all of them theoretical substitutions nobody has ever observed. require_population_signal=True pushes an "observed gnomAD frequency OR ClinVar entry" filter down into the same Parquet scan, instead of materializing every substitution and filtering in Python afterward:

from iris.adapters.driven.stores.uvar import COLUMN_GROUPS

cols = [*COLUMN_GROUPS["population_variation"], *COLUMN_GROUPS["clinical_significance"]]
table = store.query_region_columns(region, columns=cols, require_population_signal=True)

Live run, same 201 bp window: 1 row returned (of 603 theoretical) in 0.479s — the one ClinVar-Pathogenic C>T. (dbSNP membership is deliberately not used as a population signal here — a variant can have an rsID purely from clinical submission without any observed allele count.)

aggregate="pos_bin_1m" collapses a region to per-1Mb-bin variant counts — for genome/chromosome-scale zoom levels where row-level data would be impractical:

big_region = GenomicPosition(chromosome="21", start=1, end=46_709_983)
agg = store.query_region_columns(big_region, columns=[], aggregate="pos_bin_1m")

Live run: the full 46.7 Mb chromosome (121M rows) collapses to 42 bins in 1.18s — proving the pushdown prunes row groups rather than materializing everything before aggregating.

UvarVariantStore.query_positions_columns() — scattered point lookups

An isin predicate (still row-group-prunable) for arbitrary, non-contiguous positions — e.g. a variant list, rather than one window. This is what UvarAnnotator uses internally.

UvarAnnotator — annotate-by-key, merges onto existing VariantAnnotation

Unlike fetch(), which reconstructs VariantAnnotation objects from scratch for whatever falls in a window, UvarAnnotator matches by Variant.as_key() — the same contract DbNsfpAnnotator, CaddAnnotator, and every other VariantAnnotator use — and merges uvar's fields onto the input object via attrs.evolve(). That's what lets uvar run through the same annotate_vcf use case as any other source, chained or compared side-by-side:

from iris.applications.genomics.use_cases.annotate_vcf import annotate_vcf
from iris.adapters.driven.annotators.uvar import UvarAnnotator

[result] = annotate_vcf([variant_annotation], [UvarAnnotator(data_dir=UVAR_DIR, workers=8)])

Live run for 21:6116503 C>T (a bare VariantAnnotation with no prior enrichment, as a VcfReader would produce it):

annotated in 0.455s
scores:              (VariantScore(name='cadd_phred', type=PATHOGENICITY, value=22.9),)
classifications:      (ClinicalClassification(classification=PATHOGENIC, source='uvar_clinvar'),)
allele_frequencies:   ()   # no gnomAD genome AF for this variant
identity preserved:   True  # result.variant is the same object passed in

Only cadd_phred and revel are extracted as VariantScores and only gnomad_genome.af as an AlleleFrequency — see _extract_uvar_fields() in stores/uvar.py for the full mapping (shared by fetch() and UvarAnnotator so the column→domain-object mapping is defined exactly once). The 400+ remaining columns are available via query_region_columns()/query_positions_columns() for callers that need more than this fixed default set.

workers partitions by chromosome across a ThreadPoolExecutor, each worker opening its own UvarVariantStore (and so its own pyarrow.dataset) — verified to produce identical results to workers=1 (test_annotate_with_workers_matches_sequential).

Dashboard use cases built on COLUMN_GROUPS

src/iris/applications/genomics/use_cases/region_features/ (the region dashboard's backend) reads uvar through the same query_region_columns(), gated by require_population_signal and choosing an aggregation level from the region span (region_features/aggregation.py):

  • get_population_variation_tracks() — `COLUMN_GROUPS["population_variation"]
  • ["clinical_significance"]`, combined with MCPS and RGC-ME tracks.
  • get_insilico_tracks()COLUMN_GROUPS["insilico"] (dbnsfp, main, spliceai, alphamissense) alongside population_variation columns, so the frontend can show only observed/annotated variants (gosling_common._flatten_insilico_record mirrors the same signal gate).

Benchmarking against dbNSFP

projects/benchmarkings/uvar_vs_dbnsfp.py runs UvarAnnotator and DbNsfpAnnotator over the same variant subset through the same annotate_vcf use case, and reports coverage, cadd_phred/revel absolute-value agreement, ClinVar classification agreement, and per-source throughput — a concrete way to decide which source to trust for a given field before wiring it into a pipeline:

uv run python projects/benchmarkings/uvar_vs_dbnsfp.py \
    --vcf /path/to/valid.vcf.gz \
    --sample-size 500 --seed 42 \
    --output results/uvar_vs_dbnsfp.tsv

Design notes & caveats

  • Column projection is top-level only. columns=["gnomad_genome"] always pulls the whole struct (all 9 populations, faf, quality, region_flags, …); there is no per-leaf-field pushdown in this Parquet layout. Request narrower top-level groups (COLUMN_GROUPS) rather than the full 54-column schema when scanning large regions.
  • Exhaustive, not observational. Every theoretical SNV has a row whether or not it has ever been observed in any cohort. Row presence alone is not evidence of anything — always gate on a population/clinical signal (require_population_signal, or check gnomad_genome.af/clinvar.clnsig directly) before treating a match as meaningful.
  • fetch() never requests all 416 columns, by design — it's meant for gene-sized regions with a small fixed column set. Larger regions or different field needs should go through query_region_columns() directly with a purpose-specific column list.
  • UvarAnnotator only extracts a small slice into domain objects (cadd_phred, revel, one gnomAD-genome frequency, ClinVar classification). It is not a substitute for the raw query_*_columns() paths when a caller needs, say, SpliceAI or AlphaMissense as first-class VariantScores — that mapping does not exist yet in _extract_uvar_fields().
  • gencode/ucsc/refseq can disagree — they're independently computed against different transcript catalogs, not cross-validated against each other.
  • Row count per position is not fixed at 3 in repeat-dense regions — see Coverage model.

Reference

iris.adapters.driven.stores.uvar

UvarVariantStore — Parquet-backed adapter for the uvar universal SNV database.

uvar is a per-chromosome Parquet database with exhaustive coverage of every possible SNV in GRCh38 (3 rows per position, one per alternate allele), 416 columns of precomputed annotation (population frequencies, ClinVar, in-silico predictors, conservation, regulatory scores, ...). Files are large (up to ~46 GB per chromosome) and sorted by position, so this adapter relies entirely on pyarrow.dataset predicate pushdown (row-group statistics pruning) rather than any manual indexing.

Two access modes, both region-bounded (region.start/region.end must both be set — there is no "fetch the whole file" mode given the file sizes):

  • :meth:fetch — full port conformance (VariantAnnotationStore), maps a small fixed default column set into :class:VariantAnnotation. Intended for small regions (gene-level); never requests all 416 columns.
  • :meth:query_region_columns — low-level bulk path for the region-features use cases. Column projection works at TOP-LEVEL struct/scalar granularity only (e.g. requesting "gnomad_genome" returns the whole nested struct; there is no per-leaf-field pushdown). aggregate="pos_bin_1m" groups by the schema's existing 1 Mb bin column for genome/chromosome-level summaries instead of returning row-level data.

COLUMN_GROUPS module-attribute

COLUMN_GROUPS = {
    name: (load_str_tuple(_TOML, "column_groups", name))
    for name in (
        "population_variation",
        "clinical_significance",
        "insilico",
    )
}

Maps a dashboard "source" filter name to the top-level uvar columns it needs.

UvarVariantStore

Bases: VariantAnnotationStore

Region-scale variant store backed by per-chromosome uvar Parquet files.

ATTRIBUTE DESCRIPTION
data_dir

Directory containing chr{1..22,X,Y}.p Parquet files.

TYPE: Path

file_pattern

Filename template, formatted with the canonical chromosome name (e.g. "21", "X") — note the files use a chr prefix (chr21.p) even though the in-file chromosome column values do not.

TYPE: str

assembly

Genome assembly label stored in produced GenomicPosition objects.

TYPE: str

source

Label stored on produced VariantAnnotation.metadata.

TYPE: str

query_region_columns

query_region_columns(
    region,
    *,
    columns,
    aggregate=None,
    require_population_signal=False,
)

Return uvar rows within region, projected to columns (plus position/chromosome).

PARAMETER DESCRIPTION
region

1-based inclusive genomic window. Both start and end must be set — there is no unbounded query mode.

TYPE: GenomicPosition

columns

Top-level uvar column names to project (see COLUMN_GROUPS for the dashboard-source groupings). Struct columns are returned whole (no per-leaf-field projection).

TYPE: Sequence[str]

aggregate

When "pos_bin_1m", groups rows by the schema's existing 1 Mb bin column and returns a per-bin variant count instead of row-level data — used for genome/chromosome-level zoom, where returning every row would be impractical.

TYPE: str | None DEFAULT: None

require_population_signal

uvar is exhaustive (every theoretical SNV, 3 rows/position) — for gene/cytoband-sized regions this is hundreds of thousands to millions of rows, almost all never-observed. When True, pushes down a row-group/predicate filter for an observed gnomAD frequency or a ClinVar classification (mirrors use_cases.gosling_common._has_population_signal, which otherwise has to apply this same gate in Python after the full exhaustive table is materialized). dbSNP membership is deliberately not used as a signal — see _has_population_signal for why.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Table

A pyarrow.Table. Empty (zero columns) if the chromosome has no

Table

backing file.

RAISES DESCRIPTION
ValueError

If region.start or region.end is None.

Source code in src/iris/adapters/driven/stores/uvar.py
def query_region_columns(
    self,
    region: GenomicPosition,
    *,
    columns: Sequence[str],
    aggregate: str | None = None,
    require_population_signal: bool = False,
) -> pa.Table:
    """Return uvar rows within region, projected to columns (plus position/chromosome).

    Args:
        region: 1-based inclusive genomic window. Both start and end
            must be set — there is no unbounded query mode.
        columns: Top-level uvar column names to project (see
            COLUMN_GROUPS for the dashboard-source groupings). Struct
            columns are returned whole (no per-leaf-field projection).
        aggregate: When ``"pos_bin_1m"``, groups rows by the schema's
            existing 1 Mb bin column and returns a per-bin variant count
            instead of row-level data — used for genome/chromosome-level
            zoom, where returning every row would be impractical.
        require_population_signal: uvar is exhaustive (every theoretical
            SNV, 3 rows/position) — for gene/cytoband-sized regions this
            is hundreds of thousands to millions of rows, almost all
            never-observed. When True, pushes down a row-group/predicate
            filter for an observed gnomAD frequency or a ClinVar
            classification (mirrors
            ``use_cases.gosling_common._has_population_signal``, which
            otherwise has to apply this same gate in Python *after* the
            full exhaustive table is materialized). dbSNP membership is
            deliberately not used as a signal — see
            ``_has_population_signal`` for why.

    Returns:
        A pyarrow.Table. Empty (zero columns) if the chromosome has no
        backing file.

    Raises:
        ValueError: If region.start or region.end is None.
    """
    if region.start is None or region.end is None:
        raise ValueError("region.start and region.end must be non-None for UvarVariantStore")

    dataset = self._dataset_for(region.chromosome)
    if dataset is None:
        return pa.table({})

    filt = (pc.field("position") >= region.start) & (pc.field("position") <= region.end)
    if require_population_signal:
        filt = filt & (
            pc.field("gnomad_genome", "af").is_valid()
            | (
                pc.list_value_length(  # pyright: ignore[reportAttributeAccessIssue]
                    pc.field("clinvar", "clnsig")
                )
                > 0
            )
        )
    scan_columns = list(dict.fromkeys((*columns, "position", "chromosome")))
    if aggregate == "pos_bin_1m" and "pos_bin_1m" not in scan_columns:
        scan_columns.append("pos_bin_1m")

    table = dataset.to_table(filter=filt, columns=scan_columns)

    if aggregate == "pos_bin_1m":
        table = table.group_by("pos_bin_1m").aggregate([("position", "count")])

    return table

query_positions_columns

query_positions_columns(chrom, positions, *, columns)

Point-lookup rows for an explicit, possibly scattered set of positions.

Uses an isin predicate rather than :meth:query_region_columns's >=/<= range — still row-group-prunable via pyarrow's statistics, but the right tool when the targets are arbitrary positions from a variant list (e.g. annotate-by-key) rather than one contiguous window. Used by UvarAnnotator.

PARAMETER DESCRIPTION
chrom

Chromosome, any spelling HUMAN_CHROMOSOMES resolves.

TYPE: str

positions

1-based positions to fetch.

TYPE: Sequence[int]

columns

Top-level uvar column names to project.

TYPE: Sequence[str]

RETURNS DESCRIPTION
Table

A pyarrow.Table. Empty (zero columns) if the chromosome has no

Table

backing file or positions is empty.

Source code in src/iris/adapters/driven/stores/uvar.py
def query_positions_columns(
    self, chrom: str, positions: Sequence[int], *, columns: Sequence[str]
) -> pa.Table:
    """Point-lookup rows for an explicit, possibly scattered set of positions.

    Uses an ``isin`` predicate rather than :meth:`query_region_columns`'s
    ``>=``/``<=`` range — still row-group-prunable via pyarrow's statistics,
    but the right tool when the targets are arbitrary positions from a variant
    list (e.g. annotate-by-key) rather than one contiguous window. Used by
    ``UvarAnnotator``.

    Args:
        chrom: Chromosome, any spelling ``HUMAN_CHROMOSOMES`` resolves.
        positions: 1-based positions to fetch.
        columns: Top-level uvar column names to project.

    Returns:
        A pyarrow.Table. Empty (zero columns) if the chromosome has no
        backing file or *positions* is empty.
    """
    if not positions:
        return pa.table({})
    dataset = self._dataset_for(chrom)
    if dataset is None:
        return pa.table({})

    filt = pc.field("position").isin(pa.array(positions, type=pa.int64()))
    scan_columns = list(dict.fromkeys((*columns, "position", "chromosome")))
    return dataset.to_table(filter=filt, columns=scan_columns)

fetch

fetch(region)

Return VariantAnnotation objects within region, using a fixed default column set.

Intended for small (gene-level) regions — always requests _DEFAULT_FETCH_COLUMNS, never the full 416-column schema. Larger regions should use :meth:query_region_columns directly with a narrower, purpose-specific column set.

PARAMETER DESCRIPTION
region

1-based inclusive genomic window.

TYPE: GenomicPosition

RETURNS DESCRIPTION
list[VariantAnnotation]

Possibly-empty list of VariantAnnotation.

RAISES DESCRIPTION
ValueError

If region.start or region.end is None.

Source code in src/iris/adapters/driven/stores/uvar.py
def fetch(self, region: GenomicPosition) -> list[VariantAnnotation]:
    """Return VariantAnnotation objects within region, using a fixed default column set.

    Intended for small (gene-level) regions — always requests
    ``_DEFAULT_FETCH_COLUMNS``, never the full 416-column schema. Larger
    regions should use :meth:`query_region_columns` directly with a
    narrower, purpose-specific column set.

    Args:
        region: 1-based inclusive genomic window.

    Returns:
        Possibly-empty list of VariantAnnotation.

    Raises:
        ValueError: If region.start or region.end is None.
    """
    table = self.query_region_columns(region, columns=_DEFAULT_FETCH_COLUMNS)
    results = (
        _row_to_variant_annotation(row, assembly=self.assembly, source=self.source)
        for row in table.to_pylist()
    )
    return [r for r in results if r is not None]

iris.adapters.driven.annotators.uvar

UvarAnnotator — enriches existing VariantAnnotation objects from the uvar Parquet store.

UvarVariantStore.fetch() is region-bounded: it reconstructs VariantAnnotation objects from scratch for whatever falls inside a window. This annotator instead matches by Variant.as_key(), the same annotate-by-key contract every other VariantAnnotator uses (DbNsfpAnnotator, McpsAFAnnotator), merging uvar's frequencies/classifications/scores onto the input objects via attrs.evolve rather than replacing them. This is what lets uvar be run through the annotate_vcf use case and compared against other annotators on equal footing.

UvarAnnotator

Bases: VariantAnnotator

Annotates variants with uvar's population frequency, ClinVar, and in-silico scores.

Point-lookup counterpart to UvarVariantStore: same fixed _DEFAULT_FETCH_COLUMNS set as fetch() (never the full 416-column schema), but keyed by variant identity instead of a genomic window.

ATTRIBUTE DESCRIPTION
data_dir

Directory containing chr{1..22,X,Y}.p Parquet files.

TYPE: Path

file_pattern

Filename template, see UvarVariantStore.

TYPE: str

workers

Number of chromosome-partitioned parallel queries. Each worker opens its own UvarVariantStore (and so its own pyarrow Dataset) — _dataset_cache is per-instance and not shared across threads. workers=1 (default) preserves sequential behaviour.

TYPE: int

from_registry classmethod

from_registry(source='uvar', registry=None)

Build a UvarAnnotator resolving source from the iris DataRegistry.

Source code in src/iris/adapters/driven/annotators/uvar.py
@classmethod
def from_registry(cls, source: str = "uvar", registry: Any = None) -> "UvarAnnotator":
    """Build a UvarAnnotator resolving *source* from the iris DataRegistry."""
    from iris.config import get_registry

    r = registry or get_registry()
    return cls(data_dir=r.resolve_path(source))

annotate

annotate(items)

Annotate variants with uvar frequencies, ClinVar classification, and scores.

Source code in src/iris/adapters/driven/annotators/uvar.py
def annotate(self, items: Sequence[VariantAnnotation]) -> list[VariantAnnotation]:
    """Annotate variants with uvar frequencies, ClinVar classification, and scores."""
    targets: set[_Key] = set()
    for va in items:
        maybe_key = va.variant.as_key()
        if maybe_key is None:
            continue
        chrom, pos, ref, alt = maybe_key
        targets.add((normalize_chromosome(chrom), pos, ref, alt))
    if not targets:
        return list(items)

    by_chrom: dict[str, set[int]] = {}
    for chrom, pos, _ref, _alt in targets:
        by_chrom.setdefault(chrom, set()).add(pos)

    hit_map = self._load(by_chrom, targets)
    return [self._enrich(va, hit_map) for va in items]