Outline

01 · Setting the scene
Methodological background

02 · Building distances with manydist
Variable-specific dissimilarities, scaling, and aggregation

03 · Distance diagnostics
Understanding how variables shape the resulting distances

04 · Distance-based learning pipelines
Integrating distance construction into modelling workflows

05 · Examples and practical insights
Connecting distance choices to downstream results

01 · Setting the scene

Learning from mixed-type data

Many statistical learning methods rely, explicitly or implicitly, on comparing observations.

Terminology alert!

I will often use distance as shorthand for pairwise dissimilarity.

Some measures discussed here are not metrics in the strict mathematical sense,
but they all quantify how different two observations are.

  • If the data are mixed-type, then comparison is not straightforward:

Many learning methods can operate directly on a dissimilarity matrix.

Dimension reduction

Multidimensional scaling (MDS)1

Clustering methods

Hierarchical clustering (HC), PAM2, and spectral clustering3

Nearest-neighbour prediction

Nearest-neighbour classification and nearest-neighbour averaging for regression

Desirable properties1

Multivariate Additivity

Let \(\mathbf{x}_i=\left(x_{i1}, \dots, x_{iQ}\right)\) denote a \(Q-\)dimensional vector. A distance function \(d\left(\mathbf{x}_i,\mathbf{x}_\ell\right)\) between observations \(i\) and \(\ell\) is multivariate additive if

\[ d\left(\mathbf{x}_i,\mathbf{x}_\ell\right)=\sum_{j=1}^{Q} d_j\left(\mathbf{x}_i,\mathbf{x}_\ell\right), \]

where \(d_j\left(\mathbf{x}_i,\mathbf{x}_\ell\right)\) denotes the \(j-\)th variable specific distance.

  • Manhattan distance satisfies the additivity property; the Euclidean distance does not

Desirable properties1

If additivity holds, by-variable distances are added together: they should be on equivalent scales

Commensurability

Let \({\boldsymbol X}_i =\left(X_{i1}, \dots, X_{iQ}\right)\) denote a \(Q-\)dimensional random variable corresponding to an observation \(i\). Furthermore, let \(d_{j}\) denote the distance function corresponding to the \(j-\)th variable.

We have commensurability if, for all \(j\), and \(i \neq \ell\),

\[ E[d_{j}({ X}_{ij}, {X}_{\ell j})] = c, \]

where \(c\) is some constant.

Mixed-data setup

a mixed data set

  • \(n\) observations described by \(Q\) variables, \(Q_{n}\) numerical and \(Q_{c}\) categorical

  • the \(n\times Q\) data matrix \({\bf X}=\left[{\bf X}_{n},{\bf X}_{c}\right]\) is column-wise partitioned

A formulation for mixed distance between observations \(i\) and \(\ell\):

\[\begin{eqnarray}\label{genmixeddist_formula} d\left(\mathbf{x}_i,\mathbf{x}_\ell\right)&=& \sum_{j_n=1}^{Q_n} d_{j_n}\left(\mathbf{x}_i^{(n)},\mathbf{x}_\ell^{(n)}\right)+ \sum_{j_c=1}^{Q_c} d_{j_c}\left(\mathbf{x}_i^{(c)},\mathbf{x}_\ell^{(c)}\right)=\\ &=& \sum_{j_n=1}^{Q_n} w_{j_n} \delta^{(n)}_{j_n}\left(\mathbf{x}_i^{(n)},\mathbf{x}_\ell^{(n)}\right)+ \sum_{j_c=1}^{Q_c} w_{j_c}\delta^{(c)}_{j_c}\left(\mathbf{x}_i^{(c)},\mathbf{x}_\ell^{(c)}\right) \end{eqnarray}\]

numeric case

  • \(\delta^n_{j_n}\) is a function quantifying the dissimilarity between observations on the \(j_n-\)th numerical variable

  • \(w_{j_n}\) is a weight for the \(j_n-\)th variable.

categorical case

dissimilarity between the categories chosen by subjects \(i\) and \(\ell\) for categorical variable \(j_c\)

  • \(w_{j_c}\) is a weight for the \(j_c-\)th variable

Distributions, scaling and bias: the numeric case

Distributions, scaling and bias: the categorical case

  • \(q\in \{2,3,5,10\}\)
  • \(p_1 \in \{0.05,0.1,0.2,0.33, 0.5,0.66, 0.8,0.9,0.95\}\)
  • \(p_j = (1-p_1)/(q-1)\), with \(j=2,\dots,q\),

The expected distance increases with the heterogeneity of the distribution and with the number of categories

Independence-based distances

Independence-based pairwise distance

No inter-variable relations are considered.

  • in the continuous case: Euclidean or Manhattan distances

  • in the categorical case: Hamming / matching distance, among many others

  • in the mixed-data case: Gower dissimilarity index

variable contributions may be balanced, but still treated as separate sources of information

Beyond commensurability

commensurability makes variable contributions comparable across scales and data types.

  • If variables are correlated or associated, the same information may contribute repeatedly to the distance: redundancy

by variable differences: independence-based

  • When variables are correlated or associated, shared information is effectively counted multiple times

  • inflated dissimilarities may cause potential distortions in downstream unsupervised learning tasks.

by variable differences: independence-based

  • When variables are correlated or associated, shared information is effectively counted multiple times

  • inflated dissimilarities may cause potential distortions in downstream unsupervised learning tasks.

by variable differences: independence-based

The Euclidean distance \(\longrightarrow\) shared information is over-counted

accounting for inter-variable relations: association-based

The Mahalanobis distance \(\longrightarrow\) shared information is not over-counted

this is an association-based distance for continuous data

association-based distance

Association-based for continuous: Mahalanobis distance

