def _compute_node_covariance(self, small_ts, input_ts_h5):
        """
        Compute the temporal covariance between nodes in a TimeSeries dataType.
        A nodes x nodes matrix is returned for each (state-variable, mode).
        """
        data_shape = small_ts.data.shape

        # (nodes, nodes, state-variables, modes)
        result_shape = (data_shape[2], data_shape[2], data_shape[1],
                        data_shape[3])
        self.log.info("result shape will be: %s" % str(result_shape))

        result = numpy.zeros(result_shape)

        # One inter-node temporal covariance matrix for each state-var & mode.
        for mode in range(data_shape[3]):
            for var in range(data_shape[1]):
                data = input_ts_h5.data[:, var, :, mode]
                data = data - data.mean(axis=0)[numpy.newaxis, 0]
                result[:, :, var, mode] = numpy.cov(data.T)

        self.log.debug("result")
        self.log.debug(narray_describe(result))

        covariance = Covariance(source=small_ts, array_data=result)
        return covariance
Example #2
0
    def _compute_cross_correlation(self, small_ts, input_ts_h5):
        """
        Cross-correlate two one-dimensional arrays. Return a CrossCorrelation datatype with result.
        """
        # (tpts, nodes, nodes, state-variables, modes)
        result_shape = self._result_shape(small_ts.data.shape)
        self.log.info("result shape will be: %s" % str(result_shape))

        result = numpy.zeros(result_shape)

        # TODO: For region level, 4s, 2000Hz, this takes ~3hours...(which makes node_coherence seem positively speedy
        # Probably best to add a keyword for offsets, so we just compute +- some "small" range...
        # One inter-node correlation, across offsets, for each state-var & mode.
        for mode in range(result_shape[4]):
            for var in range(result_shape[3]):
                data = input_ts_h5.data[:, var, :, mode]
                data = data - data.mean(axis=0)[numpy.newaxis, :]
                # TODO: Work out a way around the 4 level loop:
                for n1 in range(result_shape[1]):
                    for n2 in range(result_shape[2]):
                        result[:, n1, n2, var, mode] = correlate(data[:, n1], data[:, n2], mode="same")

        self.log.debug("result")
        self.log.debug(narray_describe(result))

        offset = (small_ts.sample_period *
                  numpy.arange(-numpy.floor(result_shape[0] / 2.0), numpy.ceil(result_shape[0] / 2.0)))

        cross_corr = CrossCorrelation(source=small_ts, array_data=result, time=offset)

        return cross_corr
    def _compute_correlation_coefficients(self, ts_h5, t_start, t_end):
        """
        Compute the correlation coefficients of a 2D array (tpts x nodes).
        Yields an array of size nodes x nodes x state-variables x modes.

        The time interval over which the correlation coefficients are computed
        is defined by t_start, t_end

        See: http://docs.scipy.org/doc/numpy/reference/generated/numpy.corrcoef.html
        """
        # (nodes, nodes, state-variables, modes)
        input_shape = ts_h5.data.shape
        result_shape = self._result_shape(input_shape)
        self.log.info("result shape will be: %s" % str(result_shape))

        result = numpy.zeros(result_shape)

        t_lo = int((1. / self.input_time_series_index.sample_period) *
                   (t_start - self.input_time_series_index.sample_period))
        t_hi = int((1. / self.input_time_series_index.sample_period) *
                   (t_end - self.input_time_series_index.sample_period))
        t_lo = max(t_lo, 0)
        t_hi = max(t_hi, input_shape[0])

        # One correlation coeff matrix, for each state-var & mode.
        for mode in range(result_shape[3]):
            for var in range(result_shape[2]):
                current_slice = tuple([
                    slice(t_lo, t_hi + 1),
                    slice(var, var + 1),
                    slice(input_shape[2]),
                    slice(mode, mode + 1)
                ])
                data = ts_h5.data[current_slice].squeeze()
                result[:, :, var, mode] = numpy.corrcoef(data.T)

        self.log.debug("result")
        self.log.debug(narray_describe(result))

        return result
