Shortcuts

Source code for torcheeg.transforms.numpy.band_pyeeg

from typing import Dict, Tuple, Union
from scipy.signal import butter, lfilter, hann

import numpy as np

from ..base_transform import EEGTransform

np.seterr(all="ignore")


def butter_bandpass(low_cut, high_cut, fs, order=3):
    nyq = 0.5 * fs
    low = low_cut / nyq
    high = high_cut / nyq
    b, a = butter(order, [low, high], btype='band')
    return b, a


class BandTransform(EEGTransform):
    def __init__(self,
                 sampling_rate: int = 128,
                 order: int = 5,
                 band_dict: Dict[str, Tuple[int, int]] = {
                     "theta": [4, 8],
                     "alpha": [8, 14],
                     "beta": [14, 31],
                     "gamma": [31, 49]
                 },
                 apply_to_baseline: bool = False):
        super(BandTransform, self).__init__(apply_to_baseline=apply_to_baseline)
        self.sampling_rate = sampling_rate
        self.order = order
        self.band_dict = band_dict

    def apply(self, eeg: np.ndarray, **kwargs) -> np.ndarray:
        band_list = []
        for low, high in self.band_dict.values():
            c_list = []
            for c in eeg:
                b, a = butter_bandpass(low,
                                       high,
                                       fs=self.sampling_rate,
                                       order=self.order)
                c_list.append(self.opt(lfilter(b, a, c)))
            c_list = np.array(c_list)
            band_list.append(c_list)
        return np.stack(band_list, axis=-1)

    def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray:
        raise NotImplementedError

    @property
    def repr_body(self) -> Dict:
        return dict(
            super().repr_body, **{
                'sampling_rate': self.sampling_rate,
                'order': self.order,
                'band_dict': {...}
            })


def embed_seq(time_series, tau, embedding_dimension):
    if not type(time_series) == np.ndarray:
        typed_time_series = np.asarray(time_series)
    else:
        typed_time_series = time_series

    shape = (typed_time_series.size - tau * (embedding_dimension - 1),
             embedding_dimension)

    strides = (typed_time_series.itemsize, tau * typed_time_series.itemsize)

    return np.lib.stride_tricks.as_strided(typed_time_series,
                                           shape=shape,
                                           strides=strides)


