Topological Data Analysis: Finding Clusters, Loops, and Voids Across Scales
Machine Learning
Topology
Clustering
TDA
Author
Ravi Kalia
Published
July 22, 2026
Topological Data Analysis: Finding Clusters, Loops, and Voids Across Scales
Clustering output depends on a scale hyperparameter (k, eps, dendrogram cut height). Topological data analysis (TDA) indexes every scale at once via a filtration and records which features persist across many thresholds.
1 Clustering scale parameters
Common clustering methods require one scale before analysis:
K-means: number of groups k.
DBSCAN: neighbourhood radius eps.
Hierarchical clustering: cut height on the merge tree.
Wrong scale collapses real structure or splits one group into noise.
TDA replaces a single threshold with a nested sequence of shapes indexed by radius \(\varepsilon \geq 0\). Features with long lifetimes are treated as signal; features with birth \(\approx\) death are treated as noise.
2 Vietoris-Rips complex
A point cloud has no intrinsic shape until pairs are joined by distance.
Simplices and simplicial complex:
0-simplex: point.
1-simplex: edge (2 points).
2-simplex: filled triangle (3 points).
3-simplex: filled tetrahedron (4 points).
Given \(X = \{x_1, \dots, x_n\}\) and \(\varepsilon \geq 0\), the Vietoris-Rips complex\(R_\varepsilon(X)\) includes a simplex on every subset whose pairwise distances are \(\leq \varepsilon\):
\[
\{x_{i_0}, \dots, x_{i_k}\} \in R_\varepsilon(X) \iff \|x_{i_p} - x_{i_q}\| \leq \varepsilon \text{ for every pair } p, q.
\]
The rule uses only the pairwise distance matrix.
As \(\varepsilon\) increases, simplices only accumulate: \(R_0(X) \subseteq R_{\varepsilon_1}(X) \subseteq R_{\varepsilon_2}(X) \subseteq \cdots\). This nested family is the filtration.
3 Filtration on four points
Four corners of a unit square illustrate components and a loop at distinct scales. Neighbouring corners are distance 1 apart; opposite corners are \(\sqrt{2} \approx 1.414\) apart.
One \(H_1\) interval [1.0, 1.41421354]: born at \(\varepsilon = 1\), dead at \(\varepsilon = \sqrt{2}\). Three finite \(H_0\) bars die at \(\varepsilon = 1\); one component persists (\(\infty\)).
4 Homology dimensions
\(\beta_0\) equals connected components: threshold the distance graph at \(\varepsilon\) and count components. That is single-linkage clustering.
Higher dimensions use the same quotient:
\[
\beta_k = \underbrace{\text{(all $k$-dimensional cycles)}}_{\text{the ``things''}} \Big/ \underbrace{\text{(the ones that bound a $(k{+}1)$-dimensional patch)}}_{\text{``trivial'' cycles}}
\]
\(k=0\): points modulo paths → clusters.
\(k=1\): loops modulo filled disks → holes.
\(k=2\): shells modulo filled solids → voids.
Persistent homology runs this at every \(k\) and every \(\varepsilon\) simultaneously.
5 Noisy ring vs blobs
Data provenance (synthetic):
make_circles (scikit-learn): points on two concentric circles + Gaussian noise.
make_blobs: three separated Gaussian clouds.
Ground truth is known: one dataset has a loop; one does not.
Real analogues: periodic trajectories (ring) vs mixture populations (blobs).
Points lie above the diagonal; vertical distance = lifetime.
Near-diagonal points: short-lived noise.
Far from diagonal: persistent structure.
Ring: one \(H_1\) point far above the diagonal (the loop). Blobs: \(H_1\) near the diagonal; \(H_0\) shows three long-lived components before merge.
5.1 Single-linkage equivalence
\(H_0\) death times equal single-linkage merge heights on the same data.
Code
from scipy.cluster.hierarchy import linkagedgm0 = ripser(blobs, maxdim=0)["dgms"][0]death_times = np.sort(dgm0[np.isfinite(dgm0[:, 1]), 1])Z = linkage(blobs, method="single")merge_heights = np.sort(Z[:, 2])print("H0 death times match single-linkage merge heights:", np.allclose(death_times, merge_heights))
H0 death times match single-linkage merge heights: True
6 TDA vs clustering
Aspect
Traditional clustering
TDA
Scale
One hyperparameter (k, eps, cut height)
All scales in one filtration
Output
Single partition
Persistence diagram (birth/death per feature)
Shape assumptions
Often convex blobs or one density scale
Components, loops, voids without convexity
Noise handling
Depends on hyperparameter
Short lifetime ≈ noise
Sensitivity
Different k/eps → different partitions
Deterministic given distance + filtration
Cost
\(O(n)\)–\(O(n^2)\)
\(O(n^3)\) or worse for full Rips; subsample at scale
Clustering answers “what is the grouping at this scale?” TDA answers “at which scales does structure exist, and with what lifetime?” Loops and voids are not expressible as point partitions.
7 Diagram vectorization
A persistence diagram is a variable-size multiset of points. Models need fixed-length vectors.
Vectorization families:
Summary statistics: max lifetime, total persistence, count above threshold, persistence entropy.
Persistence images: rasterize (birth, lifetime) into a fixed grid, weighted by persistence.
Persistence landscapes: functional summary in a vector space; supports averaging and kernel methods.
Applications: material microstructures, molecular conformations, time series via Takens delay embedding.
7.1 Ring vs blob classifier
Data provenance (synthetic):
60 Gaussian blobs and 60 jittered unit circles, 100 points each.
Labels known by construction.
Task: can \(H_1\) summary statistics separate the two shape classes?
Each cloud → three numbers from its \(H_1\) diagram; random forest for classification.
Perfect hold-out accuracy on this toy split demonstrates the pipeline (diagram → features → classifier), not a general accuracy claim.
8 Practical constraints
Cost: full Rips persistence is roughly \(O(n^3)\). Subsample, use landmark/witness complexes, or cap maximum simplex dimension beyond a few thousand points.
Metric choice: TDA removes the scale hyperparameter, not the metric (Euclidean vs cosine vs learned distance).
Complement to clustering: use \(H_0\) persistence to read cluster count and merge confidence; then run k-means or DBSCAN at an informed scale.
Mapper: cover-based graph summary (separate from persistence diagrams).
Libraries: ripser/persim for fast low-dimensional Rips; GUDHI and giotto-tda for broader complexes and scikit-learn transformers.