Skip to content

A surface Laplacian

On a grid of electrodes (HD-EMG, ECoG), a Laplacian replaces each channel by itself minus the mean of its neighbours. Whatever is spread over the whole grid (a distant source, the common artefact) cancels; what is local to a few electrodes stays. Laplacian.from_grid(rows, cols) is the 5-point version; from_positions handles any layout.

import numpy as np
import dspk
from plating import signals
ROWS, COLS = 4, 4
# No LFP here (its phase differs across channels, so it is not shared): the shared parts are the
# common artefact and the hum, made large
s = signals.synthetic(channels=ROWS * COLS, lfp_uv=0.0, common_uv=150.0, hum_uv=60.0)
lap = dspk.ops.spatial.Laplacian.from_grid(ROWS, COLS)
y = lap.apply(s.data)
ch = s.loudest_channel
for name, comp in (("common artefact", "common"), ("spikes", "spikes")):
kept = np.std(lap.apply(s.components[comp])[ch]) / (np.std(s.components[comp][ch]) or 1)
print(f"{name}: {100 * kept:.0f} % of its RMS kept on channel {ch}")

A surface Laplacian

The whole script is playground/05_spatial/02_laplacian.py. Run it with uv run python playground/05_spatial/02_laplacian.py.