# Introduction to taxodist

## Overview

`taxodist` retrieves ordered taxonomic lineages from The Taxonomicon and uses them to compare named nodes in a classification hierarchy.

The package provides functions for:

- lineage retrieval;
- most-recent-common-ancestor queries;
- taxonomic hierarchy distances;
- clade membership and filtering;
- paths between hierarchy nodes;
- pairwise distance matrices;
- clustering and principal coordinates analysis;
- session and persistent cache management.

Inputs may represent species, genera, families, orders, named clades, or other nodes available in the source classification.

## What the distance represents

Let \(L_A\) and \(L_B\) be the ordered lineages of two hierarchy nodes. Let \(h(A,B)\) be the depth of their most recent common ancestor, defined from their continuous common prefix.

The distance is

\[ d(A,B) = \begin{cases} 0, & L_A = L_B, \\ 1/h(A,B), & L_A \ne L_B. \end{cases} \]

A deeper shared ancestor produces a smaller distance. Within a connected hierarchy, finite distances lie between zero and one.

If two lineages have no shared root, the package returns an infinite distance. Missing lineages instead produce missing pairwise values in a distance matrix.

The measure is an ultrametric within each connected hierarchy. Its mathematical properties and assumptions are discussed in:

    vignette("methodological-notes", package = "taxodist")

## Distance and membership are different

A descendant belongs to each clade represented among its ancestors. However, the descendant and ancestor remain distinct hierarchy nodes.

For example, Tyrannosaurus is a member of Dinosauria, but the distance between the two nodes is positive:

\[ d(\textit{Tyrannosaurus},\textit{Dinosauria}) = \frac{1}{\operatorname{depth}(\textit{Dinosauria})}. \]

Use:

- `taxo_distance()` to compare hierarchy nodes;
- `is_member()` to test containment within a clade;
- `taxo_path()` to inspect the complete ancestry path between nodes.

The distance represents classification depth. It is not evolutionary time, genetic distance, morphological divergence, or phylogenetic branch length.

## Reproducible packaged examples

The package includes `taxobase`, a reference object generated from The Taxonomicon. It allows documentation and examples to run without contacting an external service.

    names(taxobase)
    #>  [1] "taxa"                  "found_taxa"            "coverage"
    #>  [4] "matrix"                "pairwise"              "lineage_homo"
    #>  [7] "lineage_tyrannosaurus" "closest"               "filter"
    #> [10] "search"                "statistical_taxa"      "statistical_matrix"
    #> [13] "metadata"
    taxobase$metadata
    #> $source
    #> [1] "The Taxonomicon"
    #>
    #> $source_url
    #> [1] "http://taxonomicon.taxonomy.nl"
    #>
    #> $generated_on
    #> [1] "2026-08-06"
    #>
    #> $package_version
    #> [1] "0.6.0"
    #>
    #> $distance_definition
    #> [1] "0 for identical nodes; otherwise 1 / depth(MRCA)"

The metadata records the source, generation date, package version, and distance definition used to construct the object.

### Stored lineages

    tail(taxobase$lineage_tyrannosaurus)
    #> [1] "Tyrannoraptora"   "Tyrannosauroidea" "Tyrannosauridae"  "Tyrannosaurinae"
    #> [5] "Tyrannosaurini"   "Tyrannosaurus"
    tail(taxobase$lineage_homo)
    #> [1] "Hominoidea" "Hominidae"  "Homininae"  "Hominini"   "Hominina"
    #> [6] "Homo"

Lineages are ordered from the root of the source classification to the queried node.

### Stored pairwise comparison

    taxobase$pairwise
    #>
    #> ── Taxonomic Distance ──
    #>
    #> • Tyrannosaurus vs Homo
    #>   Distance : 0.0263157894736842
    #>   MRCA : Amniota (depth 38)
    #>   Depth A : 72
    #>   Depth B : 75

A `taxodist_result` records:

- the distance;
- the MRCA and its depth;
- the depth of each complete lineage;
- the two queried names.

    class(taxobase$pairwise)
    #> [1] "taxodist_result"
    names(taxobase$pairwise)
    #> [1] "distance"   "mrca"       "mrca_depth" "depth_a"    "depth_b"
    #> [6] "taxon_a"    "taxon_b"

