Skip to content

Common average and common median

Whatever every channel picks up alike (mains hum, movement, the reference electrode’s own activity) can be estimated from the channels themselves and subtracted, at every sample: CAR subtracts the mean over channels, CMR the median. They agree on clean data; they part when one channel goes bad: the mean drags that channel’s artefact onto all the others, the median ignores it.

import numpy as np
import dspk
from plating import signals
s = signals.synthetic(channels=16)
data = s.data.copy()
# One bad channel: a large, brief artefact on channel 3 (a loose contact)
BAD, AT, WIDTH, SIZE = 3, int(0.25 * s.sample_rate), int(0.02 * s.sample_rate), 2_000.0
data[BAD, AT : AT + WIDTH] += SIZE * np.hanning(WIDTH)
car = dspk.ops.spatial.car(data)
cmr = dspk.ops.spatial.cmr(data)
# What the bad channel did to a good one: referenced with it minus referenced without it
good = 0
for name, op, out in (("CAR", dspk.ops.spatial.car, car), ("CMR", dspk.ops.spatial.cmr, cmr)):
leak = np.max(np.abs(out[good] - op(s.data)[good]))
print(f"{name}: the bad channel's artefact reaches channel {good} with {leak:.0f} µV (it is {SIZE:,.0f} µV)")

Common average and common median

The whole script is playground/03_referencing/01_car_and_cmr.py. Run it with uv run python playground/03_referencing/01_car_and_cmr.py.