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