[docs]class BandApproximateEntropy(BandTransform): r''' A transform method for calculating the approximate entropy of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/entropy.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandApproximateEntropy() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: M (int): A positive integer represents the length of each compared run of data. (default: :obj:`5`) R (float): A positive real number specifies a filtering level. (default: :obj:`5`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, M: int = 5, R: float = 1.0, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandApproximateEntropy, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.M = M self.R = R
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: N = len(eeg) Em = embed_seq(eeg, 1, self.M) A = np.tile(Em, (len(Em), 1, 1)) B = np.transpose(A, [1, 0, 2]) D = np.abs(A - B) # D[i,j,k] = |Em[i][k] - Em[j][k]| InRange = np.max(D, axis=2) <= self.R # Probability that random M-sequences are in range Cm = InRange.mean(axis=0) # M+1-sequences in range if M-sequences are in range & last values are close Dp = np.abs( np.tile(eeg[self.M:], (N - self.M, 1)) - np.tile(eeg[self.M:], (N - self.M, 1)).T) Cmp = np.logical_and(Dp <= self.R, InRange[:-1, :-1]).mean(axis=0) Phi_m, Phi_mp = np.sum(np.log(Cm)), np.sum(np.log(Cmp)) Ap_En = (Phi_m - Phi_mp) / (N - self.M) return Ap_En @property def repr_body(self) -> Dict: return dict(super().repr_body, **{'M': self.M, 'R': self.R})
[docs]class BandSampleEntropy(BandTransform): r''' A transform method for calculating the sample entropy of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/entropy.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandSampleEntropy() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: M (int): A positive integer represents the length of each compared run of data. (default: :obj:`5`) R (float): A positive real number specifies a filtering level. (default: :obj:`5`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, M: int = 5, R: float = 1.0, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandSampleEntropy, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.M = M self.R = R
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: N = len(eeg) Em = embed_seq(eeg, 1, self.M) A = np.tile(Em, (len(Em), 1, 1)) B = np.transpose(A, [1, 0, 2]) D = np.abs(A - B) InRange = np.max(D, axis=2) <= self.R np.fill_diagonal(InRange, 0) Cm = InRange.sum(axis=0) Dp = np.abs( np.tile(eeg[self.M:], (N - self.M, 1)) - np.tile(eeg[self.M:], (N - self.M, 1)).T) Cmp = np.logical_and(Dp <= self.R, InRange[:-1, :-1]).sum(axis=0) Samp_En = np.log(np.sum(Cm + 1e-100) / np.sum(Cmp + 1e-100)) return Samp_En @property def repr_body(self) -> Dict: return dict(super().repr_body, **{'M': self.M, 'R': self.R})
[docs]class BandSVDEntropy(BandTransform): r''' A transform method for calculating the SVD entropy of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/entropy.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandSVDEntropy() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: Tau (int): A positive integer represents the embedding time delay which controls the number of time periods between elements of each of the new column vectors. (default: :obj:`1`) DE (int): A positive integer represents the ength of the embedding dimension. (default: :obj:`1`) W (np.ndarray, optional): A list of normalized singular values of the embedding matrix (can be preset for speeding up). (default: :obj:`None`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, Tau: int = 1, DE: int = 1, W: Union[np.ndarray, None] = None, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandSVDEntropy, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.Tau = Tau self.DE = DE self.W = W
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: if self.W is None: Y = embed_seq(eeg, self.Tau, self.DE) W = np.linalg.svd(Y, compute_uv=0) W /= sum(W) # normalize singular values else: W = self.W return -1 * sum(W * np.log(W)) @property def repr_body(self) -> Dict: return dict(super().repr_body, **{ 'Tau': self.Tau, 'DE': self.DE, 'W': [...] })
[docs]class BandDetrendedFluctuationAnalysis(BandTransform): r''' A transform method for calculating the detrended fluctuation analysis (DFA) of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/detrended_fluctuation_analysis.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandDetrendedFluctuationAnalysis() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: Ave (float, optional): The average value of the time series. (default: :obj:`None`) L (List[np.array]): Box sizes to partition/slice/segment the integrated sequence into boxes. At least two boxes are needed, and it should be a list of integers in ascending order. (default: :obj:`np.array`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, Ave: Union[float, None] = None, L: Union[np.ndarray, None] = None, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandDetrendedFluctuationAnalysis, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.Ave = Ave self.L = L
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: eeg = np.array(eeg) if self.Ave is None: Ave = np.mean(eeg) else: Ave = self.Ave Y = np.cumsum(eeg) Y -= Ave if self.L is None: L = np.floor( len(eeg) * 1 / (2**np.array(list(range(4, int(np.log2(len(eeg))) - 4))))) else: L = self.L F = np.zeros(len(L)) for i in range(0, len(L)): n = int(L[i]) assert n, 'Time series is too short while the box length is too big.' for j in range(0, len(eeg), n): if j + n < len(eeg): c = list(range(j, j + n)) c = np.vstack([c, np.ones(n)]).T y = Y[j:j + n] F[i] += np.linalg.lstsq(c, y)[1] F[i] /= ((len(eeg) / n) * n) F = np.sqrt(F) Alpha = np.linalg.lstsq(np.vstack([np.log(L), np.ones(len(L))]).T, np.log(F), rcond=None)[0][0] return Alpha @property def repr_body(self) -> Dict: return dict(super().repr_body, **{'Ave': self.Ave, 'L': [...]})
[docs]class BandHiguchiFractalDimension(BandTransform): r''' A transform method for calculating the higuchi fractal dimension (HFD) of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/fractal_dimension.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandHiguchiFractalDimension() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: K_max (int): The max number of new self-similar time series applying Higuchi's algorithm. (default: :obj:`6`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, K_max: int = 6, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandHiguchiFractalDimension, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.K_max = K_max
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: L = [] x = [] N = len(eeg) for k in range(1, self.K_max): Lk = [] for m in range(0, k): Lmk = 0 for i in range(1, int(np.floor((N - m) / k))): Lmk += abs(eeg[m + i * k] - eeg[m + i * k - k]) Lmk = Lmk * (N - 1) / np.floor((N - m) / float(k)) / k Lk.append(Lmk) L.append(np.log(np.mean(Lk))) x.append([np.log(float(1) / k), 1]) (p, _, _, _) = np.linalg.lstsq(x, L, rcond=None) return p[0] @property def repr_body(self) -> Dict: return dict(super().repr_body, **{'K_max': self.K_max})
[docs]class BandHjorth(BandTransform): r''' A transform method for calculating the hjorth mobility/complexity of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/hjorth_mobility_complexity.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandHjorth() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: D (np.ndarray, optional): The first order differential sequence of the time series (can be preset for speeding up). (default: :obj:`None`) mode (str): Options include mobility, complexity, and both, which are used to calculate hjorth mobility, hjorth complexity, and concatenate the two, respectively. (default: :obj:`mobility`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, D: Union[np.ndarray, None] = None, mode: str = 'mobility', sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandHjorth, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.D = D self.mode = mode
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: if self.D is None: D = np.diff(eeg) D = D.tolist() else: D = self.D D.insert(0, eeg[0]) # pad the first difference D = np.array(D) n = len(eeg) M2 = float(sum(D**2)) / n TP = sum(np.array(eeg)**2) M4 = 0 for i in range(1, len(D)): M4 += (D[i] - D[i - 1])**2 M4 = M4 / n mobility = np.sqrt(M2 / TP) complexity = np.sqrt(float(M4) * TP / M2 / M2) if self.mode == 'mobility': return mobility elif self.mode == 'complexity': return complexity elif self.mode == 'both': return np.concatenate(mobility, complexity) else: raise NotImplementedError @property def repr_body(self) -> Dict: return dict(super().repr_body, **{'D': [...]})
[docs]class BandHurst(BandTransform): r''' A transform method for calculating the hurst exponent of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/hurst.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandHurst() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) If the output H=0.5,the behavior of the EEG signals is similar to random walk. If H<0.5, the EEG signals cover less "distance" than a random walk, vice verse. .. automethod:: __call__ '''
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: eeg = np.array(eeg) N = eeg.size T = np.arange(1, N + 1) Y = np.cumsum(eeg) Ave_T = Y / T S_T = np.zeros(N) R_T = np.zeros(N) for i in range(N): S_T[i] = np.std(eeg[:i + 1]) eeg_T = Y - T * Ave_T[i] R_T[i] = np.ptp(eeg_T[:i + 1]) R_S = R_T / S_T R_S = np.log(R_S)[1:] n = np.log(T)[1:] A = np.column_stack((n, np.ones(n.size))) [m, c] = np.linalg.lstsq(A, R_S, rcond=None)[0] H = m return H
[docs]class BandPetrosianFractalDimension(BandTransform): r''' A transform method for calculating the petrosian fractal dimension (PFD) of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/fractal_dimension.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandHiguchiFractalDimension() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: D (np.ndarray, optional): The first order differential sequence of the time series (can be preset for speeding up). (default: :obj:`None`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, D: Union[np.ndarray, None] = None, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandPetrosianFractalDimension, self).__init__(sampling_rate=sampling_rate, order=order, band_dict=band_dict, apply_to_baseline=apply_to_baseline) self.D = D
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def opt(self, eeg: np.ndarray, **kwargs) -> np.ndarray: if self.D is None: D = np.diff(eeg) D = D.tolist() else: D = self.D N_delta = 0 for i in range(1, len(D)): if D[i] * D[i - 1] < 0: N_delta += 1 n = len(eeg) return np.log10(n) / (np.log10(n) + np.log10(n / n + 0.4 * N_delta)) @property def repr_body(self) -> Dict: return dict(super().repr_body, **{'D': [...]})
def bin_power(X, band, sampling_rate): C = np.fft.fft(X) C = abs(C) power = np.zeros(len(band) - 1) for Freq_Index in range(0, len(band) - 1): Freq = float(band[Freq_Index]) Next_Freq = float(band[Freq_Index + 1]) power[Freq_Index] = sum( C[int(np.floor(Freq / sampling_rate * len(X))):int(np.floor(Next_Freq / sampling_rate * len(X)))]) power_ratio = power / sum(power) return power, power_ratio
[docs]class BandBinPower(EEGTransform): r''' A transform method for calculating the power of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/fractal_dimension.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandBinPower() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: sampling_rate (int): The original sampling rate in Hz (default: :obj:`128`) order (int): The order of the filter. (default: :obj:`5`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandBinPower, self).__init__(apply_to_baseline=apply_to_baseline) self.sampling_rate = sampling_rate self.order = order self.band_dict = band_dict
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def apply(self, eeg: np.ndarray, **kwargs) -> np.ndarray: band_a_b_list = [] for band_a_b in self.band_dict.values(): band_a_b_list += band_a_b band = sorted(list(set(band_a_b_list))) c_list = [] for c in eeg: c_list.append(bin_power(c, band, self.sampling_rate)[1]) return np.array(c_list) @property def repr_body(self) -> Dict: return dict( super().repr_body, **{ 'sampling_rate': self.sampling_rate, 'order': self.order, 'band_dict': {...}, })
[docs]class BandSpectralEntropy(EEGTransform): r''' A transform method for calculating the spectral entropy of EEG signals in several sub-bands with EEG signals as input. We revised part of the implementation in PyEEG to fit the TorchEEG pipeline. - Paper: Bao F S, Liu X, Zhang C. PyEEG: an open source python module for EEG/MEG feature extraction[J]. Computational intelligence and neuroscience, 2011, 2011. - URL: https://www.hindawi.com/journals/cin/2011/406391/ - Related Project: https://github.com/forrestbao/pyeeg/blob/master/pyeeg/entropy.py Please cite the above paper if you use this module. .. code-block:: python from torcheeg import transforms t = transforms.BandSampleEntropy() t(eeg=np.random.randn(32, 128))['eeg'].shape >>> (32, 4) Args: power_ratio (np.ndarray, optional): A list of normalized signal power in the set of sub-bands (can be preset for speeding up). (default: :obj:`None`) band_dict: (dict): Band name and the critical sampling rate or frequencies. By default, the differential entropy of the four sub-bands, theta, alpha, beta and gamma, is calculated. (default: :obj:`{...}`) apply_to_baseline: (bool): Whether to act on the baseline signal at the same time, if the baseline is passed in when calling. (default: :obj:`False`) .. automethod:: __call__ ''' def __init__(self, power_ratio: Union[np.ndarray, None] = None, sampling_rate: int = 128, order: int = 5, band_dict: Dict[str, Tuple[int, int]] = { "theta": [4, 8], "alpha": [8, 14], "beta": [14, 31], "gamma": [31, 49] }, apply_to_baseline: bool = False): super(BandSpectralEntropy, self).__init__(apply_to_baseline=apply_to_baseline) self.power_ratio = power_ratio self.sampling_rate = sampling_rate self.order = order self.band_dict = band_dict
[docs] def __call__(self, *args, eeg: np.ndarray, baseline: Union[np.ndarray, None] = None, **kwargs) -> Dict[str, np.ndarray]: r''' Args: eeg (np.ndarray): The input EEG signals in shape of [number of electrodes, number of data points]. baseline (np.ndarray, optional) : The corresponding baseline signal, if apply_to_baseline is set to True and baseline is passed, the baseline signal will be transformed with the same way as the experimental signal. Returns: np.ndarray[number of electrodes, number of sub-bands]: The differential entropy of several sub-bands for all electrodes. ''' return super().__call__(*args, eeg=eeg, baseline=baseline, **kwargs)
def apply(self, eeg: np.ndarray, **kwargs) -> np.ndarray: band_a_b_list = [] for band_a_b in self.band_dict.values(): band_a_b_list += band_a_b band = sorted(list(set(band_a_b_list))) c_list = [] for c in eeg: if self.power_ratio is None: power, power_ratio = bin_power(c, band, self.sampling_rate) else: power_ratio = self.power_ratio spectral_entropy = 0 for i in range(0, len(power_ratio) - 1): spectral_entropy += power_ratio[i] * np.log(power_ratio[i]) spectral_entropy /= np.log(len(power_ratio)) c_list.append([-1 * spectral_entropy]) return np.array(c_list) @property def repr_body(self) -> Dict: return dict( super().repr_body, **{ 'power_ratio': [...], 'sampling_rate': self.sampling_rate, 'order': self.order, 'band_dict': {...}, })
Read the Docs v: latest
Versions
latest
stable
v1.1.2
v1.1.1
v1.1.0
v1.0.11
v1.0.10
v1.0.9
v1.0.8.post1
v1.0.8
v1.0.7
v1.0.6
v1.0.4
v1.0.3
v1.0.2
v1.0.1
Downloads
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.

Docs

Access comprehensive developer documentation for PyTorch

View Docs

Tutorials

Get in-depth tutorials for beginners and advanced developers

View Tutorials

Resources

Find development resources and get your questions answered

View Resources