-
Notifications
You must be signed in to change notification settings - Fork 0
Recipes
import micromotion as mm
rec = mm.read("mocap_data/A0001.tsv")
for name in rec.markers:
xyz = mm.interpolate_gaps(rec.marker(name), max_gap=200)
xyz = xyz[~np.isnan(xyz).any(axis=1)]
if len(xyz) < rec.fs * 60:
continue
print(name, round(mm.qom(xyz, rec.fs, kind="position").mean_mm_s, 3))Drop series that are more than about 20 per cent missing rather than filling them.
y = mm.to_rate(xyz, rec.fs, mm.COMMON_RATE)
row = {
"qom_mm_s": mm.qom(y, mm.COMMON_RATE, kind="position").mean_mm_s,
"native": mm.qom(xyz, rec.fs, kind="position").mean_mm_s,
"legacy": mm.qom(xyz, rec.fs, kind="position", band="optical_legacy").mean_mm_s,
"rate_hz": rec.fs,
"missing": float(np.isnan(rec.data).mean()),
}Carry the native and legacy columns even if you do not use them. Someone will ask, and recomputing a corpus takes ten minutes.
Record the task instruction and the body placement in the same table. They matter more than any filter choice.
raw = mm.qom(acc, fs, unit="g") # everything
comp = mm.qom(acc, fs, unit="g", variant="compensated") # postural only
mm.band_power_fraction(np.linalg.norm(acc, axis=1), fs,
{"resp": (0.1, 0.5), "card": (0.7, 2.2)})The compensated variant raises the lower edge to 0.5 Hz and notches the recording's own cardiac peak. In one chest-worn dataset roughly 38 per cent of signal power sat at the heart rate.
t1, hr1 = mm.instantaneous_rate(haemoglobin, 75.0)
t2, hr2 = mm.instantaneous_rate(np.linalg.norm(chest_acc, axis=1), 100.0)
r = mm.search_lag(t1, hr1, t2, hr2, max_lag_s=300)
if r["confident"]:
t2_aligned = mm.apply_lag(t2, -r["lag_s"])Works because both instruments see the same heart, and a heart rate wanders in a way specific enough to match. A flat rate cannot be aligned.
If both recordings contain a sync clap, mm.find_transient(x, fs, search_s=20) is exact and
needs no assumptions.
rec = mm.read_balance_board(path)
cop = rec.data * np.array(mm.io.BOARD_MM) # normalised 0-1 -> mm
grid, cop = mm.regularize(rec.t, cop, fs_out=20.0, max_gap_s=2.0)
mm.sway_geometry(cop), mm.ellipse_area_95(cop)The board is irregularly sampled with duplicate and backward timestamps. regularize sorts
and cleans before interpolating, and refuses to bridge long gaps.
from micromotion import dynamics as dy
dy.dfa(speed)["alpha"] # 0.5 white, 1.0 pink, 1.5 Brownian
dy.surrogate_test(speed, dy.trev, n=99)The surrogate test asks whether the series differs from a linear Gaussian process with the same spectrum and distribution. Use at least 99 surrogates for anything reportable — 25 gives a p value that cannot go below 0.038.