Skip to content

One feature vector per recording

Every attempt to compare recordings needs the same thing first, which is a fixed set of numbers describing one recording. Clustering, identity classification, condition classification and dimensionality reduction all start there, and each of them will happily invent its own set.

That is what this solves. Two analyses that reduce a recording differently are incomparable for reasons that have nothing to do with the question either one is asking.

import micromotion as mm

f = mm.feature_vector(xyz, fs, kind="position", unit="mm")
mm.FEATURE_NAMES        # the column order

The function returns None if the recording is shorter than two minutes, or if it contains any non-finite sample, rather than a vector computed from too little data.

The eleven

group descriptors what they answer
amount and smoothness qom, jerk how much, and how abruptly
frequency and texture centroid, f50, frozen, burst how fast, and how evenly spread in time
sway geometry path, extent, area, anis, vert how far it travels, how large a region, how elongated

The five geometric descriptors need true position and are nan for accelerometer collections. A chest-worn accelerometer cannot give a sway ellipse, and double-integrating one to fake it reports drift as posture.

Two arguments that must be passed

kind and unit are required and are not guessed, because collections do not all record the same quantity. Optical systems store position in millimetres, while accelerometers store acceleration in g or m/s². Differentiating an acceleration series as though it were position shifts every descriptor two derivatives up and still returns finite, plausible-looking numbers. That failure is silent, which is why the signature refuses to default and raises instead.

The rate that matters is the sensor's

mm.feature_vector(xyz, fs=100.0, kind="position", unit="mm", sensor_fs=15.0)   # jerk is nan, correctly

A uniform grid can be an upsample of a much slower sensor. A file stored at 100 Hz whose sensor updated at 15 Hz cannot carry a 10 Hz band, and no filter can see the difference, since the grid looks fast enough. Pass sensor_fs wherever the two differ, and jerk will be nan rather than a number computed from interpolation.

Jerk is computed at a different band, deliberately

Ten of the eleven use the canonical band, but jerk uses WIDEBAND, because it lives in the octave that the canonical band gives up. At a 5 Hz ceiling it falls to between a third and two thirds of its value, and the ranking of recordings shifts. One definition of jerk across a corpus is worth more than one band within a single table. Where the sampling rate cannot deliver WIDEBAND, jerk is nan rather than a narrower measure reported under a wider name.

The set is a commitment

Fixing eleven descriptors and their names is the point, since two analyses that reduce a recording differently cannot be compared. It also means the set is something other people's results depend on, so adding or removing one is a breaking change in practice.

What belongs here and what does not

This is a measurement library. A classifier belongs in an analysis repository, where a reader can see its train/test split and its leakage. What belongs here is the input that every such model starts from, so that two attempts at the same question begin from the same numbers.