curvilinearStress
function curvilinearStress(f): MetricResult & { raw: number;};Defined in: metrics/embedding.ts:100
Stress that forgives tearing but not folding.
The weighting depends on the projected distance, so errors between points that end up close together are punished and errors between points that end up far apart are excused. That asymmetry matches how a viewer reads a scatterplot: things drawn together look related, things drawn apart simply look unrelated.
- Needs: high-dimensional data and projection. No labels.
- Range: [0, ∞), lower is better; 0 is exact.
- Cost: O(1), from an O(N²·D) pass, which must be given
ccaLambda.
The returned value is normalised: Σ(d − d̂)²·F(d̂) ÷ Σd²·F(d̂), where d is
the high-dimensional distance, d̂ the projected one and F the weighting
kernel. raw is the bare numerator Σ(d − d̂)²·F(d̂), the quantity as
originally published.
The pass options control the kernel: ccaLambda is the neighbourhood width the
weighting decays over, and ccaKernel picks its shape — "exponential"
(default) uses F(d̂) = exp(−d̂/λ), "step" uses F(d̂) = 1 for d̂ ≤ λ and 0
beyond. A ccaLambda that is missing or not strictly positive leaves the CCA
accumulators unfilled, and this read-out then throws.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
f | EmbeddingMoments |
Returns
Section titled “Returns”MetricResult & {
raw: number;
}
Throws
Section titled “Throws”if the pass ran without a positive ccaLambda.
Demartines & Hérault, IEEE Trans. Neural Networks 8 (1997) https://doi.org/10.1109/72.554199
Example
Section titled “Example”import { analyze, curvilinearStress } from "@saehrimnir/sickle";
// ccaLambda is required here, not on the read-out; ccaKernel defaults to// "exponential". Without a positive ccaLambda, curvilinearStress throws.const a = analyze(data, projection, { ccaLambda: 0.3, ccaKernel: "exponential" });const s = curvilinearStress(a.embedding);s.value; // 0.622 for a noisy 50-point circle cut open into a lines.raw; // 847.19 — the unnormalised sum