spine_measure¶

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¶

from entropy_spine import spine_measure
help(spine_measure)
Help on function spine_measure in module entropy_spine.interface.measures:

spine_measure(X, name=None, *, k: 'int' = 5)
    spine_measure: turn 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.

    The ten measures, and the usual orientation of each:

        asis            your own precomputed measure                      yours
        knn             distance to the K-th nearest neighbour            upper
        frequency       count divided by the total, counts given          lower
        statefrequency  tally the distinct rows, frequency per state     lower
        degree          row sum of an adjacency matrix                    upper
        singular        the singular values of your matrix                upper
        dist2mean       distance from the mean                            upper
        dist2median     distance from the median                          upper
        zrownorm        distance from the mean, in std-deviation units    upper
        robustrownorm   distance from the median, in MAD units            upper

    'singular' and 'statefrequency' change what an object IS: for 'singular' the
    objects are the singular values, for 'statefrequency' the distinct states.
    ``meta["objectsChanged"]`` records both. The conventional orientation is
    recorded in meta, never enforced: you choose the orientation.

    Returns ``(mu, meta)``.

Seeing a measure on its own¶

Objects are rows. Every measure returns one nonnegative value per object.

import numpy as np
rng = np.random.default_rng(5)
X = np.vstack([rng.standard_normal((150, 3)),
               rng.standard_normal((12, 3)) + 8])
mu, meta = spine_measure(X, "robustrownorm")
print(f"{mu.size} objects, measure from {mu.min():.3g} to {mu.max():.3g}")
162 objects, measure from 0.0579 to 14

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 = np.column_stack([X, np.ones(X.shape[0])])   # a constant column
_, meta = spine_measure(Xc, "zrownorm")
{k: v for k, v in meta.items() if k != "states"}
{'name': 'zrownorm',
 'droppedColumns': [4],
 'parameters': {},
 'objectsChanged': False,
 'conventionalOrientation': 'upper',
 'nObjects': 162}

Measures that change what an object is¶

singular makes the objects the singular values, so the count is min(X.shape) 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 = rng.standard_normal((60, 15)) @ np.diag([20, 12, 7] + [0.3] * 12)
sv, meta_sv = spine_measure(M, "singular")
print(f"{M.shape[0]} rows in, {sv.size} objects out, "
      f"objectsChanged = {meta_sv['objectsChanged']}")
60 rows in, 15 objects out, objectsChanged = True

The one measure that accepts text¶

statefrequency compares rows literally rather than computing with them, so it takes strings, sequences of rows, and DataFrames mixing text and numbers.

states = ["ok", "ok", "warn", "ok", "fault", "warn", "ok", "fault"]
freq, meta_state = spine_measure(np.asarray(states), "statefrequency")
list(zip(meta_state["objectIds"], meta_state["counts"].tolist(),
         freq.round(3).tolist()))
[(np.str_('fault'), 2, 0.25),
 (np.str_('ok'), 4, 0.5),
 (np.str_('warn'), 2, 0.25)]

Choosing between them¶

The measure guide on the documentation site is the piece to read before applying SPINE to your own problem, and spine_compare shows what happens when you get it wrong.

See also¶

spine, spine_compare.