### Reference distance matrix

    reference_matrix <- taxobase$matrix

    inherits(reference_matrix, "dist")
    #> [1] TRUE
    attr(reference_matrix, "Size")
    #> [1] 52
    head(attr(reference_matrix, "Labels"))
    #> [1] "Tyrannosaurus" "Velociraptor"  "Triceratops"   "Brachiosaurus"
    #> [5] "Stegosaurus"   "Ankylosaurus"

    round(
      as.matrix(reference_matrix)[1:6, 1:6],
      digits = 4
    )
    #>               Tyrannosaurus Velociraptor Triceratops Brachiosaurus Stegosaurus
    #> Tyrannosaurus        0.0000       0.0149      0.0175        0.0169      0.0175
    #> Velociraptor         0.0149       0.0000      0.0175        0.0169      0.0175
    #> Triceratops          0.0175       0.0175      0.0000        0.0175      0.0169
    #> Brachiosaurus        0.0169       0.0169      0.0175        0.0000      0.0175
    #> Stegosaurus          0.0175       0.0175      0.0169        0.0175      0.0000
    #> Ankylosaurus         0.0175       0.0175      0.0169        0.0175      0.0161
    #>               Ankylosaurus
    #> Tyrannosaurus       0.0175
    #> Velociraptor        0.0175
    #> Triceratops         0.0169
    #> Brachiosaurus       0.0175
    #> Stegosaurus         0.0161
    #> Ankylosaurus        0.0000

A matrix returned by `distance_matrix()` is a standard R `dist` object. It is symmetric, has a zero diagonal, and can be supplied to functions accepting pairwise dissimilarities.

## Live lineage queries

The following examples contact The Taxonomicon and are not evaluated while the vignette is built.

### Retrieving a lineage

    get_lineage("Tyrannosaurus")
    get_lineage("Drosophila melanogaster")

Numeric Taxonomicon identifiers can also be supplied directly:

    get_lineage("67263")

Using an identifier is useful when a name corresponds to more than one valid taxonomic entry.

### Computing a pairwise distance

    result <- taxo_distance(
      "Tyrannosaurus",
      "Velociraptor"
    )

    result
    result$distance
    result$mrca

### Finding the MRCA

    mrca("Tyrannosaurus", "Velociraptor")
    mrca("Tyrannosaurus", "Triceratops")
    mrca("Tyrannosaurus", "Homo")

The returned MRCA depends on the classification currently provided by The Taxonomicon and may change following taxonomic revisions.

### Inspecting the path between nodes

    taxo_path(
      "Tyrannosaurus",
      "Triceratops"
    )

    taxo_path(
      "Dinosauria",
      "Tyrannosaurus"
    )

The path ascends from the first node to the MRCA and descends toward the second node. For an ancestor-descendant pair, the ancestor itself is the MRCA.

## Working with multiple taxa

### Constructing a distance matrix

    taxa <- c(
      "Tyrannosaurus",
      "Velociraptor",
      "Spinosaurus",
      "Allosaurus"
    )

    mat <- distance_matrix(
      taxa,
      progress = TRUE
    )

    mat

Lineages are retrieved once and cached for subsequent comparisons.

### Finding the closest candidates

A stored example is available in `taxobase`:

    taxobase$closest
    #>           taxon   distance
    #> 1  Velociraptor 0.01492537
    #> 3 Brachiosaurus 0.01694915
    #> 2   Triceratops 0.01754386
    #> 4      Panthera 0.02631579
    #> 6    Drosophila 0.06666667
    #> 5       Quercus 0.25000000

For a live query:

    closest_relative(
      "Carnotaurus",
      c(
        "Aucasaurus",
        "Velociraptor",
        "Triceratops",
        "Brachiosaurus"
      )
    )

Candidates are sorted from smallest to largest distance. A missing candidate is retained with a missing distance.

### Focal distances

    focal_distances(
      focal = "Tyrannosaurus",
      community = c(
        "Velociraptor",
        "Triceratops",
        "Spinosaurus"
      )
    )

This produces a table containing the candidate name, distance, MRCA, and MRCA depth.

## Clade operations

### Testing membership

    is_member("Tyrannosaurus", "Dinosauria")
    is_member("Tyrannosaurus", "Theropoda")
    is_member("Tyrannosaurus", "Ornithischia")

Matching is case-insensitive, ignores surrounding whitespace, and requires the complete clade name rather than a partial substring.

### Filtering taxa

The package data include a stored filtering example:

    taxobase$filter
    #>  [1] "Tyrannosaurus" "Velociraptor"  "Spinosaurus"   "Gallus"
    #>  [5] "Struthio"      "Falco"         "Corvus"        "Spheniscus"
    #>  [9] "Aquila"        "Ara"           "Passer"

A live operation can be performed with:

    taxa <- c(
      "Tyrannosaurus",
      "Carnotaurus",
      "Triceratops",
      "Velociraptor",
      "Homo",
      "Drosophila"
    )

    filter_clade(taxa, "Dinosauria")
    filter_clade(taxa, "Theropoda")

