Skip to contents

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