Skip to content

StressMDS ​

Weighted metric MDS. It minimizes

for a weighting you choose, which turns a family of separately-named methods into one parameter.

How It Works ​

Setting weights to an exponent gives :

weightsobjectivealso known as
0raw stressthe objective SMACOF minimizes
-1Sammon stressthe objective Sammon minimizes
-2elastic scalingKamada-Kawai energy, see KKMDS

A more negative exponent concentrates the objective on short distances. Optimization is Jacobi-preconditioned gradient descent with a backtracking line search, warm-started from classical MDS on the same distances.

Why or When to Use ​

StressMDS does not replace SMACOF or Sammon. Those solve the same objectives at 0 and -1 with their own historical algorithms and reach different local minima, so swapping them out would silently change existing layouts. Reach for StressMDS when you want something they cannot express:

  • exponents between or beyond the three named ones, as a continuous local↔global dial;
  • explicit weight matrices, where a weight of zero drops the pair from the objective — the standard way to handle missing or untrusted dissimilarities, which nothing else here offers;
  • fewer iterations: on a benchmark suite it reached lower Sammon stress in 52 iterations than Sammon's own optimizer reached in 200.

Example ​

The same Iris data at three weightings. The difference is subtle on data this small and clean, which is itself worth knowing.

weights: 0
weights: -1
weights: -2

How-to (Code) ​

javascript
import * as druid from "@saehrimnir/druidjs";

const data = [
  /* ... multi-dimensional data ... */
];

// An exponent
const projection = new druid.StressMDS(data, { weights: -1 }).transform();

// Or the named constants
const elastic = new druid.StressMDS(data, { weights: druid.WEIGHTS_ELASTIC }).transform();

// Or a function of the target distance
const custom = new druid.StressMDS(data, { weights: (d) => 1 / (1 + d) }).transform();

Missing data — a zero weight removes the pair from the objective entirely:

javascript
const W = new druid.Matrix(N, N, (i, j) => (wasObserved(i, j) ? 1 : 0));
const projection = new druid.StressMDS(D, { metric: "precomputed", weights: W }).transform();

Parameters ​

ParameterDefaultMeaning
weights-2Exponent, matrix, or function. Zero weights drop the pair.
init_DR"MDS"Starting configuration. The objective is non-convex, and classical MDS on the same distances keeps the descent out of poor local minima.
learning_rate0.1Dimensionless — the gradient is preconditioned by weighted degree, so this needs no rescaling for the data or the weighting.
iterations300Maximum gradient steps.
epsilon1e-6Stop once the relative stress improvement falls below this.

What the exponent actually buys ​

Worth stating plainly, because the obvious guess is wrong. Holding the solver, initialization and budget fixed and varying only the exponent, over 5 datasets × 5 seeds:

weights10-NN preserveddistance correlation
056.3%0.8382
-157.9%0.8309
-259.1%0.8144
-357.9%0.7962

Global fidelity falls monotonically as the exponent drops — that part is reliable. Local neighborhood preservation, however, peaks near -2 rather than improving without limit, and on data with no manifold or cluster structure it moves the other way throughout (an isotropic 30-dimensional Gaussian ran 19.6% at 0 down to 13.1% at -3). So -2 is a reasonable default rather than a maximum, and on structureless data 0 is the better choice.

See also the StressMDS API reference.