spineMeasure

Turn your data into a nonnegative measure.
The measure is a scientific input, not a setting. SPINE does not know what your data means; choosing the measure is the modelling step. There is no default and there never should be.
You rarely call this directly: spine calls it for you. It is here so you can see what a measure produces, and build one yourself.

Syntax and the ten measures

help spineMeasure
SPINE Measure Turn user data into a nonnegative measure for SPINE. [mu, Meta] = spineMeasure(X, name) [mu, Meta] = spineMeasure(X, name, 'K', 5) X is a real numeric matrix whose ROWS are objects and whose columns are features. Every measure returns a nonnegative column vector holding one value per object. The measure is a scientific input, not a setting. SPINE does not know what your data means; choosing the measure is the modelling step. There is no default and there never should be. Measures tied to a specific quantity 'asis' X is already the measure. X must be a nonnegative column. 'knn' Distance to the K-th nearest neighbour. upper 'frequency' Count divided by the total, counts given. lower 'statefrequency' Tally the distinct ROWS of X and give each lower state its relative frequency. 'degree' Row sum of an adjacency matrix. upper 'singular' Singular values of X. upper 'statefrequency' is the one measure that accepts text: X may be numeric, string, categorical, or a cell array of character vectors, and columns may be mixed when it arrives through a table or struct array. Each row is one observed state, matched exactly, and the objects become the DISTINCT states. Meta.stateOfRow maps each input row to its state, Meta.counts holds the tallies, and Meta.states holds the states themselves. Use 'frequency' when you already hold one count per category, and 'statefrequency' when you hold raw observations that still need tallying. General-purpose distance-from-centre measures, for data with no domain-specific quantity yet. Centre x scaling, in four combinations: 'dist2mean' || x_i - mean || upper 'dist2median' || x_i - median || upper 'zrownorm' || x_i - mean || in standard-deviation units upper 'robustrownorm' || x_i - median || in MAD units upper 'singular' and 'statefrequency' change what an object IS. For 'singular' the objects are the singular values, so the number of objects is min(size(X)) and not the number of rows; for 'statefrequency' they are the distinct states. Meta.objectsChanged records this. The conventional orientation above is recorded in Meta, never enforced. You choose the orientation. Uses base MATLAB only. zscore, mad, and pdist all live in the Statistics Toolbox, so the standardization and the pairwise distances are computed directly here to keep this file toolbox-free. Author Abd AlRahman AlMomani Assistant Professor of Data Science and Mathematics, ERAU, AZ almomaniar@gmail.com

Seeing a measure on its own

Objects are rows. Every measure returns one nonnegative value per object.
rng(5);
X = [randn(150,3); randn(12,3) + 8];
mu = spineMeasure(X, 'robustrownorm');
fprintf('%d objects, measure from %.3g to %.3g\n', numel(mu), min(mu), max(mu));
162 objects, measure from 0.377 to 13.9

What the measure records about itself

The second output says what was done: the name, any parameters, columns dropped for having no spread, and whether the measure redefined what an object is.
Xc = [X, ones(size(X,1),1)]; % a constant column
[~, Meta] = spineMeasure(Xc, 'zrownorm');
disp(Meta)
name: "zrownorm" droppedColumns: 4 parameters: [1×1 struct] objectsChanged: 0 conventionalOrientation: "upper" nObjects: 162

Measures that change what an object is

singular makes the objects the singular values, so the count is min(size(X)) rather than the number of rows. statefrequency does the same in its own way: the objects become the distinct states, however many rows produced them, and Meta.objectsChanged records both. frequency and degree keep one object per row and only reinterpret it, as a category or a node.
M = randn(60,15) * diag([20 12 7 0.3*ones(1,12)]);
[sv, MetaSv] = spineMeasure(M, 'singular');
fprintf('%d rows in, %d objects out, objectsChanged = %d\n', ...
size(M,1), numel(sv), MetaSv.objectsChanged);
60 rows in, 15 objects out, objectsChanged = 1

The one measure that accepts text

statefrequency compares rows literally rather than computing with them, so it takes string arrays, categorical arrays, cell arrays of character vectors, and tables or struct arrays mixing text and numbers.
states = ["ok";"ok";"warn";"ok";"fault";"warn";"ok";"fault"];
[freq, MetaState] = spineMeasure(states, 'statefrequency');
disp([MetaState.objectIds, string(MetaState.counts), string(freq)])
"fault" "2" "0.25" "ok" "4" "0.5" "warn" "2" "0.25"

Choosing between them

docs/measure-guide.html is the piece to read before applying SPINE to your own problem, and spineCompare shows what happens when you get it wrong.

See also

spine, spineCompare.
close all