Let \({\bf X}_{con}\) be \(n\times Q_{d}\) a data matrix of \(n\) observations described by \(Q_{d}\) continuous variables, and let \(\bf S\) the sample covariance matrix, the Mahalanobis distance matrix is

\[ {\bf D}_{mah} = \left[\operatorname{diag}({\bf G})\,{\bf 1}_{n}^{\sf T} + {\bf 1}_{n}\,\operatorname{diag}({\bf G})^{\sf T} - 2{\bf G}\right]^{\odot 1/2} \] where

  • \([\cdot]^{\odot 1/2}\) denotes the element-wise square root

  • \({\bf G}=({\bf C}{\bf X}_{con}){\bf S}^{-1}({\bf C}{\bf X}_{con})^{\sf T}\) is the Mahalanobis Gram matrix

  • \({\bf C}={\bf I}_{n}-\tfrac{1}{n}{\bf 1}_{n}{\bf 1}_{n}^{\sf T}\) is the centering operator

association-based distance

Association-based for categorical: total variation distance (TVD)(Le & Ho, 2005)

The distance matrix \({\bf D}_{tvd}\) can be defined via the delta framework1 upon properly defining the block-diagonal matrix \({\bf \Delta}\)

Let \({\bf X}_{cat}\) be \(n\times Q_{c}\) a data matrix of \(n\) observations described by \(Q_{c}\) categorical variables.

\[ {\bf D} = {\bf Z}{\Delta}{\bf Z}^{\sf T} = \left[\begin{array}{ccc} {\bf Z}_{1} & \dots & {\bf Z}_{Q_{c}} \end{array} \right]\left[\begin{array}{ccc} {\bf\Delta}_1 & & \\ & \ddots &\\ & & {\bf\Delta}_{Q_{c}} \end{array} \right] \left[ \begin{array}{c} {\bf Z}_{1}^{\sf T}\\ \vdots \\ {\bf Z}_{Q_{c}}^{\sf T} \end{array} \right] \]

  • in the framework, setting \({\Delta}_j\) determines the categorical distance measure of choice (independent- or association-based)

association-based distance

Association-based for categorical: total variation distance (TVD) (Le & Ho, 2005) (2)

Consider the empirical joint probability distributions stored in the off-diagonal blocks of \({\bf P}\):

\[ {\bf P} = \frac{1}{n} \begin{bmatrix} {\bf Z}_1^{\sf T}{\bf Z}_1 & {\bf Z}_1^{\sf T}{\bf Z}_2 & \cdots & {\bf Z}_1^{\sf T}{\bf Z}_{Q_c} \\ \vdots & \ddots & \vdots & \vdots \\ {\bf Z}_{Q_c}^{\sf T}{\bf Z}_1 & {\bf Z}_{Q_c}^{\sf T}{\bf Z}_2 & \cdots & {\bf Z}_{Q_c}^{\sf T}{\bf Z}_{Q_c} \end{bmatrix}. \]

The block matrix \(\bf R\) refer to the conditional probability distributions for each variable \(j\) given each variable \(i\) (\(i,j=1,\ldots,Q_c\), \(i\neq j\)), stored in the block matrix

\[ {\bf R} = {\bf P}_z^{-1}({\bf P} - {\bf P}_z). \]

where \({\bf P}_z = {\bf P} \odot {\bf I}_{Q^*}\), and \({\bf I}_{Q^*}\) is the \(Q^*\times Q^*\) identity matrix.

association-based distance: a small example

Data

Consider two categorical variables:

  • \(X_1\) with categories \(A,B,C\)
  • \(X_2\) with categories \(u,v\)
# A tibble: 10 × 3
      id X1    X2   
   <int> <fct> <fct>
 1     1 A     u    
 2     2 A     u    
 3     3 A     v    
 4     4 B     u    
 5     5 B     u    
 6     6 B     v    
 7     7 C     u    
 8     8 C     v    
 9     9 C     v    
10    10 C     v    

Indicator matrices

\[ {\bf Z}_1 = \begin{pmatrix} 1&0&0\\ 1&0&0\\ 1&0&0\\ 0&1&0\\ 0&1&0\\ 0&1&0\\ 0&0&1\\ 0&0&1\\ 0&0&1\\ 0&0&1 \end{pmatrix}, \qquad {\bf Z}_2 = \begin{pmatrix} 1&0\\ 1&0\\ 0&1\\ 1&0\\ 1&0\\ 0&1\\ 1&0\\ 0&1\\ 0&1\\ 0&1 \end{pmatrix}. \]

association-based distance: from \({\bf Z}\) to \({\bf P}\)

Let

\[ {\bf Z} = [{\bf Z}_1,{\bf Z}_2]. \]

The empirical co-occurrence matrix is

\[ {\bf P} = \frac{1}{10}{\bf Z}^{\sf T}{\bf Z}. \]

For this example,

