Source code for elephant.waveform_features
# -*- coding: utf-8 -*-
"""
.. autosummary::
:toctree: _toctree/waveform_features
waveform_width
waveform_snr
:copyright: Copyright 2014-2024 by the Elephant team, see `doc/authors.rst`.
:license: Modified BSD, see LICENSE.txt for details.
"""
from __future__ import division, print_function, unicode_literals
import warnings
import neo
import numpy as np
__all__ = [
"waveform_width",
"waveform_snr"
]
[docs]
def waveform_width(waveform, cutoff=0.75):
"""
Calculate the width (trough-to-peak TTP) of a waveform.
Searches for an index of a minimum within first `cutoff` of the waveform
vector, next for a maximum after the identified minimum, and returns the
difference between them.
Parameters
----------
waveform : array-like
Time course of a single waveform. Accepts a list, a numpy array or a
quantity.
cutoff : float, optional
Defines the normalized range `[0, cutoff]` of the input sequence for
computing the minimum. Must be in `[0, 1)` range.
Default: 0.75
Returns
-------
width : int
Width of a waveform expressed as a number of data points
Raises
------
ValueError
If `waveform` is not a one-dimensional vector with at least two
numbers.
If `cutoff` is not in `[0, 1)` range.
Examples
--------
>>> from elephant.waveform_features import waveform_width
>>> waveform_width([20, 25, 10, -5, -2, 7, 15], cutoff=0.75)
3
"""
waveform = np.squeeze(waveform)
if np.ndim(waveform) != 1:
raise ValueError('Expected 1-dimensional waveform.')
if len(waveform) < 2:
raise ValueError('Too short waveform.')
if not (0 <= cutoff < 1):
raise ValueError('Cuttoff must be in range [0, 1).')
min_border = max(1, int(len(waveform) * cutoff))
idx_min = np.argmin(waveform[:min_border])
idx_max = np.argmax(waveform[idx_min:]) + idx_min
width = idx_max - idx_min
return width
[docs]
def waveform_snr(waveforms):
"""
Return the signal-to-noise ratio of the waveforms of one or more
spike trains :cite:`waveforms-Hatsopoulos2007_5105`.
Signal-to-noise ratio is defined as the difference in mean peak-to-trough
voltage divided by twice the mean SD. The mean SD is computed by
measuring the SD of the spike waveform over all acquired spikes
at each of the sample time points of the waveform and then averaging.
Parameters
----------
waveforms : array-like
A list or a quantity or a numpy array of waveforms of shape
``(n_waveforms, time)`` in case of a single spike train or
``(n_waveforms, n_spiketrains, time)`` in case of one or more spike
trains.
Returns
-------
snr : float or np.ndarray
Signal-to-noise ratio according to
:cite:`waveforms-Hatsopoulos2007_5105`. If the input `waveforms`
shape is ``(n_waveforms, time)`` or ``(n_waveforms, 1, time)``, a
single float is returned. Otherwise, if the shape is
``(n_waveforms, n_spiketrains, time)``, a numpy array of length
``n_spiketrains`` is returned.
Notes
-----
The waveforms of a `neo.SpikeTrain` can be extracted as
`spiketrain.waveforms`, if it's loaded from a file, in which case you need
to set ``load_waveforms=True`` in ``neo.read_block()``.
Examples
--------
>>> from elephant.waveform_features import waveform_snr
>>> waveforms = [[20, 25, 10, -5, -2, 7, 15], [17, 29, 11, -4, 0, 5, 20]]
>>> waveform_snr(waveforms)
12.249999999999998
"""
if isinstance(waveforms, neo.SpikeTrain):
warnings.warn("spiketrain input is deprecated; pass "
"'spiketrain.waveforms' directly.", DeprecationWarning)
waveforms = waveforms.waveforms
# asarray removes quantities, if present
waveforms = np.squeeze(np.asarray(waveforms))
# average over all waveforms for each bin
mean_waveform = waveforms.mean(axis=0)
# standard deviation over all waveforms over all bins
std_waveform = waveforms.std(axis=0).mean(axis=-1)
# peak to trough voltage signal
peak_range = mean_waveform.max(axis=-1) - mean_waveform.min(axis=-1)
# noise
noise = 2 * std_waveform
snr = peak_range / noise
if np.isnan(snr).any():
warnings.warn('The waveforms noise was evaluated to 0. Returning NaN')
return snr