def calculate_complex_cross_coherence(time_series, epoch_length, segment_length, segment_shift, window_function,
                                      average_segments, subtract_epoch_average, zeropad, detrend_ts, max_freq,
                                      npat):
    """
    # type: (TimeSeries, float, float, float, str, bool, bool, int, bool, float, float)  -> ComplexCoherenceSpectrum
    Calculate the FFT, Cross Coherence and Complex Coherence of time_series
    broken into (possibly) epochs and segments of length `epoch_length` and
    `segment_length` respectively, filtered by `window_function`.

    Parameters
    __________

    time_series : TimeSeries
    The timeseries for which the CrossCoherence and ComplexCoherence is to be computed.

    epoch_length : float
    In general for lengthy EEG recordings (~30 min), the timeseries are divided into equally
    sized segments (~ 20-40s). These contain the  event that is to be characterized by means of the
    cross coherence. Additionally each epoch block will be further divided into segments to  which
    the FFT will be applied.

    segment_length : float
    The segment length determines the frequency resolution of the resulting power spectra --
    longer windows produce finer frequency resolution.

    segment_shift : float
    Time length by which neighboring segments are shifted. e.g.
    `segment shift` = `segment_length` / 2 means 50% overlapping segments.

    window_function : str
    Windowing functions can be applied before the FFT is performed.

    average_segments : bool
    Flag. If `True`, compute the mean Cross Spectrum across  segments.

    subtract_epoch_average: bool
    Flag. If `True` and if the number of epochs is > 1, you can optionally subtract the
    mean across epochs before computing the complex coherence.

    zeropad : int
    Adds `n` zeros at the end of each segment and at the end of window_function. It is not yet functional.

    detrend_ts : bool
    Flag. If `True` removes linear trend along the time dimension before applying FFT.

    max_freq : float
    Maximum frequency points (e.g. 32., 64., 128.) represented in the output. Default is segment_length / 2 + 1.

    npat : float
    This attribute appears to be related to an input projection matrix... Which is not yet implemented.
    """
    # self.time_series.trait["data"].log_debug(owner=cls_attr_name)
    tpts = time_series.data.shape[0]
    time_series_length = tpts * time_series.sample_period

    if len(time_series.data.shape) > 2:
        time_series_data = numpy.squeeze((time_series.data.mean(axis=-1)).mean(axis=1))

    # Divide time-series into epochs, no overlapping
    if epoch_length > 0.0:
        nepochs = int(numpy.floor(time_series_length / epoch_length))
        epoch_tpts = int(epoch_length / time_series.sample_period)
        time_series_length = epoch_length
        tpts = epoch_tpts
    else:
        epoch_length = time_series_length
        nepochs = int(numpy.ceil(time_series_length / epoch_length))

    # Segment time-series, overlapping if necessary
    nseg = int(numpy.floor(time_series_length / segment_length))
    if nseg > 1:
        seg_tpts = int(segment_length / time_series.sample_period)
        seg_shift_tpts = int(segment_shift / time_series.sample_period)
        nseg = int(numpy.floor((tpts - seg_tpts) / seg_shift_tpts) + 1)
    else:
        segment_length = time_series_length
        seg_tpts = time_series_data.shape[0]

    # Frequency
    nfreq = int(numpy.min([max_freq, numpy.floor((seg_tpts + zeropad) / 2.0) + 1]))

    resulted_shape, av_result_shape = complex_coherence_result_shape(time_series.data.shape, max_freq, epoch_length,
                                                                     segment_length, segment_shift,
                                                                     time_series.sample_period, zeropad,
                                                                     average_segments)
    cs = numpy.zeros(resulted_shape, dtype=numpy.complex128)
    av = numpy.matrix(numpy.zeros(av_result_shape, dtype=numpy.complex128))
    coh = numpy.zeros(resulted_shape, dtype=numpy.complex128)

    # Apply windowing function
    if window_function is not None:
        if window_function not in SUPPORTED_WINDOWING_FUNCTIONS:
            log.error("Windowing function is: %s" % window_function)
            log.error("Must be in: %s" % str(SUPPORTED_WINDOWING_FUNCTIONS))

        window_func = eval("".join(("numpy.", window_function)))
        win = window_func(seg_tpts)
        window_mask = (numpy.kron(numpy.ones((time_series_data.shape[1], 1)), win)).T

    nave = 0

    for j in numpy.arange(nepochs):
        data = time_series_data[j * epoch_tpts:(j + 1) * epoch_tpts, :]

        for i in numpy.arange(nseg):  # average over all segments;
            ts = data[i * seg_shift_tpts: i * seg_shift_tpts + seg_tpts, :]

            if detrend_ts:
                ts = sp_signal.detrend(ts, axis=0)

            datalocfft = numpy.fft.fft(ts * window_mask, axis=0)
            datalocfft = numpy.matrix(datalocfft)

            for f in numpy.arange(nfreq):  # for all frequencies
                if npat == 1:
                    if not average_segments:
                        cs[:, :, f, i] += numpy.conjugate(datalocfft[f, :].conj().T * datalocfft[f, :])
                        av[:, f, i] += numpy.conjugate(datalocfft[f, :].conj().T)
                    else:
                        cs[:, :, f] += numpy.conjugate(datalocfft[f, :].conj().T * datalocfft[f, :])
                        av[:, f] += numpy.conjugate(datalocfft[f, :].conj().T)
                else:
                    if not average_segments:
                        cs[:, :, f, j, i] = numpy.conjugate(datalocfft[f, :].conj().T * datalocfft[f, :])
                        av[:, f, j, i] = numpy.conjugate(datalocfft[f, :].conj().T)
                    else:
                        cs[:, :, f, j] += numpy.conjugate(datalocfft[f, :].conj().T * datalocfft[f, :])
                        av[:, f, j] += numpy.conjugate(datalocfft[f, :].conj().T)
            del datalocfft

        nave += 1.0

    # End of FORs
    if not average_segments:
        cs = cs / nave
        av = av / nave
    else:
        nave = nave * nseg
        cs = cs / nave
        av = av / nave

    # Subtract average
    for f in numpy.arange(nfreq):
        if subtract_epoch_average:
            if npat == 1:
                if not average_segments:
                    for i in numpy.arange(nseg):
                        cs[:, :, f, i] = cs[:, :, f, i] - av[:, f, i] * av[:, f, i].conj().T
                else:
                    cs[:, :, f] = cs[:, :, f] - av[:, f] * av[:, f].conj().T
            else:
                if not average_segments:
                    for i in numpy.arange(nseg):
                        for j in numpy.arange(nepochs):
                            cs[:, :, f, j, i] = cs[:, :, f, j, i] - av[:, f, j, i] * av[:, f, j, i].conj().T

                else:
                    for j in numpy.arange(nepochs):
                        cs[:, :, f, j] = cs[:, :, f, j] - av[:, f, j] * av[:, f, j].conj().T

    # Compute Complex Coherence
    ndim = len(cs.shape)
    if ndim == 3:
        for i in numpy.arange(cs.shape[2]):
            temp = numpy.matrix(cs[:, :, i])
            coh[:, :, i] = cs[:, :, i] / numpy.sqrt(temp.diagonal().conj().T * temp.diagonal())

    elif ndim == 4:
        for i in numpy.arange(cs.shape[2]):
            for j in numpy.arange(cs.shape[3]):
                temp = numpy.matrix(numpy.squeeze(cs[:, :, i, j]))
                coh[:, :, i, j] = temp / numpy.sqrt(temp.diagonal().conj().T * temp.diagonal().T)

    log.debug("result")
    log.debug(narray_describe(cs))
    spectra = spectral.ComplexCoherenceSpectrum(source=time_series,
                                                array_data=coh,
                                                cross_spectrum=cs,
                                                epoch_length=epoch_length,
                                                segment_length=segment_length,
                                                windowing_function=window_function)
    return spectra
