Annotates variants with functional annotation scores from the FAVOR
(Functional Annotation of Variants Online Resource) database, read either
from the FAVOR v1 CSV chunk files or from the FAVOR 2.0 per-chromosome
Parquet files. Every input row receives a matching outcome
(match_tier), when matched the key of the database row that
supplied its annotation (favor_key), and the rsID evidence for that
row (rsid_check). Used in PI estimation to retrieve
pathogenicity-relevant features for trait-associated and control variants,
and to build the cohort aGDS that GLOW and STAAR scan.
annotate_favor(
variants,
favor_db_path,
favor_split_file = NULL,
features = .default_favor_features(),
output_csv = NULL,
output_agds = NULL,
update_gds = FALSE,
variant_filter = NULL,
match_method = "exact",
na_allele_method = "average",
na_handling = "keep",
use_xsv = TRUE,
verbose = 1,
favor_db_format = c("auto", "csv", "parquet"),
favor_release = NULL,
rsid_policy = c("require", "record")
)One of the following:
A data.frame with VarInfo column (CHR-POS-REF-ALT format), and
optionally an rsID column (the input's rsID per row), which
feeds the rsID check described in Details
An S3 object of class glow_pi_case_data or
glow_pi_control_data from prepare_PI_case_data or
prepare_PI_control_data
A path (character) to a GDS file containing variant data (requires
SeqArray package). Variants are extracted automatically, and the
GDS annotation/id (the rsID on a chip cohort) is read as the
rsID column.
Character. The database directory: either the FAVOR v1
CSV chunk files (chr1_1.csv, chr1_2.csv, ...) or the FAVOR 2.0
Parquet files (chromosome_1.parquet, ...).
Character or NULL. CSV backend only. Path to
FAVORdatabase_chrsplit.csv, which maps positions to chunk files. If
NULL (default), searches in this order: (1) favor_db_path, (2) the
package's bundled file.
Character vector. Annotation fields to extract, named as the FAVOR v1 columns. The default is the complete annotation content of the FAVOR Essential Database (30 fields): 20 numeric scores (17 annotation principal components, CADD PHRED, LINSIGHT, FATHMM-XF) plus 10 categorical fields (GENCODE category/info and exonic category/info, MetaSVM prediction, GeneHancer, CAGE, rDHS, rsID). The categorical fields are what the built-in coding masks read, so an aGDS built with the default is directly scannable by every built-in variant category. Fields the database does not carry are skipped and reported (FAVOR 2.0 lacks four older APC versions, so the default yields 26 fields there).
Character or NULL. If provided, saves annotated results to CSV file at this path
Character or NULL. If provided, saves annotated results to aGDS file at this path (requires SeqArray package)
Logical. If TRUE and input is a GDS file, write annotations back to the input GDS file, converting it to an aGDS format. Default is FALSE. Ignored if variants is not a GDS file path.
List or NULL. Optional filter to select a subset of variants. Works with all input types (GDS, data.frame, S3 objects). Supported filters:
list(chr = "21"): Filter by chromosome (works with all inputs)
list(chr = "21", start = 1e6, end = 5e6): Filter by position
range (works with all inputs)
list(variant_ids = c(1, 5, 10)): Filter by SeqArray variant
IDs (GDS input only)
Default is NULL (use all variants). For non-GDS input, chromosome and position are parsed from the VarInfo column.
Character. How allele-bearing variants are matched:
"exact" (default): the key as given only (STAAR-compatible)
"flexible": for SNVs, the lookup key is also rewritten against FAVOR's reference base by swapping REF and ALT or complementing both alleles, for cohorts whose alleles may differ from the reference in order or strand (genotyping-chip data). Indels always match by the exact key. See Details.
Character. How position-only keys (CHR-POS-NA-NA) are annotated from the SNV rows at the position (indel rows are never included; a position with no SNV row is "uncovered"):
"average" (default): numeric features averaged over the SNV rows' non-missing values, string features take the first non-empty value
"first": every feature from the first SNV row at the position
Character. How to handle missing annotation values
after matching (never changes match_tier):
"keep" (default): Keep NA values as-is
"zero": Replace NA with 0 in numeric features
"drop": Remove variants with any NA annotations from the returned table and the CSV. The aGDS writers keep every variant of the GDS (the file stays a copy of the cohort) and leave the dropped variants' annotation and tier entries empty.
Logical. CSV backend only. If TRUE (default) and the xsv CLI
tool is available, xsv fetches the database rows at the input positions
(a streaming join on position); otherwise each chunk is read with
data.table::fread() and filtered. Both give the same rows.
Integer. Verbosity level: 0=silent, 1=messages (default), 2=detailed messages
Character. "auto" (default), "csv" or "parquet".
Under "auto" the format is detected from the files in
favor_db_path; a directory holding both kinds, or neither, is an
error. (Placed after verbose so that positional calls written for
GLOWr 0.1.1 keep their meaning.)
Character or NULL. The FAVOR release the database files come from, recorded in the aGDS provenance. NULL records "unknown".
Character. What the rsID check does with a match:
"require" (default): a swapped, flipped or
flipped_swapped match whose check is differs or
favor_none is withheld as the non-match rsid_conflict
(its annotation stays missing; favor_key names the withheld
row). Exact matches and matches checked chip_none are kept,
so exact matching is unaffected and an input without rsIDs loses
nothing.
"record": every match is kept and the check is only recorded
in rsid_check, for diagnostics.
data.frame with the input columns (for GDS input also
variant_id and rsID), one column per served feature,
match_tier (the matching outcome of each row), favor_key
(the variant_vcf of the database row that supplied the
annotation, or of the row withheld under rsid_conflict; NA
otherwise) and rsid_check (the rsID evidence of every matched
row; NA for non-matches and position-only keys). Order of variants is
preserved from input.
Database formats:
FAVOR v1 CSV: chr{N}_{K}.csv chunk files keyed by
variant_vcf ("CHR-POS-REF-ALT"), with
FAVORdatabase_chrsplit.csv mapping positions to chunks (a copy is
bundled with GLOWr).
FAVOR 2.0 Parquet: chromosome_{N}.parquet, one file per
chromosome (1-22, X, Y), sorted by position, with the annotation in
nested struct columns, read leaf by leaf one row group at a time.
Requires the arrow package. The map from our field names to the Parquet sources is
internal (.favor2_field_map()); GeneHancer, GENCODE exonic
information and GENCODE information are serialized into the v1 string
layouts. FAVOR 2.0 stores scores as float32, which R reads as the exact
double value of the float32.
The users obtain the database from the FAVOR team; GLOWr does not redistribute it.
Matching (the lookup key is rewritten, never the data): Each input row is classified as an SNV (REF and ALT each one of A, C, G, T), an indel (A/C/G/T strings, not both of length one), a position-only key (both alleles "NA"), or unsupported (anything else, including symbolic alleles and multiallelic records). For an SNV at position p with FAVOR reference base r:
"exact": the key CHR-POS-REF-ALT exists.
"swapped" (flexible): r equals ALT and CHR-POS-ALT-REF exists.
"flipped" / "flipped_swapped" (flexible): r equals the complement of REF (or of ALT) and the complemented key exists.
Indels match by the exact key only, because a VCF indel's REF is the genome
sequence and its reciprocal key names a different variant. Non-matches carry
a reason: "unmatched_alt" (the position is covered and the reference base
is found, either by REF as given or, under flexible matching, after one of
the enabled transformations, but no row carries the resulting REF-ALT pair),
"unmatched_ref" (the position is covered, REF as given does not agree with
r, and no enabled transformation finds r), "uncovered" (no database row at the position),
"no_database" (the database has no file or chunk for the chromosome),
"unsupported_allele" and "rsid_conflict" (below). Position-only keys get
"position_only", with the features aggregated over the SNV rows at the
position only (na_allele_method); a position that carries no SNV row
is "uncovered". A tier records
the lookup transformation, not a verified strand: a non-palindromic SNV pair
at a covered position always resolves through the first four tiers,
including a pair of two non-reference alleles. The cohort's own alleles,
genotypes and keys are never modified.
The rsID as evidence, never a key. FAVOR carries dbSNP's rsID on
the row of the substitution dbSNP lists, and the other rows at the position
carry none, so agreement between the input's rsID and the matched row's
confirms the site and the allele pair. For every
matched row rsid_check records "same", "differs", "chip_none" (the
input carries no rsID of the form rs<digits>) or "favor_none" (the
database row carries none). The input's rsID is the GDS annotation/id
or the data.frame's rsID column; without either, every check is
"chip_none". Under rsid_policy = "require", the default, a
transformed match checked "differs" or "favor_none" becomes
"rsid_conflict"; "record" keeps it. The check reads
the database's rsID column whether or not rsid is a requested
feature.
Outputs: the aGDS writers align rows to the GDS through
variant.id, write one native-typed sub-node per feature under
annotation/info/FunctionalAnnotation, the matching outcome as
annotation/info/favor_match_tier, the rsID check as
annotation/info/favor_rsid_check, and provenance (database format,
files, sizes and SHA-256 when a SHA256SUMS file is present, release,
GLOWr version, matching settings, the rsID policy and source, the rows the
position-only aggregation uses, outcome and check counts, date) as
attributes of the FunctionalAnnotation folder.
Computational Complexity: the matching is vectorized and linear in the number of input rows plus the number of database rows at their positions. Reading dominates: the CSV backend streams each chunk that covers the input positions; the Parquet backend reads the position column of every row group and the requested leaf columns of the groups that hold input positions, one group at a time.
Zhou, H., Arapoglou, T., Li, X., et al. (2023). FAVOR: functional annotation of variants online resource and annotator for variation across the human genome. Nucleic Acids Research, 51(D1), D1300-D1311. doi:10.1093/nar/gkac966
Zhou, H., Verma, V., Li, X., et al. (2026). FAVOR 2.0: A reengineered functional annotation of variants online resource for interpreting genomic variation. Nucleic Acids Research, 54(D1), D1405-D1414. doi:10.1093/nar/gkaf1217
prepare_PI_case_data for case variant preparation
prepare_PI_control_data for control variant preparation
get_PI for PI estimation using annotated variants
if (FALSE) { # \dontrun{
# Annotate a chip cohort's GDS from FAVOR 2.0, rewriting the lookup key for
# allele order and strand, and withholding transformed matches that the
# chip's rsID does not confirm
annotate_favor(
variants = "data/chr22.gds",
favor_db_path = "data/favor2-db/FAVOR2.0",
match_method = "flexible",
rsid_policy = "require",
output_agds = "results/chr22_favor2.gds"
)
# Annotate case variants given by position only, from FAVOR v1 CSV
annotated_cases <- annotate_favor(
variants = cases,
favor_db_path = "data/FAVOR",
na_allele_method = "average"
)
table(annotated_cases$match_tier)
# GDS input with a position range filter
annotated <- annotate_favor(
variants = "data/genotypes.gds",
favor_db_path = "data/FAVOR",
variant_filter = list(chr = "21", start = 1e6, end = 5e6)
)
} # }