Cluster reliability
The question: if you see a cluster in the plot, is it one cluster in the data — and if the data has a cluster, is it drawn as one?
This is the cluster-level version of trustworthiness and continuity, and it catches something they cannot. Trustworthiness works point by point, so a projection that takes one group and slides it apart into two halves keeps every local neighbourhood intact and scores 0.997.
The two numbers
Section titled “The two numbers”snc returns both at once, because they are the two directions of the same idea:
- Steadiness — does a group that looks separate in the projection stay together in the data? Low steadiness means the projection has invented a separation.
- Cohesiveness — does a group that is together in the data stay together in the projection? Low cohesiveness means the projection has split something real.
const s = snc(hd, ld, { iterations: 100, seed: 42 });s.steadiness; // 0.9097s.cohesiveness; // 0.7058The case it exists for
Section titled “The case it exists for”One group in the data, drawn as two:
Loading…
Trustworthiness 0.997, continuity 0.971 — the point-level view sees nothing wrong. Cohesiveness 0.314, because the group that was together is now two things.
Note it is cohesiveness that drops, not steadiness. The projection has not merged anything that was apart; it has split something that was together. Getting these two the right way round is the whole reason they are reported separately.
How it works
Section titled “How it works”Neighbours are shared-nearest-neighbour, built through an inverted index in
rather than the obvious . A cluster is then drawn, not found: a random
walk starts at a random point and steps along the k-NN graph, accepting each edge with
probability equal to its shared-neighbour similarity, until it has taken
walkRatio * N steps. Steadiness draws its walks in the projection, cohesiveness in
the data.
Each drawn cluster is partitioned in the opposite space by DruidJS’s seeded k-means into groups, and every pair of groups is scored by how much their shared-neighbour distance changed between the two spaces. That is where the k-means comes in — it splits a cluster into comparable pieces, it does not find the clusters. Using it rather than HDBSCAN keeps the whole thing reproducible given a seed and avoids a second clustering dependency.
Points are capped at maxPoints: 6000 by default, because the pairwise structure is
in memory.
Reading these
Section titled “Reading these”Both are , higher is better, and there is no meaningful chance floor — a
random projection scores low on both. 1.0 on both means every cluster at every scale
survives in both directions; snc(v, v) on identical spaces returns exactly 1.
The unit of analysis is a cluster pair, not a point, so snc is not a MetricResult
and has no localKind. Per-point contributions are still available: pass
local: true and the result carries localSteadiness and localCohesiveness, each
point’s share of the distortion it took part in, normalised to and inverted
so that high is good, as everywhere else.
const s = snc(hd, ld, { iterations: 150, seed: 42, local: true });s.localSteadiness[i]; // 1 means point i was never part of a distorted pairIt costs memory, which is why it is off by default.
in both time and memory, plus the k-means iterations. This is one of the four measures that should not run live in a browser.
Verification
Section titled “Verification”Checked against the reference implementation by the paper’s authors. Because both are stochastic the comparison is distributional: sickle’s value must fall within the reference’s own spread across seeds, and the ordering across the five fixtures must match exactly. It does.
References
Section titled “References”- Jeon, Ko, Jo, Yi & Seo, IEEE TVCG 28 (2022) steadiness & cohesiveness