Example #5
0
def compute_fast_fourier_transform(time_series, segment_length,
                                   window_function, detrend):
    """
    # type: (TimeSeries, float, function, bool) -> FourierSpectrum
    Calculate the FFT of time_series broken into segments of length
    segment_length and filtered by window_function.

    Parameters
    __________

    time_series : TimeSeries
    The TimeSeries to which the FFT is to be applied.

    segment_length : float
    The segment length determines the frequency resolution of the resulting power spectra -- longer
    windows produce finer frequency resolution

    window_function : str
    Windowing functions can be applied before the FFT is performed. Default is None, possibilities are: 'hamming';
    'bartlett';'blackman'; and 'hanning'. See, numpy.<function_name>.

    detrend : bool
    Default is True, False means no detrending is performed on the time series.
    """

    tpts = time_series.data.shape[0]
    time_series_length = tpts * time_series.sample_period

    # Segment time-series, overlapping if necessary
    nseg = int(numpy.ceil(time_series_length / segment_length))
    if nseg > 1:
        seg_tpts = numpy.ceil(segment_length / time_series.sample_period)
        overlap = (seg_tpts * nseg - tpts) / (nseg - 1.0)
        starts = [max(seg * (seg_tpts - overlap), 0) for seg in range(nseg)]
        segments = [
            time_series.data[int(start):int(start) + int(seg_tpts)]
            for start in starts
        ]
        segments = [segment[:, :, :, :, numpy.newaxis] for segment in segments]
        ts = numpy.concatenate(segments, axis=4)
    else:
        segment_length = time_series_length
        ts = time_series.data[:, :, :, :, numpy.newaxis]
        seg_tpts = ts.shape[0]

    log.debug("Segment length being used is: %s" % segment_length)

    # Base-line correct the segmented time-series
    if detrend:
        ts = scipy.signal.detrend(ts, axis=0)
        log.debug("time_series " + narray_describe(ts))

    # Apply windowing function
    if window_function is not None:
        wf = SUPPORTED_WINDOWING_FUNCTIONS[window_function]
        window_mask = numpy.reshape(wf(int(seg_tpts)),
                                    (int(seg_tpts), 1, 1, 1, 1))
        ts = ts * window_mask

    # Calculate the FFT
    result = numpy.fft.fft(ts, axis=0)
    nfreq = result.shape[0] // 2
    result = result[1:nfreq + 1, :]

    log.debug("result " + narray_describe(result))

    spectra = FourierSpectrum(source=time_series,
                              segment_length=segment_length,
                              array_data=result,
                              windowing_function=window_function)
    spectra.configure()

    return spectra
