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.
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.