Source code for neurokit2.signal.signal_resample

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import scipy.ndimage
import scipy.signal


[docs] def signal_resample( signal, desired_length=None, sampling_rate=None, desired_sampling_rate=None, method="interpolation", ): """**Resample a continuous signal to a different length or sampling rate** Up- or down-sample a signal. The user can specify either a desired length for the vector, or input the original sampling rate and the desired sampling rate. Parameters ---------- signal : Union[list, np.array, pd.Series] The signal (i.e., a time series) in the form of a vector of values. desired_length : int The desired length of the signal. sampling_rate : int The original sampling frequency (in Hz, i.e., samples/second). desired_sampling_rate : int The desired (output) sampling frequency (in Hz, i.e., samples/second). method : str Can be ``"interpolation"`` (see ``scipy.ndimage.zoom()``), ``"numpy"`` for numpy's interpolation (see ``np.interp()``),``"pandas"`` for Pandas' time series resampling, ``"poly"`` (see ``scipy.signal.resample_poly()``) or ``"FFT"`` (see ``scipy.signal.resample()``) for the Fourier method. ``"FFT"`` is the most accurate (if the signal is periodic), but becomes exponentially slower as the signal length increases. In contrast, ``"interpolation"`` is the fastest, followed by ``"numpy"``, ``"poly"`` and ``"pandas"``. Returns ------- array Vector containing resampled signal values. See Also -------- signal_interpolate Examples -------- **Example 1**: Downsampling .. ipython:: python import numpy as np import pandas as pd import neurokit2 as nk signal = nk.signal_simulate(duration=1, sampling_rate=500, frequency=3) # Downsample data = {} for m in ["interpolation", "FFT", "poly", "numpy", "pandas"]: data[m] = nk.signal_resample(signal, sampling_rate=500, desired_sampling_rate=30, method=m) @savefig p_signal_resample1.png scale=100% nk.signal_plot([data[m] for m in data.keys()]) @suppress plt.close() **Example 2**: Upsampling .. ipython:: python :verbatim: signal = nk.signal_simulate(duration=1, sampling_rate=30, frequency=3) # Upsample data = {} for m in ["interpolation", "FFT", "poly", "numpy", "pandas"]: data[m] = nk.signal_resample(signal, sampling_rate=30, desired_sampling_rate=500, method=m) @savefig p_signal_resample2.png scale=100% nk.signal_plot([data[m] for m in data.keys()], labels=list(data.keys())) @suppress plt.close() **Example 3**: Benchmark .. ipython:: python :verbatim: signal = nk.signal_simulate(duration=1, sampling_rate=1000, frequency=3) # Timing benchmarks %timeit nk.signal_resample(signal, method="interpolation", sampling_rate=1000, desired_sampling_rate=500) %timeit nk.signal_resample(signal, method="FFT", sampling_rate=1000, desired_sampling_rate=500) %timeit nk.signal_resample(signal, method="poly", sampling_rate=1000, desired_sampling_rate=500) %timeit nk.signal_resample(signal, method="numpy", sampling_rate=1000, desired_sampling_rate=500) %timeit nk.signal_resample(signal, method="pandas", sampling_rate=1000, desired_sampling_rate=500) """ if desired_length is None: desired_length = int(np.round(len(signal) * desired_sampling_rate / sampling_rate)) # Sanity checks if len(signal) == desired_length: return signal # Resample if method.lower() == "fft": resampled = _resample_fft(signal, desired_length) elif method.lower() == "poly": resampled = _resample_poly(signal, desired_length) elif method.lower() == "numpy": resampled = _resample_numpy(signal, desired_length) elif method.lower() == "pandas": resampled = _resample_pandas(signal, desired_length) else: resampled = _resample_interpolation(signal, desired_length) return resampled
# ============================================================================= # Methods # ============================================================================= def _resample_numpy(signal, desired_length): resampled_signal = np.interp( np.linspace(0.0, 1.0, desired_length, endpoint=False), # where to interpolate np.linspace(0.0, 1.0, len(signal), endpoint=False), # known positions signal, # known data points ) return resampled_signal def _resample_interpolation(signal, desired_length): resampled_signal = scipy.ndimage.zoom(signal, desired_length / len(signal)) return resampled_signal def _resample_fft(signal, desired_length): resampled_signal = scipy.signal.resample(signal, desired_length) return resampled_signal def _resample_poly(signal, desired_length): resampled_signal = scipy.signal.resample_poly(signal, desired_length, len(signal)) return resampled_signal def _resample_pandas(signal, desired_length): # Convert to Time Series index = pd.date_range("20131212", freq="ms", periods=len(signal)) resampled_signal = pd.Series(signal, index=index) # Create resampling factor resampling_factor = str(np.round(1 / (desired_length / len(signal)), 6)) + "ms" # Resample resampled_signal = resampled_signal.resample(resampling_factor).bfill().values # Sanitize resampled_signal = _resample_sanitize(resampled_signal, desired_length) return resampled_signal # ============================================================================= # Internals # ============================================================================= def _resample_sanitize(resampled_signal, desired_length): # Adjust extremities diff = len(resampled_signal) - desired_length if diff < 0: resampled_signal = np.concatenate( [resampled_signal, np.full(np.abs(diff), resampled_signal[-1])] ) elif diff > 0: resampled_signal = resampled_signal[0:desired_length] return resampled_signal