Introduction to OLSr
Martin Morgan
Roswell Park Comprehensive Cancer Center, Buffalo, NY, USSource:
vignettes/a_introduction.Rmd
a_introduction.Rmd
The Ontology Lookup Service (‘OLS’) is a repository of biomedical ontologies provided by the European Bioinformatics Institute (EMBL-EBI). OLSr provides an interface to the OLS service, allowing discovery of ontologies, terms, and relations. Results are presented in a ‘tidy’ framework, so that manipulation tasks are easily accomplished with verbs such as ‘filter()’, ‘select()’, and ‘mutate()’. An effort is made to integrate with other packages useful for more comprehensive biological and ontological analysis.
Written: 20 November, 2023
Installation
Install OLSr from GitHub using the remotes package.
if (!"remotes" in rownames(installed.packages()))
install.packages("remotes", repos = "https://cran.r-project.org")
remotes::install_github("mtmorgan/OLSr")
Attach the library to the current session. Most functions in OLSr return results as a
tibble
, so the dplyr package is
also attached.
Ontologies
Start by discovering ontologies available in the OLS.
onto <- get_ontologies()
onto
#> # A tibble: 265 × 31
#> id title description version numberOfTerms numberOfProperties
#> <chr> <chr> <chr> <chr> <int> <int>
#> 1 ado Alzheimer's D… Alzheimer'… 2.0.1 1963 186
#> 2 afpo African Popul… AfPO is an… 2024-0… 473 62
#> 3 agro Agronomy Onto… AgrO is an… NA 4162 293
#> 4 aism Ontology for … The ontolo… 2024-0… 8540 567
#> 5 amphx Amphioxus Dev… An ontolog… NA 403 10
#> 6 apo Ascomycete Ph… A structur… 2024-1… 619 27
#> 7 apollo_sv Apollo Struct… An OWL2 on… 2024-1… 1715 396
#> 8 aro Antibiotic Re… Antibiotic… NA 8526 25
#> 9 bco Biological Co… An ontolog… 2021-1… 253 472
#> 10 bfo Basic Formal … The upper … NA 35 22
#> # ℹ 255 more rows
#> # ℹ 25 more variables: numberOfIndividuals <int>, languages <list>,
#> # loaded <chr>, updated <chr>, versionIri <chr>, namespace <chr>,
#> # preferredPrefix <chr>, homepage <chr>, mailingList <chr>, tracker <chr>,
#> # logo <lgl>, creators <lgl>, annotations <lgl>, fileLocation <chr>,
#> # oboSlims <lgl>, labelProperty <chr>, definitionProperties <list>,
#> # synonymProperties <list>, hierarchicalProperties <list>, baseUris <list>, …
This vignette uses for examples the ontology with title ‘Cell
Ontology’, used by the CELLxGENE project to
classify cell types. Find the record corresponding to this ontology,
using glimpse()
to provide a convenient view of the
ontology description.
onto |>
filter(title == "Cell Ontology" ) |>
glimpse()
#> Rows: 1
#> Columns: 31
#> $ id <chr> "cl"
#> $ title <chr> "Cell Ontology"
#> $ description <chr> "An ontology of cell types."
#> $ version <chr> "2025-01-07"
#> $ numberOfTerms <int> 16637
#> $ numberOfProperties <int> 394
#> $ numberOfIndividuals <int> 18
#> $ languages <list> ["en"]
#> $ loaded <chr> "2025-01-20T23:16:05.759253163"
#> $ updated <chr> "2025-01-20T23:16:05.759253163"
#> $ versionIri <chr> "http://purl.obolibrary.org/obo/cl/releases/202…
#> $ namespace <chr> "cl"
#> $ preferredPrefix <chr> "CL"
#> $ homepage <chr> "https://obophenotype.github.io/cell-ontology/"
#> $ mailingList <chr> "https://groups.google.com/g/cl_edit"
#> $ tracker <chr> "https://github.com/obophenotype/cell-ontology/…
#> $ logo <lgl> NA
#> $ creators <lgl> NA
#> $ annotations <lgl> NA
#> $ fileLocation <chr> "http://purl.obolibrary.org/obo/cl.owl"
#> $ oboSlims <lgl> FALSE
#> $ labelProperty <chr> "http://www.w3.org/2000/01/rdf-schema#label"
#> $ definitionProperties <list> <NULL>
#> $ synonymProperties <list> <NULL>
#> $ hierarchicalProperties <list> <NULL>
#> $ baseUris <list> <NULL>
#> $ hiddenProperties <list> <NULL>
#> $ preferredRootTerms <lgl> NA
#> $ isSkos <lgl> FALSE
#> $ allowDownload <lgl> FALSE
#> $ `_links` <list> [["https://www.ebi.ac.uk/ols4/api/ontologies/cl…
Important information includes the ‘id’ of the ontology (used in many subsequent steps), the ontology home page, and the number of terms in the ontology.
onto |>
filter(title == "Cell Ontology") |>
select(id, homepage, numberOfTerms)
#> # A tibble: 1 × 3
#> id homepage numberOfTerms
#> <chr> <chr> <int>
#> 1 cl https://obophenotype.github.io/cell-ontology/ 16637
Information on individual ontologies is also available with
get_ontology()
; this record includes a link to the ontology
page in the OLS. The home page is a good starting place for
understanding the ontology in a more visual and interactive
environment.
cl <- get_ontology("cl")
glimpse(cl)
#> Rows: 1
#> Columns: 16
#> $ languages <list> "en"
#> $ lang <chr> "en"
#> $ ontologyId <chr> "cl"
#> $ loaded <chr> "2025-01-20T23:16:05.759253163"
#> $ updated <chr> "2025-01-20T23:16:05.759253163"
#> $ status <chr> "LOADED"
#> $ message <chr> ""
#> $ version <chr> "2025-01-07"
#> $ fileHash <lgl> NA
#> $ loadAttempts <int> 0
#> $ numberOfTerms <int> 16637
#> $ numberOfProperties <int> 394
#> $ numberOfIndividuals <int> 18
#> $ config <list> "cl"
#> $ baseUris <list> <NULL>
#> $ `_links` <list> ["https://www.ebi.ac.uk/ols4/api/ontologies/cl?la…
cl |> pull("_links")
#> [[1]]
#> [[1]]$href
#> [1] "https://www.ebi.ac.uk/ols4/api/ontologies/cl?lang=en"
Terms
Terms are central to using an ontology. Retrieve all terms from the
‘cl’ ontology. By default, only terms defined in the ontology per
se are used; add the argument all_ontologies = TRUE
to
this and subsequent functions to also include terms referenced in other
ontologies. This query can take several minutes to complete when first
executed, but the result is cached locally (as discussed in greater
detail below) so subsequent use is quick.
terms <- get_terms("cl")
terms
#> # A tibble: 2,861 × 13
#> label obo_id description iri synonyms annotation has_children short_form
#> <chr> <chr> <chr> <chr> <list> <list> <lgl> <chr>
#> 1 mesen… CL:40… Any mesenc… http… <list> <named list> FALSE CL_4040004
#> 2 mesen… CL:40… A mesenchy… http… <list> <named list> FALSE CL_4040005
#> 3 derma… CL:40… A chromato… http… <NULL> <named list> FALSE CL_4040006
#> 4 TAC3-… CL:40… A GABAergi… http… <list> <named list> FALSE CL_4042001
#> 5 TAC3-… CL:40… A nucleus … http… <list> <named list> FALSE CL_4042002
#> 6 borde… CL:40… A central … http… <list> <named list> TRUE CL_4042003
#> 7 choro… CL:40… A choroid … http… <list> <named list> FALSE CL_4042004
#> 8 strom… CL:40… A choroid … http… <list> <named list> FALSE CL_4042005
#> 9 dural… CL:40… A border a… http… <list> <named list> FALSE CL_4042006
#> 10 proto… CL:40… An astrocy… http… <NULL> <named list> FALSE CL_4042007
#> # ℹ 2,851 more rows
#> # ℹ 5 more variables: in_subset <list>, obo_definition_citation <list>,
#> # obo_xref <list>, obo_synonym <list>, `_links` <list>
Glimpse the second term to get a sense of available information.
terms |>
slice(1) |>
glimpse()
#> Rows: 1
#> Columns: 13
#> $ label <chr> "mesenchymal stem cell of orbital adipose tiss…
#> $ obo_id <chr> "CL:4040004"
#> $ description <chr> "Any mesenchymal stem cell of adipose tissue t…
#> $ iri <chr> "http://purl.obolibrary.org/obo/CL_4040004"
#> $ synonyms <list> ["OAMSC", "Orbital ASCs", "orbital adipose-der…
#> $ annotation <list> [["https://orcid.org/0000-0002-0819-0473"], ["…
#> $ has_children <lgl> FALSE
#> $ short_form <chr> "CL_4040004"
#> $ in_subset <list> ["human_subset", "mouse_subset"]
#> $ obo_definition_citation <list> [["Any mesenchymal stem cell of adipose tissu…
#> $ obo_xref <list> <NULL>
#> $ obo_synonym <list> [["OAMSC", "hasExactSynonym", <NULL>, [["PMID…
#> $ `_links` <list> [["https://www.ebi.ac.uk/ols4/api/ontologies/c…
Hierarchical data
Some columns in the terms
table represent hieararchical
(relational, 1:many mapping) data, e.g., the in_subset
column
terms |>
select(label, obo_id, in_subset)
#> # A tibble: 2,861 × 3
#> label obo_id in_subset
#> <chr> <chr> <list>
#> 1 mesenchymal stem cell of orbital adipose tissue CL:4040004 <list [2]>
#> 2 mesenchymal stem cell of apical papilla CL:4040005 <list [2]>
#> 3 dermal chromatophore CL:4040006 <list [2]>
#> 4 TAC3-positive striatal interneuron CL:4042001 <list [2]>
#> 5 TAC3-positive medium spiny neuron CL:4042002 <list [2]>
#> 6 border associated macrophage CL:4042003 <list [2]>
#> 7 choroid epiplexus macrophage CL:4042004 <list [2]>
#> 8 stromal choroid plexus macrophage CL:4042005 <list [2]>
#> 9 dural macrophage CL:4042006 <list [2]>
#> 10 protoplasmic astrocyte CL:4042007 <list [2]>
#> # ℹ 2,851 more rows
Individual terms apparently belong to 0 or more subsets.
terms |>
count(lengths(in_subset))
#> # A tibble: 7 × 2
#> `lengths(in_subset)` n
#> <int> <int>
#> 1 0 141
#> 2 1 38
#> 3 2 1604
#> 4 3 735
#> 5 4 282
#> 6 5 58
#> 7 6 3
The R for Data Science chapter on hierarchical data suggests
using tidyr
functions unnest_wider()
and unnest_longer()
to expand such data.
terms |>
select(in_subset, obo_id, label) |>
tidyr::unnest_longer(in_subset)
#> # A tibble: 6,887 × 3
#> in_subset obo_id label
#> <chr> <chr> <chr>
#> 1 human_subset CL:4040004 mesenchymal stem cell of orbital adipose tissue
#> 2 mouse_subset CL:4040004 mesenchymal stem cell of orbital adipose tissue
#> 3 human_subset CL:4040005 mesenchymal stem cell of apical papilla
#> 4 mouse_subset CL:4040005 mesenchymal stem cell of apical papilla
#> 5 human_subset CL:4040006 dermal chromatophore
#> 6 mouse_subset CL:4040006 dermal chromatophore
#> 7 human_subset CL:4042001 TAC3-positive striatal interneuron
#> 8 mouse_subset CL:4042001 TAC3-positive striatal interneuron
#> 9 human_subset CL:4042002 TAC3-positive medium spiny neuron
#> 10 mouse_subset CL:4042002 TAC3-positive medium spiny neuron
#> # ℹ 6,877 more rows
Defined subsets and the number of terms in each can be computed as
terms |>
tidyr::unnest_longer(in_subset) |>
count(in_subset, sort = TRUE)
#> # A tibble: 12 × 2
#> in_subset n
#> <chr> <int>
#> 1 mouse_subset 2689
#> 2 human_subset 2674
#> 3 cellxgene_subset 801
#> 4 human_reference_atlas 480
#> 5 location_grouping 65
#> 6 BDS_subset 64
#> 7 blood_and_immune_upper_slim 36
#> 8 eye_upper_slim 24
#> 9 general_cell_types_upper_slim 24
#> 10 kidney_upper_slim 19
#> 11 added_for_HCA 8
#> 12 _upper_level 3
Use filter()
with a helper function that summarizes
subsets of individual terms to identify the
human_reference_atlas
subset. Using vapply()
(rather than, e.g., sapply()
) can be considered a best
practice, so that the filter function is guaranteed to return a scalar
logical value regardless of input length.
is_human_reference_atlas <- function(x)
vapply(x, \(x) any(x == "human_reference_atlas"), logical(1))
human_reference_atlas <-
terms |>
filter(is_human_reference_atlas(in_subset))
human_reference_atlas
#> # A tibble: 480 × 13
#> label obo_id description iri synonyms annotation has_children short_form
#> <chr> <chr> <chr> <chr> <list> <list> <lgl> <chr>
#> 1 brain… CL:40… "A cell th… http… <NULL> <named list> FALSE CL_4023072
#> 2 mammi… CL:40… "A neuron … http… <NULL> <named list> FALSE CL_4023074
#> 3 midbr… CL:40… "A GABAerg… http… <NULL> <named list> FALSE CL_4023079
#> 4 midge… CL:40… "A retinal… http… <NULL> <named list> TRUE CL_4023188
#> 5 paras… CL:40… "A retinal… http… <list> <named list> TRUE CL_4023189
#> 6 alveo… CL:40… "An alveol… http… <list> <named list> FALSE CL_4028002
#> 7 alveo… CL:40… "An alveol… http… <list> <named list> FALSE CL_4028003
#> 8 alveo… CL:40… "A pulmona… http… <list> <named list> FALSE CL_4028004
#> 9 alveo… CL:40… "A pulmona… http… <list> <named list> FALSE CL_4028006
#> 10 fallo… CL:40… "An epithe… http… <list> <named list> FALSE CL_4030006
#> # ℹ 470 more rows
#> # ℹ 5 more variables: in_subset <list>, obo_definition_citation <list>,
#> # obo_xref <list>, obo_synonym <list>, `_links` <list>
There are 480 terms in the human_reference_atlas
subset.
Individual terms
Suppose we are interested in B cells. Discover term labels starting
with "B cell"
, selecting useful fields for display. For
subsequent work, create a variable capturing the obo_id
of
the "B cell"
term.
terms |>
filter(startsWith(label, "B cell")) |>
select(label, obo_id, description)
#> # A tibble: 6 × 3
#> label obo_id description
#> <chr> <chr> <chr>
#> 1 B cell CL:0000236 A lymphocyte of B lineage …
#> 2 B cell, CD19-positive CL:0001201 A B cell that is CD19-posi…
#> 3 B cell of appendix CL:0009032 A B cell that is located i…
#> 4 B cell of medullary sinus of lymph node CL:0009045 A B cell found in the lymp…
#> 5 B cell of anorectum CL:0009050 A B cell that is located i…
#> 6 B cell zone reticular cell CL:0009104 A fibroblastic reticular c…
b_cell <- # "CL:0000236"
terms |>
filter(label == "B cell") |>
pull(obo_id)
Slightly richer information on individual terms is available with
get_term()
, using the ontology id
and term
obo_id
as keys.
get_term("cl", b_cell) |>
glimpse()
#> Rows: 1
#> Columns: 22
#> $ obo_id <chr> "CL:0000236"
#> $ label <chr> "B cell"
#> $ description <chr> "A lymphocyte of B lineage that is capable of …
#> $ iri <chr> "http://purl.obolibrary.org/obo/CL_0000236"
#> $ lang <chr> "en"
#> $ synonyms <list> ["B lymphocyte", "B-cell", "B-lymphocyte"]
#> $ annotation <list> [["BTO:0000776", "CALOHA:TS-0068", "FMA:62869"…
#> $ ontology_name <chr> "cl"
#> $ ontology_prefix <chr> "CL"
#> $ ontology_iri <chr> "http://purl.obolibrary.org/obo/cl.owl"
#> $ is_obsolete <lgl> FALSE
#> $ term_replaced_by <lgl> NA
#> $ is_defining_ontology <lgl> TRUE
#> $ has_children <lgl> TRUE
#> $ is_root <lgl> FALSE
#> $ short_form <chr> "CL_0000236"
#> $ in_subset <list> ["blood_and_immune_upper_slim", "cellxgene_sub…
#> $ obo_definition_citation <list> [["A lymphocyte of B lineage that is capable o…
#> $ obo_xref <list> [["BTO", "0000776", <NULL>, "http://purl.oboli…
#> $ obo_synonym <lgl> NA
#> $ is_preferred_root <lgl> FALSE
#> $ `_links` <list> [["https://www.ebi.ac.uk/ols4/api/ontologies/…
Relatives
The functions get_parents()
and
get_children()
retrieve parent and child terms of
individual terms; get_ancestors()
and
get_descendants()
return all ancestors (to the root of the
ontology) or descendants (where the final term repreents a leaf). For B
cells the ancestors are
b_cell_ancestors <-
get_ancestors("cl", b_cell)
Ancestors and descendants also contain the in_subset
column, so these groups are easily filtered to a particlur subset,
e.g.,
b_cell_ancestors |>
filter(is_human_reference_atlas(in_subset))
#> # A tibble: 3 × 14
#> obo_id label description iri synonyms annotation has_children is_root
#> <chr> <chr> <chr> <chr> <list> <list> <lgl> <lgl>
#> 1 CL:0000945 lymph… A lymphocy… http… <NULL> <named list> TRUE FALSE
#> 2 CL:0000542 lymph… A lymphocy… http… <NULL> <named list> TRUE FALSE
#> 3 CL:0000738 leuko… An achroma… http… <list> <named list> TRUE FALSE
#> # ℹ 6 more variables: short_form <chr>, in_subset <list>,
#> # obo_definition_citation <list>, obo_xref <list>, obo_synonym <list>,
#> # `_links` <list>
Terms without children or descendants return a tibble without any rows.
get_children("cl", "CL:0002350")
#> # A tibble: 0 × 0
get_descendants("cl", "CL:0002350")
#> # A tibble: 0 × 0
Graphs
See the Case Study: CELLxGENE Ontologies vignette for illustration of ancestors and descendants graphs.
Cache management
OLSr uses an on-disk cache to improve performance and reliability. Each query to the OLS server is cached on first use, so that subsequent responses can be read from the local disk. This greatly increases performance, and allows use of previously retrieved resources when off-line.
OLSr uses the memoise and cachem packages to
implement the cache. The disk cache is implemented using cachem’s
cache_disk()
function using the default cache size (1 Gb)
and "lsu"
(‘least recently used’) eviction policy. Objects
remain in the cache for 30 days.
A risk of using a cache is that the cache contains an ‘old’ version of the response. Circumvent this risk by manually manipulating the cache, e.g., removing objects that a known to be too old.
Use cache_directory()
to discover the location of the
cache.
cache_directory()
#> [1] "/home/runner/.cache/R/OLSr"
This location follows conventions of
tools::R_user_dir()
. All files in this directory have been
created by OLSr.
cache_info()
is a convenience function to summarize
files in the cache.
cache_info()
#> # A tibble: 96 × 5
#> file size mtime ctime atime
#> <chr> <dbl> <dttm> <dttm> <dttm>
#> 1 03d5b9987d… 808 2025-01-23 18:44:27 2025-01-23 18:44:27 2025-01-23 18:44:27
#> 2 06f6277f34… 154 2025-01-23 18:44:17 2025-01-23 18:44:17 2025-01-23 18:44:17
#> 3 09405ddbab… 768 2025-01-23 18:44:27 2025-01-23 18:44:27 2025-01-23 18:44:27
#> 4 09f0d0cb75… 720 2025-01-23 18:44:04 2025-01-23 18:44:04 2025-01-23 18:44:04
#> 5 1019c9f790… 154 2025-01-23 18:44:20 2025-01-23 18:44:20 2025-01-23 18:44:20
#> 6 1152719177… 21100 2025-01-23 18:44:01 2025-01-23 18:44:01 2025-01-23 18:44:01
#> 7 13938f8890… 5996 2025-01-23 18:44:38 2025-01-23 18:44:38 2025-01-23 18:44:38
#> 8 1d41ffdf45… 154 2025-01-23 18:44:24 2025-01-23 18:44:24 2025-01-23 18:44:24
#> 9 1d4576da3a… 1407 2025-01-23 18:44:12 2025-01-23 18:44:12 2025-01-23 18:44:12
#> 10 236f616b89… 154 2025-01-23 18:44:14 2025-01-23 18:44:14 2025-01-23 18:44:14
#> # ℹ 86 more rows
Each file represents the response to a single memoised function call. The file name is a hash of the function definition and arguments; it is not possible to associate individual files with specific function calls. Nonetheless, it can be informative to identify files modified more than, say 1 day ago, or the most recently modified file, or perhaps the total size of files in the cache.
## cache objects modified in the last 24 hours
cache_info() |>
filter(mtime > Sys.time() - 24 * 60 * 60)
#> # A tibble: 96 × 5
#> file size mtime ctime atime
#> <chr> <dbl> <dttm> <dttm> <dttm>
#> 1 03d5b9987d… 808 2025-01-23 18:44:27 2025-01-23 18:44:27 2025-01-23 18:44:27
#> 2 06f6277f34… 154 2025-01-23 18:44:17 2025-01-23 18:44:17 2025-01-23 18:44:17
#> 3 09405ddbab… 768 2025-01-23 18:44:27 2025-01-23 18:44:27 2025-01-23 18:44:27
#> 4 09f0d0cb75… 720 2025-01-23 18:44:04 2025-01-23 18:44:04 2025-01-23 18:44:04
#> 5 1019c9f790… 154 2025-01-23 18:44:20 2025-01-23 18:44:20 2025-01-23 18:44:20
#> 6 1152719177… 21100 2025-01-23 18:44:01 2025-01-23 18:44:01 2025-01-23 18:44:01
#> 7 13938f8890… 5996 2025-01-23 18:44:38 2025-01-23 18:44:38 2025-01-23 18:44:38
#> 8 1d41ffdf45… 154 2025-01-23 18:44:24 2025-01-23 18:44:24 2025-01-23 18:44:24
#> 9 1d4576da3a… 1407 2025-01-23 18:44:12 2025-01-23 18:44:12 2025-01-23 18:44:12
#> 10 236f616b89… 154 2025-01-23 18:44:14 2025-01-23 18:44:14 2025-01-23 18:44:14
#> # ℹ 86 more rows
## most recently modified file
cache_info() |>
arrange(desc(mtime)) |>
slice(1)
#> # A tibble: 1 × 5
#> file size mtime ctime atime
#> <chr> <dbl> <dttm> <dttm> <dttm>
#> 1 4e9479c7fcf… 154 2025-01-23 18:44:38 2025-01-23 18:44:38 2025-01-23 18:44:38
## total cache size
cache_info() |>
summarize(size = sum(size)) |>
pull(size) |>
structure(class = "object_size") |>
format(units = "auto")
#> [1] "4.2 Mb"
In special cases it may be useful to invalidate the cache using
unlink()
on the cache_directory()
, or on
invalidate particular records using unlink()
on file paths
created using file.path()
, cache_directory()
and individual file
names from
cache_info()
.
Session information
sessionInfo()
#> R version 4.4.2 (2024-10-31)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
#> [4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
#> [7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
#> [10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] OLSr_0.0.4 dplyr_1.1.4 BiocStyle_2.34.0
#>
#> loaded via a namespace (and not attached):
#> [1] jsonlite_1.8.9 compiler_4.4.2 BiocManager_1.30.25
#> [4] BiocBaseUtils_1.8.0 tidyselect_1.2.1 tidyr_1.3.1
#> [7] jquerylib_0.1.4 systemfonts_1.2.1 textshaping_1.0.0
#> [10] yaml_2.3.10 fastmap_1.2.0 R6_2.5.1
#> [13] rjsoncons_1.3.1.9100 generics_0.1.3 curl_6.1.0
#> [16] httr2_1.1.0 knitr_1.49 htmlwidgets_1.6.4
#> [19] tibble_3.2.1 bookdown_0.42 desc_1.4.3
#> [22] bslib_0.8.0 pillar_1.10.1 rlang_1.1.5
#> [25] utf8_1.2.4 cachem_1.1.0 xfun_0.50
#> [28] fs_1.6.5 sass_0.4.9 memoise_2.0.1
#> [31] cli_3.6.3 withr_3.0.2 pkgdown_2.1.1
#> [34] magrittr_2.0.3 digest_0.6.37 rappdirs_0.3.3
#> [37] lifecycle_1.0.4 vctrs_0.6.5 evaluate_1.0.3
#> [40] glue_1.8.0 ragg_1.3.3 purrr_1.0.2
#> [43] rmarkdown_2.29 tools_4.4.2 pkgconfig_2.0.3
#> [46] htmltools_0.5.8.1