--- title: "Getting started with biobouncer" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with biobouncer} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` 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. ```{r setup} library(biobouncer) ``` ## What can be checked `sources()` lists the source databases biobouncer knows about, and `source_info()` returns a table with a short description of each. ```{r sources} sources() ``` ## 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. ```{r pattern} check_id( c("MONDO:0005148", "mondo:5148", "GO:0006915"), source_db = "mondo" ) ``` The `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. ```{r is-valid} is_valid_id(c("P04637", "p04637"), source_db = "uniprot") ``` ## Cache 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. ```{r cache} check_id( c("MONDO:0005148", "MONDO:9999999"), source_db = "mondo", how = "cache", version = "sample" ) ``` `MONDO: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: ```{r snapshots} biobouncer_snapshots() ``` ## Remote 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: ```{r remote, eval = FALSE} # 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. ```{r species} # ENSMUSG is a mouse gene id. is_valid_id("ENSMUSG00000059552", source_db = "ensembl", species = "mus_musculus") is_valid_id("ENSMUSG00000059552", source_db = "ensembl", species = "homo_sapiens") ``` `species` 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. ```{r hgvs} is_valid_id( c( "NM_004006.2:c.4375C>T", "NP_003997.1:p.(Gly56Ala)", "NM_004006.2:c.76insG" ), source_db = "hgvs" ) ``` The 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. ```{r report} genes <- c("TP53", "MLL", "notagene", NA) report_id(genes, "hgnc", how = "cache") ``` `MLL` 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()`: ```{r repair} repair_id(genes, "hgnc", how = "cache") ``` Calling `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: ```{r checkmate} # TRUE when all are valid, otherwise a message. check_valid_id(c("MONDO:0005148", "mondo:5148"), "mondo") # A single logical. test_valid_id("MONDO:0005148", "mondo") ``` `id_predicate()` returns an elementwise predicate for data-frame validation with assertr or validate: ```{r predicate} is_mondo <- id_predicate("mondo") ids <- c("MONDO:0005148", "mondo:5148", "MONDO:0018076") ids[is_mondo(ids)] ``` For Shiny apps, `sv_biobouncer()` returns a shinyvalidate rule. The rule returns `NULL` for a valid input and a message otherwise: ```{r shiny} rule <- sv_biobouncer("mondo") rule("MONDO:0005148") rule("mondo:5148") ``` ## 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. ```{r synthesize} rows <- synthesize_ids("mondo") rows[, c("input", "category", "suggestion")] # Feed the column straight into a report. report_id(rows$input, "mondo") ``` The 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 - `pattern` mode checks shape, `cache` and `remote` check existence, and `existence` uses a snapshot first and remote as a fallback. - `report_id()` and `repair_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.