\[ {\bf P} = \begin{pmatrix} \color{#2A9D8F}{0.30} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#E76F51}{0.20} & \color{#E76F51}{0.10}\\ \color{#2A9D8F}{0} & \color{#2A9D8F}{0.30} & \color{#2A9D8F}{0} & \color{#E76F51}{0.20} & \color{#E76F51}{0.10}\\ \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0.40} & \color{#E76F51}{0.10} & \color{#E76F51}{0.30}\\ \color{#E76F51}{0.20} & \color{#E76F51}{0.20} & \color{#E76F51}{0.10} & \color{#2A9D8F}{0.50} & \color{#2A9D8F}{0}\\ \color{#E76F51}{0.10} & \color{#E76F51}{0.10} & \color{#E76F51}{0.30} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0.50} \end{pmatrix}. \]

diagonal blocks contain marginal information; off-diagonal blocks contain joint proportions

association-based distance: from \({\bf P}\) to \({\bf R}\)

The diagonal part of \({\bf P}\) is

\[ {\bf P}_z = {\bf P} \odot {\bf I}_{Q^*} = \operatorname{diag}(0.30,0.30,0.40,0.50,0.50). \]

The block matrix of conditional profiles is

\[ {\bf R} = {\bf P}_z^{-1}({\bf P}-{\bf P}_z). \]

For this example,

\[ {\bf R} = \begin{pmatrix} \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#E76F51}{0.67} & \color{#E76F51}{0.33}\\ \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#E76F51}{0.67} & \color{#E76F51}{0.33}\\ \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0} & \color{#E76F51}{0.25} & \color{#E76F51}{0.75}\\ \color{#E76F51}{0.40} & \color{#E76F51}{0.40} & \color{#E76F51}{0.20} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0}\\ \color{#E76F51}{0.20} & \color{#E76F51}{0.20} & \color{#E76F51}{0.60} & \color{#2A9D8F}{0} & \color{#2A9D8F}{0} \end{pmatrix}. \]

each off-diagonal block contains conditional profiles across variables

association-based distance

Association-based for categorical: total variation distance (TVD)(Le & Ho, 2005) (3)

Let \({\bf r}^{ji}_a\) and \({\bf r}^{ji}_b\) be the rows of \({\bf R}_{ji}\), the \((j,i)\)th off-diagonal block of \({\bf R}\).

The category dissimilarity between \(a\) and \(b\) for variable \(j\) based on the total variation distance (TVD) is defined as

\[ \delta^{j}_{tvd}(a,b) = \sum_{i\neq j}^{Q_c} w_{ji} \Phi^{ji}({\bf r}^{ji}_{a},{\bf r}^{ji}_{b}) = \sum_{i\neq j}^{Q_c} w_{ji} \left[\frac{1}{2}\sum_{\ell=1}^{q_i} |{\bf r}^{ji}_{a\ell}-{\bf r}^{ji}_{b\ell}|\right], \label{ab_delta} \]

where \(w_{ji}=1/(Q_c-1)\) for equal weighting (can be user-defined).

TVD-based dissimilarity matrix is, therefore,

\[ {\bf D}_{tvd}= {\bf Z}{\Delta}^{(tvd)}{\bf Z}^{\sf T}. \]

association-based distance: reading \({\bf R}_{12}\)

For the categories of \(X_1\), the relevant block is

\[ {\bf R}_{12} = \begin{pmatrix} 0.67 & 0.33\\ 0.67 & 0.33\\ 0.25 & 0.75 \end{pmatrix}. \]

Interpretation

Rows of \({\bf R}_{12}\) describe the distribution of \(X_2\) within each category of \(X_1\):

  • \(P(X_2=u \mid X_1=A)=\color{#E76F51}{\mathbf{0.67}}\), \(P(X_2=v \mid X_1=A)=\color{#E76F51}{\mathbf{0.33}}\)
  • \(P(X_2=u \mid X_1=B)=\color{#E76F51}{\mathbf{0.67}}\), \(P(X_2=v \mid X_1=B)=\color{#E76F51}{\mathbf{0.33}}\)
  • \(P(X_2=u \mid X_1=C)=\color{#E76F51}{\mathbf{0.25}}\), \(P(X_2=v \mid X_1=C)=\color{#E76F51}{\mathbf{0.75}}\)

categories are compared through their association profiles

association-based distance: from \({\bf R}\) to \(\Delta_1^{(tvd)}\)

Compare the rows of \({\bf R}_{12}\) using TVD.

\[ \delta^{1}_{tvd}(A,B) = \frac{1}{2} \left( |0.67-0.67| + |0.33-0.33| \right) = {\color{coral}0}. \]

\[ \delta^{1}_{tvd}(A,C) = \frac{1}{2} \left( |0.67-0.25| + |0.33-0.75| \right) = {\color{coral}0.42}. \]

\[ \delta^{tvd}_{1}(B,C) = {\color{coral}0.42}. \]

Therefore,

\[ \Delta^{(tvd)}_1 = \begin{pmatrix} 0 & 0 & {\color{coral}0.42}\\ 0 & 0 & {\color{coral}0.42}\\ {\color{coral}0.42} & {\color{coral}0.42} & 0 \end{pmatrix}. \]

\(A\) and \(B\) are close because they have the same profile with respect to \(X_2\)

association-based distance: reading \({\bf R}_{21}\)

For the categories of \(X_2\), the relevant block is

\[ {\bf R}_{21} = \begin{pmatrix} 0.40 & 0.40 & 0.20\\ 0.20 & 0.20 & 0.60 \end{pmatrix}. \]

Interpretation

Rows of \({\bf R}_{21}\) describe the distribution of \(X_1\) within each category of \(X_2\):

  • \(P(X_1=A \mid X_2=u)=\color{#E76F51}{\mathbf{0.40}}\), \(P(X_1=B \mid X_2=u)=\color{#E76F51}{\mathbf{0.40}}\), \(P(X_1=C \mid X_2=u)=\color{#E76F51}{\mathbf{0.20}}\)
  • \(P(X_1=A \mid X_2=v)=\color{#E76F51}{\mathbf{0.20}}\), \(P(X_1=B \mid X_2=v)=\color{#E76F51}{\mathbf{0.20}}\), \(P(X_1=C \mid X_2=v)=\color{#E76F51}{\mathbf{0.60}}\)

categories of \(X_2\) are compared through their association profiles with respect to \(X_1\)

association-based distance: from \({\bf R}_{21}\) to \(\Delta_2^{(tvd)}\)

Compare the rows of \({\bf R}_{21}\) using TVD.

\[ \delta^{tvd}_{1}(u,v) = \frac{1}{2} \left( |0.40-0.20| + |0.40-0.20| + |0.20-0.60| \right) = {\color{coral}0.40}. \]

Therefore,

\[ \Delta^{(tvd)}_2 = \begin{pmatrix} 0 & {\color{coral}0.40}\\ {\color{coral}0.40} & 0 \end{pmatrix}. \]

\(u\) and \(v\) are different because they imply different profiles over \(X_1\)

association-based distance: from \(\Delta\) to \({\bf D}\)

We collect the category dissimilarity matrices in a block-diagonal matrix:

\[ \Delta^{(tvd)} = \begin{pmatrix} \color{#2A9D8F}{\Delta^{(tvd)}_1} & \color{#E76F51}{0}\\ \color{#E76F51}{0} & \color{#2A9D8F}{\Delta^{(tvd)}_2} \end{pmatrix}. \]

The observation-level categorical distance matrix is then

\[ {\bf D}_{tvd} = {\bf Z}\Delta^{(tvd)}{\bf Z}^{\sf T} = \begin{bmatrix} {\bf Z}_1 & {\bf Z}_2 \end{bmatrix} \begin{pmatrix} \Delta^{(tvd)}_1 & 0\\ 0 & \Delta^{(tvd)}_2 \end{pmatrix} \begin{bmatrix} {\bf Z}_1^{\sf T}\\ {\bf Z}_2^{\sf T} \end{bmatrix}. \]

Equivalently,

\[ {\bf D}_{tvd} = {\bf Z}_1\Delta^{(tvd)}_1{\bf Z}_1^{\sf T} + {\bf Z}_2\Delta^{(tvd)}_2{\bf Z}_2^{\sf T}. \]

category-level dissimilarities are translated into observation-level distances

02 · Building distances with manydist

Building distance-based pipelines

manydist

A package to construct, diagnose, and use distances for continuous, categorical, and mixed-type data.

Distance construction

  • mdist()
    • presets and custom specifications
    • response-aware distances
  • step_mdist()
    • integrates distance construction into tidymodels workflows

Diagnostics

  • lovo_mdist()
  • compare_lovo_mdist()
  • benchmark_mdist()

Learning: model specs

Unsupervised learning

  • pam_dist()
  • spectral_dist()

Supervised learning

  • nearest_neighbor_dist()

the WDI data

A 2022 World Bank / WDI snapshot of country-level socio-economic indicators.

Country Region Income group World Bank lending category GDP per capita (k USD) Life expectancy (years) Unemployment (%) Urban population (% total) Population growth (%)
Tajikistan Europe & Central Asia Lower middle income IDA 1.1 71.6 7.1 26.2 2.14
West Bank and Gaza Middle East, North Africa, Afghanistan & Pakistan Lower middle income Not classified 3.8 76.7 24.4 86.6 2.43
Belarus Europe & Central Asia Upper middle income IBRD 8.0 74.1 3.6 78.5 -0.80
United Arab Emirates Middle East, North Africa, Afghanistan & Pakistan High income Not classified 50.8 80.5 2.9 85.5 5.09
El Salvador Latin America & Caribbean Upper middle income IBRD 5.1 72.0 3.0 74.1 0.39
New Zealand East Asia & Pacific High income Not classified 49.1 82.0 3.3 83.9 -0.06
Cyprus Europe & Central Asia High income Not classified 33.2 80.4 6.8 66.7 1.06
Zambia Sub-Saharan Africa Lower middle income IDA 1.4 65.3 6.0 44.6 2.76

mdist(): the main arguments

Data

  • x: training data—numeric, categorical, or mixed
  • new_data: optional observations to compare with x
  • response: optional outcome for response-aware distances

Choose a construction

  • preset: a ready-made distance specification
  • Use preset = "custom" for full control

Custom distance controls

  • method_cat: categorical dissimilarity (matching, tvd and MANY more)
  • method_num: numerical preprocessing (std, robust, pc_scores)
  • commensurable: balance variable contributions

Specialized controls

  • ncomp, threshold: component retention
  • gower_average: average or sum Gower contributions

mdist() output: an MDist R6 object

The object

[1] "MDist" "R6"   

The result keeps the distance and its specification together:

  • d$distance: pairwise or new-to-training dissimilarities
  • d$preset: selected construction
  • d$data: training data
  • d$params: fitted parameters and metadata
  • d$to_dist(): convert to a standard dist object

Printing the object

MDist object
  preset : u_mix 
  number of observations : 181 
  number of continuous variables   : 5 
  number of categorical variables   : 3 
  parameters:
    - categorical method: tvd
    - numerical preprocessing: std
    - commensurability adjustment: TRUE

Reports the preset, number of observations and variables, and the active distance parameters.

Summarising the distance

MDist summary
  Preset : u_mix 
  Observations : 181 
  Continuous vars : 5 
  Categorical vars: 3 

Distance matrix statistics:
  Mean : 8.0444 
  SD   : 3.5231 
  Min  : 0.3832 
  Max  : 24.0576 
  Diagonal all zeros (block): YES 
  Symmetric (block check)   : YES 
  Triangle viol. rate (block): 0.000 
  NA count:0
  Inf count:0

Commensurable mixed-type distance using std numerical preprocessing and tvd categorical component. 

Adds:

  • mean, SD, minimum, and maximum dissimilarity
  • zero-diagonal and symmetry checks
  • an approximate triangle-inequality check
  • counts of missing and infinite values

Constructing a mixed-type distance using a preset

Directly with mdist()

d_preset <- mdist(
  wdi_x,
  preset = "u_dep"
)

Inside a recipe with step_mdist()

rec_preset <- recipe(~ ., data = wdi_x) |>
  step_mdist(
    all_predictors(),
    preset = "u_dep"
  )

d_preset_step <- rec_preset |>
  prep(training = wdi_x) |>
  bake(new_data = NULL)
all.equal(
  unname(as.matrix(d_preset$distance)),
  unname(as.matrix(d_preset_step))
)
[1] TRUE

step_mdist() embeds the same distance specification into a modelling workflow

Constructing a custom mixed-type distance

Directly with mdist()

wdi_x <- wdi_data |> 
  dplyr::select(-country)

d_custom <- mdist(
  wdi_x,
  method_cat  = "eskin",
  method_num  = "std",
  commensurable = FALSE
)

Inside a recipe with step_mdist()

rec_custom <- recipe(~ ., data = wdi_x) |>
  step_mdist(
    all_predictors(),
    method_cat  = "eskin",
    method_num  = "std",
    commensurable = FALSE
  )

d_custom_step <- rec_custom |>
  prep(training = wdi_x) |>
  bake(new_data = NULL)
all.equal(
  unname(as.matrix(d_custom$distance)),
  unname(as.matrix(d_custom_step))
)
[1] TRUE

03 · Distance diagnostics

No one-size-fits-all distance

Different distance definitions induce different distance-based representations of the same data.

Same data, different representation

Changing the distance changes the global dissimilarity structure on which downstream learning methods rely.

Leave-one-variable-out diagnostics

How can we measure the contribution of each variable to this structure?

  • compare the dissimilarity matrix computed with and without the variable in question

LOVO-based diagnostics: what is evaluated?

For each variable \(X_j\), compare the full distance \({\bf D}\) with the distance obtained after removing it, \({\bf D}_{-j}\).

Distance structure

Always computed

  • mean absolute difference
  • relative distance change

Outputs include mad_importance and relative_distance.

MDS geometry

Optional: mds = TRUE

Compare MDS configurations using congruence and the alienation coefficient.

dims controls the number of MDS dimensions.

Cluster stability

Optional: supply cluster_k

Compare full and LOVO partitions using the adjusted Rand index (ARI).

Importance is reported as \(1-\mathrm{ARI}\).

LOVO can stop at the distance matrix—or continue into a chosen downstream representation.

lovo_mdist(): controlling the diagnostics

Data and distance

  • x: data used to construct the distance
  • response, response_used: optional response-aware construction
  • ...: arguments passed to mdist(), such as preset, method_cat, and method_num
  • keep_dist: retain the full and LOVO distance matrices

Optional MDS diagnostics

  • mds = TRUE: enable MDS comparisons
  • dims: number of retained MDS dimensions

Optional clustering diagnostics

  • cluster_k: number of clusters; NULL disables clustering
  • cluster_methods: "pam", "hclust", and/or "spectral"
  • hclust_method: linkage for hierarchical clustering
  • spectral_sigma, spectral_nstart: spectral-clustering controls

Output

An MDistLOVO R6 object. Its $results tibble has one row per left-out variable and supports print(), summary(), and autoplot().

compare_lovo_mdist(): WDI example

compare_lovo_mdist() applies the same LOVO diagnostics to a named list of distance specifications.

mds and cluster_k make the optional diagnostics available; autoplot() selects the metric to display.

WDI: MDS geometry

autoplot(lovo_wdi_compare, metric = "ac_importance")

Larger alienation means that leaving out a variable produces a stronger change in the MDS configuration.

WDI: PAM clustering stability

autoplot(lovo_wdi_compare, metric = "pam_importance")

Larger \(1-\mathrm{ARI}\) means that leaving out a variable produces a stronger change in the PAM partition.

benchmark_mdist(): comparing distance choices

While LOVO asks which variables matter within a distance, benchmark_mdist() asks how much candidate distance constructions differ from one another.

Main features

  • one mdist() run for each row of specs
  • failures are recorded without stopping the benchmark
  • successful distances are compared pairwise
  • distance and MDS diagnostics are computed; clustering ARI is optional
  • returns an MDistBenchmark tibble

Inspecting the result

  • summary(wdi_benchmark)

  • autoplot(wdi_benchmark, metric = "relative_distance")

benchmark_mdist(): WDI summary

summary(wdi_benchmark)
Summary of MDistBenchmark
  specifications : 4
  successful     : 4
  failed         : 0
  comparisons    : 6

Pairwise diagnostic summary:
# A tibble: 5 × 4
  metric              min median   max
  <chr>             <dbl>  <dbl> <dbl>
1 mad               1.06   4.66  7.64 
2 relative_distance 0.132  1.20  1.81 
3 mds_congruence    0.947  0.972 0.989
4 alienation        0.150  0.235 0.320
5 ari_pam           0.637  0.663 0.763

The summary reports the range of pairwise differences across successful distance specifications: raw and relative distance changes, MDS congruence and alienation, and—when requested—clustering ARI.

benchmark_mdist(): WDI pairwise comparison

autoplot(wdi_benchmark, metric = "relative_distance")

Each cell compares two complete distance constructions; larger values indicate more substantial disagreement between their pairwise dissimilarities.

04 · Distance-based learning pipelines

Distance-based classification pipeline

set.seed(123)
wdi_region <- wdi_data |>
  dplyr::filter(region != "North America") |>
  dplyr::mutate(
    region = droplevels(region)
  )
wdi_split <- initial_split(
  wdi_region,
  strata = region
)
wdi_train <- training(wdi_split)
wdi_test  <- testing(wdi_split)
wdi_rec <- recipe(region ~ ., data = wdi_train) |>
  update_role(country, new_role = "id") |>
  step_mdist(
    all_predictors(),
    preset = "u_dep"
  )
knn_spec <- nearest_neighbor_dist(
  mode = "classification",
  neighbors = tune()
)
wdi_wf <- workflow() |>
  add_recipe(wdi_rec) |>
  add_model(knn_spec)

Data

Prepare the classification task.

Distance-based classification pipeline

set.seed(123)
wdi_region <- wdi_data |>
  dplyr::filter(region != "North America") |>
  dplyr::mutate(
    region = droplevels(region)
  )
wdi_split <- initial_split(
  wdi_region,
  strata = region
)
wdi_train <- training(wdi_split)
wdi_test  <- testing(wdi_split)
wdi_rec <- recipe(region ~ ., data = wdi_train) |>
  update_role(country, new_role = "id") |>
  step_mdist(
    all_predictors(),
    preset = "u_dep"
  )
knn_spec <- nearest_neighbor_dist(
  mode = "classification",
  neighbors = tune()
)
wdi_wf <- workflow() |>
  add_recipe(wdi_rec) |>
  add_model(knn_spec)

Data

Prepare the classification task.

Split

Create training and test sets.

Distance-based classification pipeline

set.seed(123)
wdi_region <- wdi_data |>
  dplyr::filter(region != "North America") |>
  dplyr::mutate(
    region = droplevels(region)
  )
wdi_split <- initial_split(
  wdi_region,
  strata = region
)
wdi_train <- training(wdi_split)
wdi_test  <- testing(wdi_split)
wdi_rec <- recipe(region ~ ., data = wdi_train) |>
  update_role(country, new_role = "id") |>
  step_mdist(
    all_predictors(),
    preset = "u_dep"
  )
knn_spec <- nearest_neighbor_dist(
  mode = "classification",
  neighbors = tune()
)
wdi_wf <- workflow() |>
  add_recipe(wdi_rec) |>
  add_model(knn_spec)

Data

Prepare the classification task.

Split

Create training and test sets.

Recipe

Use step_mdist() to construct the distance representation.

Distance-based classification pipeline

set.seed(123)
wdi_region <- wdi_data |>
  dplyr::filter(region != "North America") |>
  dplyr::mutate(
    region = droplevels(region)
  )
wdi_split <- initial_split(
  wdi_region,
  strata = region
)
wdi_train <- training(wdi_split)
wdi_test  <- testing(wdi_split)
wdi_rec <- recipe(region ~ ., data = wdi_train) |>
  update_role(country, new_role = "id") |>
  step_mdist(
    all_predictors(),
    preset = "u_dep"
  )
knn_spec <- nearest_neighbor_dist(
  mode = "classification",
  neighbors = tune()
)
wdi_wf <- workflow() |>
  add_recipe(wdi_rec) |>
  add_model(knn_spec)

Data

Prepare the classification task.

Split

Create training and test sets.

Recipe

Use step_mdist() to construct the distance representation.

Model

Specify a distance-based KNN classifier.

Distance-based classification pipeline

set.seed(123)
wdi_region <- wdi_data |>
  dplyr::filter(region != "North America") |>
  dplyr::mutate(
    region = droplevels(region)
  )
wdi_split <- initial_split(
  wdi_region,
  strata = region
)
wdi_train <- training(wdi_split)
wdi_test  <- testing(wdi_split)
wdi_rec <- recipe(region ~ ., data = wdi_train) |>
  update_role(country, new_role = "id") |>
  step_mdist(
    all_predictors(),
    preset = "u_dep"
  )
knn_spec <- nearest_neighbor_dist(
  mode = "classification",
  neighbors = tune()
)
wdi_wf <- workflow() |>
  add_recipe(wdi_rec) |>
  add_model(knn_spec)

Data

Prepare the classification task.

Split

Create training and test sets.

Recipe

Use step_mdist() to construct the distance representation.

Model

Specify a distance-based KNN classifier.

Workflow

Combine preprocessing and model specification.

Tuning a distance-based KNN pipeline

set.seed(123)
wdi_folds <- vfold_cv(
  wdi_train,
  v = 5,
  strata = region
)
knn_grid <- tibble(
  neighbors = c(1, 3, 5, 7, 9, 11, 15)
)
knn_tuned <- tune_grid(
  wdi_wf,
  resamples = wdi_folds,
  grid = knn_grid,
  metrics = metric_set(accuracy)
)
best_k <- select_best(
  knn_tuned,
  metric = "accuracy"
)
final_wf <- finalize_workflow(
  wdi_wf,
  best_k
)
final_res <- last_fit(
  final_wf,
  split = wdi_split,
  metrics = metric_set(accuracy)
)

Resample

Create cross-validation folds on the training set.

Tuning a distance-based KNN pipeline

set.seed(123)
wdi_folds <- vfold_cv(
  wdi_train,
  v = 5,
  strata = region
)
knn_grid <- tibble(
  neighbors = c(1, 3, 5, 7, 9, 11, 15)
)
knn_tuned <- tune_grid(
  wdi_wf,
  resamples = wdi_folds,
  grid = knn_grid,
  metrics = metric_set(accuracy)
)
best_k <- select_best(
  knn_tuned,
  metric = "accuracy"
)
final_wf <- finalize_workflow(
  wdi_wf,
  best_k
)
final_res <- last_fit(
  final_wf,
  split = wdi_split,
  metrics = metric_set(accuracy)
)

Resample

Create cross-validation folds on the training set.

Grid

Define candidate values for the number of neighbours.

Tuning a distance-based KNN pipeline

set.seed(123)
wdi_folds <- vfold_cv(
  wdi_train,
  v = 5,
  strata = region
)
knn_grid <- tibble(
  neighbors = c(1, 3, 5, 7, 9, 11, 15)
)
knn_tuned <- tune_grid(
  wdi_wf,
  resamples = wdi_folds,
  grid = knn_grid,
  metrics = metric_set(accuracy)
)
best_k <- select_best(
  knn_tuned,
  metric = "accuracy"
)
final_wf <- finalize_workflow(
  wdi_wf,
  best_k
)
final_res <- last_fit(
  final_wf,
  split = wdi_split,
  metrics = metric_set(accuracy)
)

Resample

Create cross-validation folds on the training set.

Grid

Define candidate values for the number of neighbours.

Tune

Evaluate each candidate value by cross-validation.

Tuning a distance-based KNN pipeline

set.seed(123)
wdi_folds <- vfold_cv(
  wdi_train,
  v = 5,
  strata = region
)
knn_grid <- tibble(
  neighbors = c(1, 3, 5, 7, 9, 11, 15)
)
knn_tuned <- tune_grid(
  wdi_wf,
  resamples = wdi_folds,
  grid = knn_grid,
  metrics = metric_set(accuracy)
)
best_k <- select_best(
  knn_tuned,
  metric = "accuracy"
)
final_wf <- finalize_workflow(
  wdi_wf,
  best_k
)
final_res <- last_fit(
  final_wf,
  split = wdi_split,
  metrics = metric_set(accuracy)
)

Resample

Create cross-validation folds on the training set.

Grid

Define candidate values for the number of neighbours.

Tune

Evaluate each candidate value by cross-validation.

Select

Choose the best-performing number of neighbours.

Tuning a distance-based KNN pipeline

set.seed(123)
wdi_folds <- vfold_cv(
  wdi_train,
  v = 5,
  strata = region
)
knn_grid <- tibble(
  neighbors = c(1, 3, 5, 7, 9, 11, 15)
)
knn_tuned <- tune_grid(
  wdi_wf,
  resamples = wdi_folds,
  grid = knn_grid,
  metrics = metric_set(accuracy)
)
best_k <- select_best(
  knn_tuned,
  metric = "accuracy"
)
final_wf <- finalize_workflow(
  wdi_wf,
  best_k
)
final_res <- last_fit(
  final_wf,
  split = wdi_split,
  metrics = metric_set(accuracy)
)

Resample

Create cross-validation folds on the training set.

Grid

Define candidate values for the number of neighbours.

Tune

Evaluate each candidate value by cross-validation.

Select

Choose the best-performing number of neighbours.

Finalize

Insert the selected value into the workflow.

Tuning a distance-based KNN pipeline

set.seed(123)
wdi_folds <- vfold_cv(
  wdi_train,
  v = 5,
  strata = region
)
knn_grid <- tibble(
  neighbors = c(1, 3, 5, 7, 9, 11, 15)
)
knn_tuned <- tune_grid(
  wdi_wf,
  resamples = wdi_folds,
  grid = knn_grid,
  metrics = metric_set(accuracy)
)
best_k <- select_best(
  knn_tuned,
  metric = "accuracy"
)
final_wf <- finalize_workflow(
  wdi_wf,
  best_k
)
final_res <- last_fit(
  final_wf,
  split = wdi_split,
  metrics = metric_set(accuracy)
)

Resample

Create cross-validation folds on the training set.

Grid

Define candidate values for the number of neighbours.

Tune

Evaluate each candidate value by cross-validation.

Select

Choose the best-performing number of neighbours.

Finalize

Insert the selected value into the workflow.

Test

Fit the finalized workflow on the training set and evaluate it on the test set.

Tuning results

Test-set performance

Metric Accuracy
accuracy 0.553

Workflow

last_fit() fits the finalized workflow on the full training set and evaluates it once on the held-out test set.

05 · Examples and practical insights

UCI Bank Marketing Dataset

Applied question

Can we identify distinct customer profiles from stable customer characteristics?

Variable Type Description
age Numeric Age in years
job Categorical Occupation
marital Categorical Marital status
education Categorical Education level
default Binary Credit currently in default
balance Numeric Average yearly account balance (€)
housing Binary Has a housing/mortgage loan
loan Binary Has a personal loan
y Binary Subscribed to a term deposit; external only

Choice of candidate distances

Objective

Are conclusions stable across defensible definitions of similarity?

Distance Preset Interpretation
Gower gower range-normalized Manhattan + simple matching
u_indep u_indep standard-deviation scaling + simple matching (commensurable)
Hennig-Liao hl standard-deviation scaling + Hennig--Liao indicator scaling
u_mix u_mix standard-deviation scaling + total variation distance (commensurable)

First check: what cluster structure is suggested?

Distance Best k ASW
u_mix 4 0.331
u_indep 5 0.284
Gower 7 0.263
Hennig-Liao 8 0.165

Within (k=2,,8), different distance constructions may favour different values of (k).

For the remainder of the comparison, however, we deliberately fix (k=6) for every method.

Controlled comparison: hold (k) fixed

What stays constant?

same 1,500 customers + same variables + same PAM algorithm + (k=6)

 

What changes?

Only the definition of dissimilarity.

This isolates the consequences of distance construction for the resulting geometry and partition.

LOVO across distances

If a variable is removed, how much does the distance representation change?

LOVO across distances

This asks a different question: if a variable is removed, how much does the PAM partition change?

Benchmarking the distance constructions

All solutions use PAM with (k=6).

Therefore, disagreement in ARI reflects changes in cluster membership induced by the distance construction.

One solution in detail: ordinary Gower + PAM

Cluster n Age Balance Housing Marital Education Main jobs Subscribed (%)
1 221 33.5 978 yes (100%) single (82%) secondary (75%) technician (39%); blue-collar (20%) 10.9%
2 204 39.0 1994 yes (100%) married (67%) tertiary (92%) management (69%); technician (9%) 11.3%
3 257 51.3 1756 no (100%) married (88%) secondary (67%) blue-collar (29%); retired (17%) 14.0%
4 433 42.0 1095 yes (100%) married (88%) secondary (75%) blue-collar (43%); admin. (19%) 6.9%
5 187 33.3 1092 no (100%) single (83%) secondary (71%) technician (33%); blue-collar (13%) 19.3%
6 198 41.4 1948 no (100%) married (66%) tertiary (89%) management (67%); technician (9%) 15.2%

This table gives a substantive interpretation of one of the four (k=6) solutions.

The previous analyses tell us which parts of that segmentation are sensitive—or robust—to changing the distance definition.

External profiling: term-deposit uptake

Distance k ASW Cramér's V Min subscribed Max subscribed
Gower 6 0.262 0.124 6.9% 19.3%
u_indep 6 0.258 0.138 4.3% 16.3%
Hennig-Liao 6 0.136 0.150 5.1% 21.4%
u_mix 6 0.293 0.135 4.4% 16.0%

y was not used to construct any distance or clustering solution.

All methods again use (k=6), so these are comparable external descriptions of the four partitions.

MCA of the clustering solutions

Clusters that lie close in the MCA representation tend to have similar patterns of customer membership.

Which customer groups persist?

Which clusters obtained under different distance definitions actually contain the same customers?

Strongest cross-method cluster correspondences

Cluster 1 Cluster 2 n1 n2 Shared Jaccard % of cluster 1 % of cluster 2
u_indep: C6 u_mix: C6 27 27 27 1.00 100% 100%
u_indep: C3 u_mix: C3 184 183 183 0.99 99% 100%
u_indep: C2 u_mix: C2 408 419 403 0.95 99% 96%
u_indep: C5 u_mix: C5 301 286 277 0.89 92% 97%
u_indep: C1 u_mix: C1 295 298 278 0.88 94% 93%
u_indep: C4 u_mix: C4 285 287 264 0.86 93% 92%
Hennig-Liao: C3 u_indep: C3 178 184 167 0.86 94% 91%
Hennig-Liao: C3 u_mix: C3 178 183 166 0.85 93% 91%

Jaccard measures symmetric overlap.

The final two columns show how much of each cluster is recovered by its corresponding cluster under the alternative distance.

What do the recurring customer groups look like?

Cluster 1 Cluster 2 Shared n Mean age Mean balance Housing Marital Education Main job Term-deposit uptake
u_indep: C6 u_mix: C6 27 38.6 -129 yes (67%) married (52%) secondary (70%) blue-collar (33%) 11.1%
u_indep: C3 u_mix: C3 183 39.7 1030 yes (73%) married (67%) secondary (67%) blue-collar (27%) 4.4%
u_indep: C2 u_mix: C2 403 41.8 1217 yes (100%) married (90%) secondary (67%) blue-collar (36%) 7.7%
u_indep: C5 u_mix: C5 277 38.4 1936 no (73%) married (63%) tertiary (96%) management (73%) 15.2%
u_indep: C1 u_mix: C1 278 33.0 1489 yes (75%) single (98%) secondary (64%) technician (26%) 16.5%
u_indep: C4 u_mix: C4 264 50.3 1713 no (100%) married (81%) secondary (68%) blue-collar (24%) 15.5%

These profiles describe the shared core of customers assigned together under two different distance constructions.

They reveal substantive customer groups that persist when the definition of similarity changes.

Practical insight from the application

Not: default distance → clustering → interpretation

A clustering result inherits the assumptions of the distance construction.

Instead: distance-aware experimentation

define plausible distances → inspect variable behaviour → compare representations → compare partitions → identify recurring groups → interpret

With manydist, distance choice becomes something we can investigate rather than something we silently inherit from a default.

Main references

van de Velden, M., Iodice D’Enza, A., Markos, A., & Cavicchia, C. (2026). Unbiased mixed-variable distance. Journal of Computational and Graphical Statistics. https://doi.org/10.1080/10618600.2026.2680181
van de Velden, M., Iodice D’Enza, A., Markos, A., & Cavicchia, C. (2024). A general framework for implementing distances for categorical variables. Pattern Recognition, 153, 110547.
Kaufman, L., & Rousseeuw, P. J. (2009). Finding groups in data: An introduction to cluster analysis. John Wiley & Sons.
Borg, I., & Groenen, P. J. (2005). Modern multidimensional scaling: Theory and applications. Springer.
Le, S. Q., & Ho, T. B. (2005). An association-based dissimilarity measure for categorical data. Pattern Recognition Letters, 26(16), 2549–2557.
Ng, A. Y., Jordan, M. I., & Weiss, Y. (2002). On spectral clustering: Analysis and an algorithm. Advances in Neural Information Processing Systems, 14, 849–856. MIT Press.

CARME 2027 in Corfu

CARME 2027 logo

June 22–25, 2027

Ionian University
Corfu, Greece

The tenth international CARME conference brings together work on correspondence analysis, categorical data, visualization, clustering, and related methods.

Invited speakers

Mikael Börjesson
Uppsala University

Paul Eilers
Erasmus University Medical Center

Patrick J. F. Groenen
Erasmus School of Economics

Trevor Hastie
Stanford University

Heungsun Hwang
McGill University

Ndèye Niang
CNAM Paris

Contact and slides

Contact

Alfonso Iodice D’Enza
iodicede@unina.it

Angelos Markos
amarkos@eled.duth.gr

GitHub Pages

https://alfonsoiodicede.github.io

Package website

QR code for the manydist package website

Slides

QR code for the MuViSU presentation slides