### Shared clades and lineage comparison

    shared_clades(
      "Tyrannosaurus",
      "Triceratops"
    )

    compare_lineages(
      "Carnotaurus",
      "Tyrannosaurus"
    )

Shared ancestry is determined from the continuous common lineage prefix. A name repeated after the first divergence is not treated as a shared ancestor.

## Clustering, ordination, and visualization

The following examples use a subset of the packaged matrix and therefore require no network access.

    labels <- attr(reference_matrix, "Labels")[1:8]

    example_matrix <- stats::as.dist(
      as.matrix(reference_matrix)[labels, labels]
    )

    example_matrix
    #>               Tyrannosaurus Velociraptor Triceratops Brachiosaurus Stegosaurus
    #> Velociraptor     0.01492537
    #> Triceratops      0.01754386   0.01754386
    #> Brachiosaurus    0.01694915   0.01694915  0.01754386
    #> Stegosaurus      0.01754386   0.01754386  0.01694915    0.01754386
    #> Ankylosaurus     0.01754386   0.01754386  0.01694915    0.01754386  0.01612903
    #> Spinosaurus      0.01562500   0.01562500  0.01754386    0.01694915  0.01754386
    #> Diplodocus       0.01694915   0.01694915  0.01754386    0.01470588  0.01754386
    #>               Ankylosaurus Spinosaurus
    #> Velociraptor
    #> Triceratops
    #> Brachiosaurus
    #> Stegosaurus
    #> Ankylosaurus
    #> Spinosaurus     0.01754386
    #> Diplodocus      0.01754386  0.01694915

### Hierarchical clustering

    clustering <- taxo_cluster(
      example_matrix,
      method = "average"
    )

    plot(
      clustering,
      main = "Taxonomic hierarchy distance clustering",
      xlab = "",
      sub = ""
    )

The result is a clustering dendrogram, not an independently inferred phylogenetic tree.

### Principal coordinates analysis

    ordination <- taxo_ordinate(
      example_matrix,
      k = 2
    )

    summary(ordination)
    #>
    #> ── Taxonomic Ordination Summary (PCoA) ──
    #>
    #> Goodness-of-Fit (GOF): 38.52%
    #>
    #>  Axis Eigenvalue Variance_Pct Cumulative_Pct
    #>   PC1  0.0002059        20.33          20.33
    #>   PC2  0.0001842        18.19          38.52
    plot(
      ordination,
      main = "Taxonomic hierarchy distance space"
    )

The goodness-of-fit and eigenvalues should be examined before interpreting a low-dimensional representation.

### Heatmap

    taxo_heatmap(
      example_matrix,
      main = "Taxonomic hierarchy distance matrix"
    )

Additional statistical examples and interpretation are provided in:

    vignette("statistical-applications", package = "taxodist")

## Name resolution and coverage

Taxonomic names may be missing, ambiguous, redirected, or associated with non-biological homonyms.

`taxo_search()` returns candidate Taxonomicon entries:

    taxo_search("Panthera")
    taxo_search("Bacteria")

When several valid biological entries exist, the package reports the available identifiers. A numeric identifier should be supplied when the intended taxonomic concept is known.

Before calculating a large matrix, coverage can be checked explicitly:

    taxa <- c(
      "Tyrannosaurus",
      "Velociraptor",
      "Quercus",
      "Not_a_real_taxon"
    )

    check_coverage(taxa)

Unresolved taxa generate missing pairwise distances. These values should be examined before clustering, ordination, or other methods requiring complete finite matrices.

## Cache management

Taxonomicon identifiers and lineages are cached in memory during an R session. This avoids repeated requests for the same taxa.

    cache_info()
    clear_cache()

The cache can also be saved and restored:

    save_cache("taxodist-cache.rds")
    clear_cache()
    load_cache("taxodist-cache.rds")

Cache files preserve retrieved lineages across sessions. They should be regenerated when an analysis is intended to incorporate subsequent revisions of the source classification.

## Interpretation and reproducibility

The numerical distance depends on the classification supplied by The Taxonomicon. Changes in lineage resolution or taxonomic concepts can alter MRCA depths and therefore alter distances.

A reproducible analysis should record:

- the `taxodist` version;
- the retrieval or dataset-generation date;
- the source classification;
- numeric identifiers used to resolve ambiguous names;
- missing or excluded taxa;
- the distance definition;
- downstream clustering or ordination settings.

Saved caches and distance matrices can preserve the exact input used in an analysis.

## Data source and citation

All retrieved lineage data originate from The Taxonomicon, based on Systema Naturae 2000.

Formatted references for both the package and the data source are available with:

    citation("taxodist")

Both the software and the underlying taxonomic source should be cited in published analyses.