Skip to content

Choosing a measure

Choosing is the hard part. Calling is one line.

There is no “overall quality” of a projection, and any library that offers one is averaging over questions you did not ask. Start from what you are going to claim about the picture.

“These points are close together, so they’re similar.” You are claiming the drawn neighbourhoods are real. Use trustworthiness — it penalises exactly the error that would make this claim false, points drawn together that were not.

“There’s no group here, so there wasn’t one.” The mirror claim, and a different measure: continuity penalises structure the projection hides rather than invents.

“This gap is twice as big as that one.” You are reading distances, not neighbourhoods. Use stress or, if you only mean the ordering of the gaps, spearmanRho — the difference matters more than it sounds, and there is a case where they disagree completely.

“This cluster is denser than that one.” Neighbourhood measures will not catch a projection that flattens the contrast. Use densityPreservation.

“These classes are separable.” Careful: most separability measures see only the projection, so a layout that invents clean groups scores well on all of them. Use them, but read the case where they all agree and are all wrong. gabrielClassificationError is the only one here that also looks at the data.

“This is one connected structure / this loop is real.” Topology. topologicalH0 for merge structure, topologicalH1 for loops — the only measure here that notices a circle has been cut open.

“I want one number to compare twenty projections.” aucLogRnx. It integrates R_NX over every neighbourhood size on a log scale, so it does not depend on a k you chose arbitrarily. It is still only the rank-based view.

Two, usually three, from different families — they are cheap once the pass has run, and they fail in different directions:

  1. One rank-based: trustworthiness and continuity, or aucLogRnx alone.
  2. One distance-based: scaleNormalizedStress, or spearmanRho if only ordering matters.
  3. One that matches your specific claim: density, topology, or separability.
you haveyou can use
the projection onlyscagnostics — and nothing else here, because with no original data there is nothing to be faithful to
the data and the projectioneverything except the label measures
data, projection and class labelseverything

Nine of the ten label measures never look at the data. That is not a flaw — they answer “does this picture look separable”, which is a real question — but it is not the same question as “is this picture honest”, and they are routinely used as though it were.

There is a way to get the second question out of them: compute the same measure on the data with the same labels, and compare. Every separability measure except gabrielClassificationError takes points of any dimension, so nothing stops you pointing one at the high-dimensional side:

import { clusters, silhouette } from "@saehrimnir/sickle";
const inData = silhouette(hd, clusters(hd, labels)).value;
const drawn = silhouette(ld, clusters(ld, labels)).value;

On the false separation case, where the labels are noise in the data and the layout draws them 5 units apart, that comparison is decisive — the data-side numbers sit exactly at chance while the drawn ones are perfect:

measureon the dataon the drawing
silhouette0.0000.902
distanceConsistency0.5071.000
neighborhoodHit0.4881.000
classificationError0.4790.000

Two things to be careful about. A gap is normal. On an honest PCA projection of well-separated blobs the same comparison gives silhouette 0.755 in 8-D against 0.884 in 2-D — projecting away noise dimensions genuinely tightens clusters, and distances concentrate in high dimensions, so the drawn figure starts with an advantage. It is the size of the gap that carries information, not its sign.

Only the bounded measures survive the trip. calinskiHarabasz, dunnIndex, averageBetweenWithin and hypothesisMargin have no upper limit and their scale depends on the dimensionality and spread of whatever space they were computed in — Calinski–Harabasz reads 1 144 in the data and 4 687 in the drawing for the good projection above. Differences between spaces are not interpretable for those four. Stick to the four in the table, which are bounded and have a known chance level.

This is a useful check, not a replacement for gabrielClassificationError. It compares two summaries and so catches wholesale invented separation; it is much weaker on a few individual points put in the wrong place, where the summaries barely move.

Generated from the library’s own TSDoc, so it cannot drift from the code.

33 of 33
measureanswersdatalabelsrangecost
Neighbourhood
aucLogRnxOne number summarising neighbourhood preservation at every scale.·[0, 1] betterO(N)
continuityAre the data's neighbours still together in the projection?·[0, 1] betterO(1)
lcmcNeighbourhood overlap with the chance level subtracted.·(-1, 1] betterO(1)
mrreFalseRank error among the neighbours the projection shows.·[0, 1] betterO(k)
mrreMissingRank error among the neighbours the data actually has.·[0, 1] betterO(k)
qnxFraction of each point's k nearest neighbours that survive the projection.·[0, 1] betterO(1)
rnxNeighbourhood preservation rescaled so chance is 0 and perfect is 1.·[0, 1] in practice betterO(1)
trustworthinessAre the neighbours you see in the projection real?·[0, 1] betterO(1)
Distance
nonMetricStressStress measured against the best monotone fit rather than the raw distances.·[0, 1] betterO(N² log N)
pearsonRLinear correlation between the original and projected distances.·[-1, 1] betterO(1)
residualVarianceThe share of distance variance the projection fails to explain.·[0, 1] betterO(1)
scaleNormalizedStressStress after rescaling the projection to fit as well as it can.·[0, 1] betterO(1)
spearmanRhoRank correlation between original and projected distances — Shepard goodness.·[-1, 1] betterO(N² log N)
stressHow far the projected distances are from the originals.·[0, ∞) betterO(1)
Embedding cost
curvilinearStressStress that forgives tearing but not folding.·[0, ∞) betterO(1)
nervNeighbourhood preservation framed as an information-retrieval trade-off.·[0, ∞) betterO(N²·D)
sammonStressStress that weights short distances most heavily.·[0, ∞) betterO(1)
Class separability
averageBetweenWithinMean distance between classes divided by mean distance within them.·[0, ∞) betterO(N²·D)
calinskiHarabaszRatio of between-class to within-class scatter.·[0, ∞) betterO(N·D)
classificationErrorShare of points whose label is not the majority among their visual neighbours.·[0, 1] betterO(N²·D)
daviesBouldinAverage worst-case overlap between each class and its closest rival.·[0, ∞) betterO(N·D + k²)
distanceConsistencyFraction of points lying nearest their own class centroid.·[0, 1] betterO(N·k·D)
dunnIndexSmallest centroid-to-centroid gap divided by the widest class.·[0, ∞) betterO(N²·D)
gabrielClassificationErrorClass disagreements between visually adjacent points, weighted by how close they really are.[0, ∞) betterO(N log N)
hypothesisMarginAverage gap between each point's nearest same-class and nearest other-class neighbour.·(-∞, ∞) betterO(N²·D)
neighborhoodHitShare of each point's visual neighbours that carry its own label.·[0, 1] betterO(N²·D)
silhouetteHow much closer each point is to its own class than to the nearest other class.·[-1, 1] betterO(N²·D)
Structure
densityPreservationWhether dense regions stayed dense and sparse ones sparse.·[-1, 1] betterO(N)
tripletAccuracyShare of point triples whose relative ordering survives the projection.·[0, 1] betterO(1)
Topology
topologicalH0Do the data and the projection merge into connected pieces at the same scales?·[0, ∞) betterO(N²·D)
topologicalH1Do the projection's loops match the data's?·[0, ∞) betterO(N³)
Cluster reliability
sncDoes the projection show the right groups?·both [0, 1] betterO(N²)
Scagnostics
scagnosticsNine measures of what the scatterplot looks like.··each in [0, 1] O(N log N)

higher is better · lower is better · hollow: unbounded, so the number is comparable only between projections of the same data