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:

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:

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:

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:

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.