Source code for neurokit2.signal.signal_phase

# -*- coding: utf-8 -*-
import itertools

import numpy as np
import scipy.signal


[docs] def signal_phase(signal, method="radians"): """**Compute the phase of the signal** The real phase has the property to rotate uniformly, leading to a uniform distribution density. The prophase typically doesn't fulfill this property. The following functions applies a nonlinear transformation to the phase signal that makes its distribution exactly uniform. If a binary vector is provided (containing 2 unique values), the function will compute the phase of completion of each phase as denoted by each value. Parameters ---------- signal : Union[list, np.array, pd.Series] The signal (i.e., a time series) in the form of a vector of values. method : str The values in which the phase is expressed. Can be ``"radians"`` (default), ``"degrees"`` (for values between 0 and 360) or ``"percents"`` (for values between 0 and 1). See Also -------- signal_filter, signal_zerocrossings, signal_findpeaks Returns ------- array A vector containing the phase of the signal, between 0 and 2*pi. Examples -------- .. ipython:: python import neurokit2 as nk signal = nk.signal_simulate(duration=10) phase = nk.signal_phase(signal) @savefig p_signal_phase1.png scale=100% nk.signal_plot([signal, phase]) @suppress plt.close() ..ipython:: python rsp = nk.rsp_simulate(duration=30) phase = nk.signal_phase(rsp, method="degrees") @savefig p_signal_phase2.png scale=100% nk.signal_plot([rsp, phase]) @suppress plt.close() .. ipython:: python # Percentage of completion of two phases signal = nk.signal_binarize(nk.signal_simulate(duration=10)) phase = nk.signal_phase(signal, method="percents") @savefig p_signal_phase3.png scale=100% nk.signal_plot([signal, phase]) """ # If binary signal if len(set(np.array(signal)[~np.isnan(np.array(signal))])) == 2: phase = _signal_phase_binary(signal) else: phase = _signal_phase_prophase(signal) if method.lower() in ["degree", "degrees"]: phase = np.rad2deg(phase) if method.lower() in ["perc", "percent", "percents", "percentage"]: phase = np.rad2deg(phase) / 360 return phase
# ============================================================================= # Method # ============================================================================= def _signal_phase_binary(signal): phase = itertools.chain.from_iterable(np.linspace(0, 1, sum([1 for i in v])) for _, v in itertools.groupby(signal)) phase = np.array(list(phase)) # Convert to radiant phase = np.deg2rad(phase * 360) return phase def _signal_phase_prophase(signal): pi2 = 2.0 * np.pi # Get pro-phase prophase = np.mod(np.angle(scipy.signal.hilbert(signal)), pi2) # Transform a pro-phase to a real phase sort_idx = np.argsort(prophase) # Get a sorting index reverse_idx = np.argsort(sort_idx) # Get index reversing sorting tht = pi2 * np.arange(prophase.size) / (prophase.size) # Set up sorted real phase phase = tht[reverse_idx] # Reverse the sorting of it return phase