Example #6
0
    def _compute_fcd_matrix(self, ts_h5):
        self.log.debug("timeseries_h5.data")
        self.log.debug(narray_describe(ts_h5.data[:]))

        input_shape = ts_h5.data.shape
        result_shape = self._result_shape(input_shape)

        fcd = np.zeros(result_shape)
        fc_stream = {
        }  # dict where the fc calculated over the sliding window will be stored
        for mode in range(result_shape[3]):
            for var in range(result_shape[2]):
                start = -self.actual_sp  # in order to well initialize the first starting point of the FC stream
                for nfcd in range(result_shape[0]):
                    start += self.actual_sp
                    current_slice = tuple([
                        slice(int(start),
                              int(start + self.actual_sw) + 1),
                        slice(var, var + 1),
                        slice(input_shape[2]),
                        slice(mode, mode + 1)
                    ])
                    data = ts_h5.read_data_slice(current_slice).squeeze()
                    fc = np.corrcoef(data.T)
                    # the triangular part of the fc is organized as a vector, excluding the diagonal (always ones)
                    triangular = np.triu_indices(len(fc), 1)
                    fc_stream[nfcd] = fc[triangular]
                for i in range(result_shape[0]):
                    j = i
                    while j < result_shape[0]:
                        fci = fc_stream[i]
                        fcj = fc_stream[j]
                        fcd[i, j, var, mode] = np.corrcoef(fci, fcj)[0, 1]
                        fcd[j, i, var, mode] = fcd[i, j, var, mode]
                        j += 1

        self.log.debug("FCD")
        self.log.debug(narray_describe(fcd))

        num_eig = 3  # number of the eigenvector that will be extracted

        eigvect_dict = {
        }  # holds eigenvectors of the fcs calculated over the epochs, key1=mode, key2=var, key3=numb ep
        eigval_dict = {
        }  # holds eigenvalues of the fcs calculated over the epochs, key1=mode, key2=var, key3=numb ep
        fcd_segmented = None
        for mode in range(result_shape[3]):
            eigvect_dict[mode] = {}
            eigval_dict[mode] = {}
            for var in range(result_shape[2]):
                eigvect_dict[mode][var] = {}
                eigval_dict[mode][var] = {}
                fcd_matrix = fcd[:, :, var, mode]
                [xir, xir_cutoff] = self._spectral_embedding(fcd_matrix)
                epochs_extremes = self._epochs_interval(
                    xir, xir_cutoff, self.actual_sp, self.actual_sw)
                fcd_segmented = fcd.copy()
                if epochs_extremes.shape[0] <= 1:
                    # means that there are no more than 1 epochs of stability, thus the eigenvectors of
                    # the FC calculated over the entire TimeSeries will be calculated
                    epochs_extremes = np.zeros((2, 2), dtype=float)
                    epochs_extremes[1, 1] = input_shape[
                        0]  # [0,0] set in order to skip the first epoch
                else:
                    # means that more than 1 epochs of stability is identified thus fcd_segmented is calculated
                    fcd_segmented[xir > xir_cutoff, :, var, mode] = 1.1
                    fcd_segmented[:, xir > xir_cutoff, var, mode] = 1.1

                for ep in range(1, epochs_extremes.shape[0]):
                    eigvect_dict[mode][var][ep] = []
                    eigval_dict[mode][var][ep] = []
                    current_slice = tuple([
                        slice(int(epochs_extremes[ep][0]),
                              int(epochs_extremes[ep][1]) + 1),
                        slice(var, var + 1),
                        slice(input_shape[2]),
                        slice(mode, mode + 1)
                    ])
                    data = ts_h5.read_data_slice(current_slice).squeeze()
                    fc = np.corrcoef(
                        data.T)  # calculate fc over the epoch of stability
                    eigval_matrix, eigvect_matrix = linalg.eig(fc)
                    eigval_matrix = np.real(eigval_matrix)
                    eigvect_matrix = np.real(eigvect_matrix)
                    eigval_matrix = eigval_matrix / np.sum(
                        np.abs(eigval_matrix)
                    )  # normalize eigenvalues to [0 and 1)
                    for en in range(num_eig):
                        index = np.argmax(eigval_matrix)
                        eigvect_dict[mode][var][ep].append(
                            abs(eigvect_matrix[:, index]))
                        eigval_dict[mode][var][ep].append(eigval_matrix[index])
                        eigval_matrix[index] = 0

        return [fcd, fcd_segmented, eigvect_dict, eigval_dict]