Distance Metrics
A distance metric quantifies how dissimilar two observations are. The choice of metric is as consequential as the choice of method: clustering, ordination, and PERMANOVA all operate on a distance matrix, and the biological interpretation of results depends directly on how distances are defined.
Properties of a Valid Distance Metric
A metric must satisfy four conditions:
- Non-negativity: d(x, y) ≥ 0
- Identity: d(x, x) = 0
- Symmetry: d(x, y) = d(y, x)
- Triangle inequality: d(x, z) ≤ d(x, y) + d(y, z)
Dissimilarity measures that violate the triangle inequality are called semi-metrics. Bray-Curtis is technically a semi-metric but is still widely used because it has good ecological properties. Methods such as NMDS and PERMANOVA work with any dissimilarity matrix and do not require a true metric.
Metrics by Data Type
Euclidean
The straight-line distance in multivariate space. The default for continuous variables on comparable scales.
dist(data_scaled, method = "euclidean")
Sensitive to variables on different scales. Always scale continuous data before computing Euclidean distances.
Manhattan
The sum of absolute differences rather than squared differences. Less sensitive to outliers than Euclidean because large differences are not squared.
dist(data_scaled, method = "manhattan")
Correlation distance
Measures dissimilarity in pattern rather than magnitude. Two samples with identical relative profiles but different absolute values will have a correlation distance of zero.
as.dist(1 - cor(t(data), use = "pairwise.complete.obs"))
Widely used for gene expression data, where the shape of an expression profile across samples matters more than its absolute level.
Mahalanobis
Euclidean distance after accounting for correlations between variables. Observations that differ along a direction of high covariance are treated as closer than the raw Euclidean distance would suggest.
# Requires a covariance matrix
S <- cov(data)
mahalanobis(data, colMeans(data), S)
Useful when variables are correlated and you want to down-weight redundant dimensions.
Bray-Curtis
The standard dissimilarity for species count and OTU data. Focuses on shared abundance, bounded between 0 (identical) and 1 (no shared species).
library(vegan)
vegdist(otu_table, method = "bray")
Ignores joint absences: two sites that both lack a species are not considered more similar because of it. This is usually desirable for ecological and microbiome data.
Jaccard
Defined for presence/absence data. The proportion of species present in at least one site that are present in both.
vegdist(pa_table, method = "jaccard")
# or for binary data:
dist(pa_table, method = "binary")
Use Jaccard when abundance information is unreliable or when you want to focus on species turnover rather than relative abundance differences.
Canberra
A weighted version of Manhattan distance where each term is normalised by the sum of the two values. Gives equal weight to differences among rare and common species.
vegdist(data, method = "canberra")
More sensitive to rare species than Bray-Curtis. Can be useful when rare taxa carry biological signal, but is sensitive to low-abundance noise.
Aitchison distance
The Euclidean distance computed on CLR-transformed data. The correct distance for compositional data (relative abundances that sum to a constant).
library(microbiome)
ps_clr <- transform(physeq, "clr")
otu_clr <- as.data.frame(t(otu_table(ps_clr)))
dist(otu_clr, method = "euclidean") # Aitchison distance
Preferred when the compositional constraint matters and you want interpretable distances. Equivalent to running PCA on CLR-transformed data (Aitchison PCA).
UniFrac
Measures the fraction of the phylogenetic tree that is unique to one sample or shared between samples. Requires a phylogenetic tree alongside the abundance table.
Unweighted UniFrac considers only presence/absence of lineages. Weighted UniFrac weights branches by relative abundance.
library(phyloseq)
# Unweighted: presence/absence of lineages
dist_unifrac <- UniFrac(physeq, weighted = FALSE)
# Weighted: accounts for relative abundance
dist_wunifrac <- UniFrac(physeq, weighted = TRUE)
Use weighted UniFrac when abundant taxa drive the ecological signal. Use unweighted UniFrac when rare but phylogenetically distinct lineages matter.
Quick Reference
| Metric | Data type | Scales needed | Joint absences | In R |
|---|---|---|---|---|
| Euclidean | Continuous | Yes | Counted | dist(..., "euclidean") |
| Manhattan | Continuous | Yes | Counted | dist(..., "manhattan") |
| Correlation | Continuous (profiles) | No | N/A | 1 - cor(t(data)) |
| Bray-Curtis | Counts, abundance | No | Ignored | vegdist(..., "bray") |
| Jaccard | Presence/absence | No | Ignored | vegdist(..., "jaccard") |
| Canberra | Counts (rare taxa) | No | Ignored | vegdist(..., "canberra") |
| Aitchison | Compositional | No | N/A | CLR then dist(..., "euclidean") |
| UniFrac | Microbiome + tree | No | Ignored | UniFrac(physeq) |
Choosing a Metric
Continuous environmental or morphological variables: use Euclidean after scaling. If outliers are a concern, Manhattan is more robust.
Species abundance or OTU tables: use Bray-Curtis. It is the most widely used and well-validated metric for ecological community data.
Presence/absence data: use Jaccard. It focuses on species turnover and is unaffected by abundance differences.
Microbiome data: use Bray-Curtis for most purposes. If a phylogenetic tree is available and phylogenetic relatedness should influence distances, use weighted UniFrac. For compositional analysis with interpretable distances, use Aitchison.
Gene expression profiles: use correlation distance when the shape of the expression trajectory matters more than absolute levels. Use Euclidean on VST-transformed counts when absolute differences matter.
Effect of Metric Choice
The same dataset can produce very different ordinations and clusterings depending on the metric. When in doubt, run two metrics and compare. Strong disagreement between them is informative: it means the structure in your data is sensitive to how you define similarity.
library(vegan)
# Compare Bray-Curtis and Jaccard on the same data
dist_bray <- vegdist(dune, method = "bray")
dist_jaccard <- vegdist(dune, method = "jaccard", binary = TRUE)
# Mantel test: are the two distance matrices correlated?
mantel(dist_bray, dist_jaccard, permutations = 999)
# High r: metrics agree on structure
# Low r: metrics capture different aspects of the data