biobouncer validates biological identifiers and inputs. It answers one question, “is this a valid identifier?”, with the same verdict in R and Python. This vignette walks through the sources, the checking modes, and the validation adapters using only offline data, so every chunk here runs without a network.
What can be checked
sources() lists the source databases biobouncer knows
about, and source_info() returns a table with a short
description of each.
sources()
#> [1] "bto" "cdd" "chebi" "chembl"
#> [5] "cl" "clinvar" "complexportal" "cosmic"
#> [9] "dbsnp" "doid" "drugbank" "ec"
#> [13] "eco" "efo" "ensembl" "go"
#> [17] "hgnc" "hgvs" "hp" "inchikey"
#> [21] "interpro" "mirbase" "mirbase_hairpin" "mondo"
#> [25] "mp" "ncbifam" "ncbitaxon" "ncit"
#> [29] "opentargets" "orphanet" "panther" "pato"
#> [33] "pdb" "pfam" "pharmgkb" "prints"
#> [37] "prosite" "reactome" "refseq" "rfam"
#> [41] "smart" "so" "uberon" "uniparc"
#> [45] "uniprot" "wikipathways"Pattern mode: is the string well-formed?
pattern mode is offline and deterministic. It checks the
shape of an identifier against the source’s pattern. It does not check
that the identifier exists.
check_id() returns a tibble with one row per input. Note
that it preserves the order and length of the input, and never collapses
an invalid value into a bare FALSE: the row tells you
why.
check_id(
c("MONDO:0005148", "mondo:5148", "GO:0006915"),
source_db = "mondo"
)
#> # A tibble: 3 × 9
#> input valid normalized suggestion source_db version species how error
#> <chr> <lgl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 MONDO:00051… TRUE MONDO:000… NA mondo NA NA patt… NA
#> 2 mondo:5148 FALSE NA MONDO:000… mondo NA NA patt… NA
#> 3 GO:0006915 FALSE NA NA mondo NA NA patt… NAThe normalized column holds the canonical form of a
valid input. The suggestion column holds a best-effort
correction for an invalid but mappable input, such as the lowercase
prefix above.
For just the verdict, use is_valid_id(), which returns a
logical vector.
is_valid_id(c("P04637", "p04637"), source_db = "uniprot")
#> [1] TRUE FALSECache mode: does the identifier exist in a snapshot?
cache mode checks existence against a pinned, offline
snapshot. A small sample snapshot ships with the package,
so the examples below need no downloads. A real analysis pins a dated
snapshot instead.
check_id(
c("MONDO:0005148", "MONDO:9999999"),
source_db = "mondo",
how = "cache",
version = "sample"
)
#> # A tibble: 2 × 9
#> input valid normalized suggestion source_db version species how error
#> <chr> <lgl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 MONDO:00051… TRUE MONDO:000… NA mondo sample NA cache NA
#> 2 MONDO:99999… FALSE NA NA mondo sample NA cache NAMONDO:9999999 is well-formed, so it passes
pattern mode, but it is not in the snapshot, so it fails
cache mode. Choosing the mode is choosing how strict you
want the check to be.
Snapshots are managed with a few helpers:
biobouncer_snapshots()
#> # A tibble: 14 × 4
#> source version n_ids location
#> <chr> <chr> <int> <chr>
#> 1 bto sample 7 bundled
#> 2 chebi sample 5 bundled
#> 3 cl sample 7 bundled
#> 4 doid sample 7 bundled
#> 5 efo sample 5 bundled
#> 6 go sample 6 bundled
#> 7 hgnc 2026-07-07 45019 bundled
#> 8 hgnc sample 7 bundled
#> 9 hp sample 7 bundled
#> 10 mondo sample 6 bundled
#> 11 mp sample 7 bundled
#> 12 pato sample 7 bundled
#> 13 so sample 7 bundled
#> 14 uberon sample 7 bundledRemote mode
remote mode checks existence live against a source API.
It needs a network, so it is not run in this vignette, but the call
looks like this:
# Live check against the Ensembl REST API.
check_id("ENSG00000139618", source_db = "ensembl", how = "remote")
# existence mode uses a snapshot when one is available and otherwise
# falls back to remote.
check_id("MONDO:0005148", source_db = "mondo", how = "existence")A network failure in remote mode raises an error. It
never returns a silent FALSE, so a failed lookup cannot be
mistaken for an absent identifier.
Species and version awareness
Some sources are species-aware. For Ensembl, the species is encoded
in the id, so pattern mode can reject a well-formed id that
belongs to the wrong species.
# ENSMUSG is a mouse gene id.
is_valid_id("ENSMUSG00000059552", source_db = "ensembl", species = "mus_musculus")
#> [1] TRUE
is_valid_id("ENSMUSG00000059552", source_db = "ensembl", species = "homo_sapiens")
#> [1] FALSEspecies accepts a name such as
"homo_sapiens" or an NCBI taxon id such as
9606. A source that is not species-aware ignores the
argument.
HGVS variant syntax
The hgvs source checks the syntax of an HGVS sequence
variant name. This is a grammar check in pattern mode. It
confirms the shape of the variant. It does not check coordinates or that
the variant exists.
is_valid_id(
c(
"NM_004006.2:c.4375C>T",
"NP_003997.1:p.(Gly56Ala)",
"NM_004006.2:c.76insG"
),
source_db = "hgvs"
)
#> [1] TRUE TRUE FALSEThe last one is invalid: an insertion must sit between two flanking
positions, so it needs a range such as c.76_77insG.
Clean a column
The most common job is not “is this one id valid” but “here is a
column, which values are wrong, and can you fix the ones you can”.
report_id() runs a check over a whole column and returns a
table that prints with a one-line summary of how many values are valid,
repairable, invalid, or missing.
genes <- c("TP53", "MLL", "notagene", NA)
report_id(genes, "hgnc", how = "cache")
#> # biobouncer report on hgnc (cache mode): 1 valid, 1 repairable, 1 invalid, 1 missing of 4
#> # A tibble: 4 × 9
#> input valid normalized suggestion source_db version species how error
#> <chr> <lgl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 TP53 TRUE TP53 NA hgnc 2026-07-07 NA cache NA
#> 2 MLL FALSE NA KMT2A hgnc 2026-07-07 NA cache NA
#> 3 notagene FALSE NA NA hgnc 2026-07-07 NA cache NA
#> 4 NA NA NA NA hgnc 2026-07-07 NA cache NAMLL is a withdrawn gene symbol, so it is invalid but
repairable: its successor is KMT2A.
repair_id() substitutes the fixable values and leaves
valid, unmappable, and missing values untouched, so it keeps the
column’s length and order and drops into
dplyr::mutate():
repair_id(genes, "hgnc", how = "cache")
#> [1] "TP53" "KMT2A" "notagene" NACalling summary() on a report returns the counts as a
one-row table.
Validation framework adapters
The adapters wrap the core classifier so it plugs into common validation frameworks. They never reimplement any checks. The checkmate-style adapters work out of the box:
# TRUE when all are valid, otherwise a message.
check_valid_id(c("MONDO:0005148", "mondo:5148"), "mondo")
#> [1] "Must be valid mondo identifiers (pattern mode), but 1 of 2 failed, for example 'mondo:5148'"
# A single logical.
test_valid_id("MONDO:0005148", "mondo")
#> [1] TRUEid_predicate() returns an elementwise predicate for
data-frame validation with assertr or validate:
is_mondo <- id_predicate("mondo")
ids <- c("MONDO:0005148", "mondo:5148", "MONDO:0018076")
ids[is_mondo(ids)]
#> [1] "MONDO:0005148" "MONDO:0018076"For Shiny apps, sv_biobouncer() returns a shinyvalidate
rule. The rule returns NULL for a valid input and a message
otherwise:
rule <- sv_biobouncer("mondo")
rule("MONDO:0005148")
#> NULL
rule("mondo:5148")
#> [1] "Not a valid mondo identifier"Generating test data
synthesize_ids() builds a labeled “messy column” for any
source, so you can exercise a validation pipeline without hand-writing
test ids. Each row carries the input, its category (valid, repairable,
invalid, or missing), and the verdict fields the checker returned for
it.
rows <- synthesize_ids("mondo")
rows[, c("input", "category", "suggestion")]
#> # A tibble: 5 × 3
#> input category suggestion
#> <chr> <chr> <chr>
#> 1 MONDO:0005148 valid NA
#> 2 mondo:0005148 repairable MONDO:0005148
#> 3 MONDO:0005148! invalid NA
#> 4 NA missing NA
#> 5 MONDO:0005149 valid NA
# Feed the column straight into a report.
report_id(rows$input, "mondo")
#> # biobouncer report on mondo (pattern mode): 2 valid, 1 repairable, 1 invalid, 1 missing of 5
#> # A tibble: 5 × 9
#> input valid normalized suggestion source_db version species how error
#> <chr> <lgl> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 MONDO:00051… TRUE MONDO:000… NA mondo NA NA patt… NA
#> 2 mondo:00051… FALSE NA MONDO:000… mondo NA NA patt… NA
#> 3 MONDO:00051… FALSE NA NA mondo NA NA patt… NA
#> 4 NA NA NA NA mondo NA NA patt… NA
#> 5 MONDO:00051… TRUE MONDO:000… NA mondo NA NA patt… NAThe column is deterministic and offline, and the Python
synthesize() produces the same one. ec,
hgvs, and hgnc have no repairable form, so
they omit that category. Pass how = "cache" for a
snapshot-mode column, where a repairable value can be a retired id that
maps to its successor.
Summary
-
patternmode checks shape,cacheandremotecheck existence, andexistenceuses a snapshot first and remote as a fallback. -
report_id()andrepair_id()validate and clean a whole column in one call. - Results are vectorized and preserve input order and length. Errors are explicit.
- The same inputs give the same verdicts in the Python package, which is enforced by a shared conformance corpus.