Source code for neurokit2.signal.signal_synchrony

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


[docs] def signal_synchrony(signal1, signal2, method="hilbert", window_size=50): """**Synchrony (coupling) between two signals** Signal coherence refers to the strength of the mutual relationship (i.e., the amount of shared information) between two signals. Synchrony is coherence "in phase" (two waveforms are "in phase" when the peaks and troughs occur at the same time). Synchrony will always be coherent, but coherence need not always be synchronous. This function computes a continuous index of coupling between two signals either using the ``"hilbert"`` method to get the instantaneous phase synchrony, or using a rolling window correlation. The instantaneous phase synchrony measures the phase similarities between signals at each timepoint. The phase refers to the angle of the signal, calculated through the hilbert transform, when it is resonating between -pi to pi degrees. When two signals line up in phase their angular difference becomes zero. For less clean signals, windowed correlations are widely used because of their simplicity, and can be a good a robust approximation of synchrony between two signals. The limitation is the need to select a window size. Parameters ---------- signal1 : Union[list, np.array, pd.Series] Time series in the form of a vector of values. signal2 : Union[list, np.array, pd.Series] Time series in the form of a vector of values. method : str The method to use. Can be one of ``"hilbert"`` or ``"correlation"``. window_size : int Only used if ``method='correlation'``. The number of samples to use for rolling correlation. See Also -------- scipy.signal.hilbert, mutual_information Returns ------- array A vector containing the phase of the signal, between 0 and 2*pi. Examples -------- .. ipython:: python import neurokit2 as nk s1 = nk.signal_simulate(duration=10, frequency=1) s2 = nk.signal_simulate(duration=10, frequency=1.5) coupling1 = nk.signal_synchrony(s1, s2, method="hilbert") coupling2 = nk.signal_synchrony(s1, s2, method="correlation", window_size=1000/2) @savefig p_signal_synchrony1.png scale=100% nk.signal_plot([s1, s2, coupling1, coupling2], labels=["s1", "s2", "hilbert", "correlation"]) @suppress plt.close() References ---------- * http://jinhyuncheong.com/jekyll/update/2017/12/10/Timeseries_synchrony_tutorial_and_simulations.html """ if method.lower() in ["hilbert", "phase"]: coupling = _signal_synchrony_hilbert(signal1, signal2) elif method.lower() in ["correlation"]: coupling = _signal_synchrony_correlation(signal1, signal2, window_size=int(window_size)) else: raise ValueError( "NeuroKit error: signal_synchrony(): 'method' should be one of 'hilbert' or 'correlation'." ) return coupling
# ============================================================================= # Methods # ============================================================================= def _signal_synchrony_hilbert(signal1, signal2): hill1 = scipy.signal.hilbert(signal1) hill2 = scipy.signal.hilbert(signal2) phase1 = np.angle(hill1, deg=False) phase2 = np.angle(hill2, deg=False) synchrony = 1 - np.sin(np.abs(phase1 - phase2) / 2) return synchrony def _signal_synchrony_correlation(signal1, signal2, window_size, center=False): """**Calculates pairwise rolling correlation at each time** Grabs the upper triangle, at each timepoint. * window: window size of rolling corr in samples * center: whether to center result (Default: False, so correlation values are listed on the right.) """ data = pd.DataFrame({"y1": signal1, "y2": signal2}) rolled = data.rolling(window=window_size, center=center).corr() synchrony = rolled["y1"].loc[rolled.index.get_level_values(1) == "y2"].values # Realign synchrony = np.append(synchrony[int(window_size / 2) :], np.full(int(window_size / 2), np.nan)) synchrony[np.isnan(synchrony)] = np.nanmean(synchrony) return synchrony