dspk.synapse.detection
Spike detection: threshold crossings, Teager–Kaiser energy (NEO) and matched filtering on data
in memory, spatial de-duplication across neighbouring channels, and the streaming detector over a
whole recording (detect_recording, as dspk detect).
Detections are dicts of arrays sorted by time: samples (sample index), channels,
amplitudes (µV at the extremum).
deduplicate |
One spike per event seen on several neighbouring channels. |
detect_recording |
Detects spikes over a whole recording, as dspk detect does. |
heights |
Detection height of every channel. |
matched_filter |
Matched-filter detection. |
neo |
Peaks of the Teager–Kaiser energy (NEO) beyond threshold * sigma(energy) on each channel. |
prototype |
A biphasic spike shape. |
threshold |
Threshold crossings. |
Functions
Section titled “Functions”deduplicate
Section titled “deduplicate”deduplicate( spikes: dict, probe: Probe, sample_rate: float, *, radius_um: float = DEFAULT_RADIUS_UM, window_ms: float = 0.5,) -> dictOne spike per event seen on several neighbouring channels: the largest stays.
Parameters
spikes:dict of numpy.ndarray
samples,channelsandamplitudes, as the detectors return.probe:Probe
The probe, for distances between channels.sample_rate:float
Sample rate in Hz.radius_um:float, defaultDEFAULT_RADIUS_UM(150.0)
Spikes on channels closer than this…window_ms:float, default0.5
…and closer in time than this are one event.
Returns
dict of np.ndarray— The spikes kept, plusparticipating: the channels each event was seen on.
detect_recording
Section titled “detect_recording”detect_recording( recording, probe: Probe, *, band: tuple[float, float] = DEFAULT_BAND, car: bool = False, threshold: float = DEFAULT_RECORDING_THRESHOLD, refractory_ms: float = DEFAULT_REFRACTORY_MS, polarity: str = 'negative', spacing: str = 'locally_exclusive', radius_um: float = DEFAULT_RADIUS_UM, k_neighbors: int = DEFAULT_K_NEIGHBORS, pre_ms: float = DEFAULT_PRE_MS, post_ms: float = DEFAULT_POST_MS, sinc_shift: bool = True, locate: bool = True, batch_s: float = DEFAULT_BATCH_S, calibration_s: float = DEFAULT_CALIBRATION_S, calibration_chunks: int = DEFAULT_CALIBRATION_CHUNKS, runtime: str | None = None,) -> tuple[Sorting, dict]Detects spikes over a whole recording, as dspk detect does.
The recording is read in place in batch_s batches: optional CAR, zero-phase band-pass,
noise calibrated on chunks spread over the recording, threshold crossings, spatial
de-duplication, snippets and per-channel templates.
Parameters
recording:dspk.io.Recording
The recording.probe:Probe
Its probe.band:float, float, defaultDEFAULT_BAND
Band-pass(low, high)in Hz.car:bool, defaultFalse
Subtract the common average first.threshold:float, defaultDEFAULT_RECORDING_THRESHOLD(5.0)
Height in units of each channel’s noise.refractory_ms:float, defaultDEFAULT_REFRACTORY_MS(1.0)
Spikes on one channel stay more than this apart.polarity:{"negative", "positive", "both"}, default'negative'
Which extrema count.spacing:{"locally_exclusive", "scipy"}, default'locally_exclusive'
Which of two nearer spikes stays (seethreshold).radius_um:float, defaultDEFAULT_RADIUS_UM(150.0)
De-duplication radius.k_neighbors:int, defaultDEFAULT_K_NEIGHBORS(4)
Channels per snippet (the spike’s nearest).pre_ms:float, defaultDEFAULT_PRE_MS(1.0)
Snippet span around each spike.post_ms:float, defaultDEFAULT_POST_MS(2.0)
Snippet span around each spike.sinc_shift:bool, defaultTrue
Realign each snippet to its sub-sample trough.locate:bool, defaultTrue
Locate every spike: the centre of mass of its snippet’s heights over itsk_neighbors.batch_s:float, defaultDEFAULT_BATCH_S(10.0)
Seconds read and processed at a time.calibration_s:float, defaultDEFAULT_CALIBRATION_S(5.0)
Seconds of signal the noise is measured on.calibration_chunks:int, defaultDEFAULT_CALIBRATION_CHUNKS(5)
Pieces those seconds are spread over.runtime:str, defaultNone
A runtime fromruntime.available; by default the current one.
Returns
Sorting— One unit per channel;spikes()["locations"]holds the locations (NaN withoutlocate).dict—sigmas_uv(noise per channel),raw_crossings,spikes,channel_counts.
Raises
ValueError: For an invalid option.OSError: When the recording cannot be read.RuntimeError: When the runtime has no device.
Examples
>>> import dspk>>> rec = dspk.io.open("session.bin")>>> probe = dspk.synapse.io.Probe.preset("neuropixels1")>>> sorting, summary = dspk.synapse.detection.detect_recording(rec, probe)heights
Section titled “heights”heights( sigmas, threshold: float = DEFAULT_THRESHOLD,) -> np.ndarrayDetection height of every channel: threshold * sigma.
Parameters
sigmas:array_like
Each channel’s noise(channels,).threshold:float, defaultDEFAULT_THRESHOLD(4.5)
Height in units of noise.
Returns
np.ndarray—(channels,);infwheresigmais not positive (the channel never detects).
matched_filter
Section titled “matched_filter”matched_filter( data, sample_rate: float, *, prototype_ms: float = 1.0, prototype_=None, threshold: float = DEFAULT_THRESHOLD, refractory_ms: float = DEFAULT_REFRACTORY_MS, spacing: str = 'locally_exclusive',) -> dictMatched-filter detection: correlation with a spike shape, peaks beyond threshold * sigma.
Only peaks where the signal is negative count.
Parameters
data:array_like
Signal(channels, samples)in µV, converted to float32 on the host.sample_rate:float
Sample rate in Hz.prototype_ms:float, default1.0
Length of the default shape (prototype), in ms.prototype_:array_like, defaultNone
Your own shape, instead of the default.threshold:float, defaultDEFAULT_THRESHOLD(4.5)
Height in units of each channel’s correlation noise.refractory_ms:float, defaultDEFAULT_REFRACTORY_MS(1.0)
Spikes on one channel stay more than this apart.spacing:{"locally_exclusive", "scipy"}, default'locally_exclusive'
Which of two nearer spikes stays."locally_exclusive": a spike stays unless a larger one is nearer, which chunked detection reproduces exactly."scipy": asscipy.signal.find_peakswithdistance.
Returns
dict of np.ndarray—samples(sample index),channelsandamplitudes(µV at the extremum), sorted by time.
neo( data, sample_rate: float, *, threshold: float = DEFAULT_NEO_THRESHOLD, refractory_ms: float = DEFAULT_REFRACTORY_MS, spacing: str = 'locally_exclusive',) -> dictPeaks of the Teager–Kaiser energy (NEO) beyond threshold * sigma(energy) on each channel.
The energy is large where the signal is both large and fast, so it favours spikes over slow waves.
Parameters
data:array_like
Signal(channels, samples)in µV, converted to float32 on the host.sample_rate:float
Sample rate in Hz.threshold:float, defaultDEFAULT_NEO_THRESHOLD(8.0)
Height in units of each channel’s energy noise.refractory_ms:float, defaultDEFAULT_REFRACTORY_MS(1.0)
Spikes on one channel stay more than this apart.spacing:{"locally_exclusive", "scipy"}, default'locally_exclusive'
Which of two nearer spikes stays."locally_exclusive": a spike stays unless a larger one is nearer, which chunked detection reproduces exactly."scipy": asscipy.signal.find_peakswithdistance.
Returns
dict of np.ndarray—samples(sample index),channelsandamplitudes(µV at the extremum), sorted by time.
prototype
Section titled “prototype”prototype(samples: int) -> np.ndarrayA biphasic spike shape: zero mean, unit norm.
Parameters
samples:int
Length.
Returns
np.ndarray—(samples,), float32.
threshold
Section titled “threshold”threshold( data, sample_rate: float, *, threshold: float = DEFAULT_THRESHOLD, polarity: str = 'negative', refractory_ms: float = DEFAULT_REFRACTORY_MS, sigmas=None, spacing: str = 'locally_exclusive',) -> dictThreshold crossings: local extrema beyond threshold * sigma on each channel.
Parameters
data:array_like
Signal(channels, samples)in µV, converted to float32 on the host.sample_rate:float
Sample rate in Hz.threshold:float, defaultDEFAULT_THRESHOLD(4.5)
Height in units of each channel’s noise.polarity:{"negative", "positive", "both"}, default'negative'
Which extrema count.refractory_ms:float, defaultDEFAULT_REFRACTORY_MS(1.0)
Spikes on one channel stay more than this apart.sigmas:array_like, defaultNone
Each channel’s noise(channels,); by default estimated asmedian(|x|) / 0.6745(ops.math.channel_noise_std).spacing:{"locally_exclusive", "scipy"}, default'locally_exclusive'
Which of two nearer spikes stays."locally_exclusive": a spike stays unless a larger one is nearer, which chunked detection reproduces exactly."scipy": asscipy.signal.find_peakswithdistance.
Returns
dict of np.ndarray—samples(sample index),channelsandamplitudes(µV at the extremum), sorted by time.
Raises
ValueError: For an unknownpolarityorspacing, orsigmasof another length.
Examples
>>> import numpy as np>>> import dspk>>> x = np.random.default_rng(0).standard_normal((4, 30_000)).astype(np.float32) * 10>>> x[1, 15_000:15_010] -= 120 # one spike>>> spikes = dspk.synapse.detection.threshold(x, 30_000.0)>>> int(spikes["channels"][0]), int(spikes["samples"][0]) # the spike's most negative sample(1, 15004)