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.
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:
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.
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.7.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.
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.
taxobase$pairwise
#>
#> ── Taxonomic Distance ──
#>
#> • Tyrannosaurus vs Homo
#> Distance : 0.0263157894736842
#> MRCA : Amniota (depth 38)
#> Depth A : 72
#> Depth B : 75A taxodist_result records:
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.0000A 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.
The following examples contact The Taxonomicon and are not evaluated while the vignette is built.
Numeric Taxonomicon identifiers can also be supplied directly:
Using an identifier is useful when a name corresponds to more than one valid taxonomic entry.
taxa <- c(
"Tyrannosaurus",
"Velociraptor",
"Spinosaurus",
"Allosaurus"
)
mat <- distance_matrix(
taxa,
progress = TRUE
)
matLineages are retrieved once and cached for subsequent comparisons. The pairwise computation itself is vectorized, so matrices of several thousand taxa are computed in seconds once their lineages are available.
When only the distances from one set of taxa to another are needed,
such as from the members of a community to a set of reference taxa,
cross_distances() returns the rectangular matrix directly.
The example below uses user-supplied lineages and therefore runs
offline:
community <- taxo_from_lineages(list(
Tyrannosaurus = c("Biota", "Dinosauria", "Theropoda", "Tyrannosaurus"),
Triceratops = c("Biota", "Dinosauria", "Ornithischia", "Triceratops")
))
reference <- taxo_from_lineages(list(
Velociraptor = c("Biota", "Dinosauria", "Theropoda", "Velociraptor"),
Homo = c("Biota", "Mammalia", "Homo")
))
cross_distances(community, reference)
#> Velociraptor Homo
#> Tyrannosaurus 0.3333333 1
#> Triceratops 0.5000000 1A 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.25000000For 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.
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.
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.01694915clustering <- 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.
taxo_tree() builds a tree directly from lineages rather
than from distances. Its topology is the classification itself, its
internal nodes are labeled with the named clades at which the taxa
diverge, and its branch lengths are chosen so that patristic distances
reproduce distance_matrix() exactly. The result is a
standard phylo object.
lineages <- taxo_from_lineages(list(
Tyrannosaurus = c("Biota", "Dinosauria", "Theropoda", "Tyrannosaurus"),
Velociraptor = c("Biota", "Dinosauria", "Theropoda", "Velociraptor"),
Triceratops = c("Biota", "Dinosauria", "Ornithischia", "Triceratops"),
Homo = c("Biota", "Mammalia", "Homo")
))
tree <- taxo_tree(lineages)
tree$node.label
#> [1] "Biota" "Dinosauria" "Theropoda"
plot(tree, show.node.label = TRUE, main = "Classification tree")
tree_distances <- distance_matrix(lineages, progress = FALSE)
all.equal(
ape::cophenetic.phylo(tree)[labels(tree_distances), labels(tree_distances)],
as.matrix(tree_distances)
)
#> [1] TRUEThe tree can be written in Newick format with
ape::write.tree(tree).
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.
taxo_heatmap(
example_matrix,
main = "Taxonomic hierarchy distance matrix",
col = hcl.colors(12, "YlOrRd", rev = TRUE)
)Graphical arguments such as main and col
are passed to stats::heatmap(), while verbose
and progress control lineage retrieval when taxon names are
supplied instead of a matrix.
Additional statistical examples and interpretation are provided in:
Taxonomic names may be missing, ambiguous, redirected, or associated with non-biological homonyms.
taxo_search() returns candidate Taxonomicon entries:
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.
Names are matched after removing surrounding whitespace and
collapsing repeated spaces, and the queried name is located in the
lineage without regard to case. Consequently,
"tyrannosaurus" and " Tyrannosaurus " resolve
to the same node as "Tyrannosaurus".
For batches of names, taxo_resolve() records the status,
candidates, selected identifier, and lineage of every input. Homonyms in
different parts of the classification can often be resolved
automatically by restricting candidates to a clade with
within:
resolved <- taxo_resolve(
c("Tyrannosaurus", "Nereis", "Not_a_real_taxon"),
within = "Animalia"
)
resolved
distance_matrix(resolved)A matrix computed from a resolution is identical to one computed from the same names, but it requires no further network requests and keeps an audit trail.
Before calculating a large matrix, coverage can be checked explicitly:
check_coverage() returns TRUE for names
found, FALSE for names absent from The Taxonomicon, and
NA when the server could not be reached, so a temporary
network failure is never mistaken for missing coverage.
Unresolved taxa generate missing pairwise distances. These values should be examined before clustering, ordination, or other methods requiring complete finite matrices.
Taxonomicon identifiers and lineages are cached in memory during an R session. This avoids repeated requests for the same taxa.
The cache can also be saved and restored:
Called without a file, save_cache() and
load_cache() use a persistent per-user location given by
tools::R_user_dir("taxodist", "cache"):
Cache files preserve retrieved lineages across sessions. They should be regenerated when an analysis is intended to incorporate subsequent revisions of the source classification.
Requests to The Taxonomicon are retried automatically after transient
network or server errors. The number of attempts can be changed with
options(taxodist.max_tries = 5).
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:
taxodist version;Saved caches and distance matrices can preserve the exact input used
in an analysis. taxo_bundle() collects the resolution,
lineages, matrix, distance definition, and provenance in a single object
that can be written to portable JSON. When an analysis is repeated
later, compare_bundles() reports which taxa changed
identifier, status, or lineage, and which distances changed as a
result:
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:
Both the software and the underlying taxonomic source should be cited in published analyses.