def test_complexcoherence(self): data = numpy.random.random((10, 10)) ts = time_series.TimeSeries(data=data) dt = spectral.ComplexCoherenceSpectrum(source=ts, array_data = numpy.random.random((10, 10)), cross_spectrum = numpy.random.random((10, 10)), epoch_length = 10, segment_length = 5) summary_info = dt.summary_info self.assertEqual(summary_info['Frequency step'], 0.2) self.assertEqual(summary_info['Maximum frequency'], 0.5) self.assertEqual(summary_info['Source'], '') self.assertEqual(summary_info['Spectral type'], 'ComplexCoherenceSpectrum') self.assertTrue(dt.aggregation_functions is None) self.assertEqual(dt.epoch_length, 10) self.assertEqual(dt.segment_length, 5) self.assertEqual(dt.shape, (10, 10)) self.assertTrue(dt.source is not None) self.assertEqual(dt.windowing_function, '')
def test_complexcoherence(self): data = numpy.random.random((10, 10)) ts = time_series.TimeSeries(data=data, title='meh') dt = spectral.ComplexCoherenceSpectrum(source=ts, windowing_function=str(''), array_data=numpy.random.random((10, 10)), cross_spectrum=numpy.random.random((10, 10)), epoch_length=10, segment_length=5) summary_info = dt.summary_info() assert summary_info['Frequency step'] == 0.2 assert summary_info['Maximum frequency'] == 0.5 assert summary_info['Source'] == 'meh' assert summary_info['Spectral type'] == 'ComplexCoherenceSpectrum' assert dt.epoch_length == 10 assert dt.segment_length == 5 assert dt.array_data.shape, (10 == 10) assert dt.source is not None assert dt.windowing_function is not None
def evaluate(self): """ 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`. """ cls_attr_name = self.__class__.__name__ + ".time_series" # self.time_series.trait["data"].log_debug(owner=cls_attr_name) tpts = self.time_series.data.shape[0] time_series_length = tpts * self.time_series.sample_period if len(self.time_series.data.shape) > 2: time_series_data = numpy.squeeze( (self.time_series.data.mean(axis=-1)).mean(axis=1)) # Divide time-series into epochs, no overlapping if self.epoch_length > 0.0: nepochs = int(numpy.floor(time_series_length / self.epoch_length)) epoch_tpts = int(self.epoch_length / self.time_series.sample_period) time_series_length = self.epoch_length tpts = epoch_tpts else: self.epoch_length = time_series_length nepochs = int(numpy.ceil(time_series_length / self.epoch_length)) # Segment time-series, overlapping if necessary nseg = int(numpy.floor(time_series_length / self.segment_length)) if nseg > 1: seg_tpts = int(self.segment_length / self.time_series.sample_period) seg_shift_tpts = int(self.segment_shift / self.time_series.sample_period) nseg = int(numpy.floor((tpts - seg_tpts) / seg_shift_tpts) + 1) else: self.segment_length = time_series_length seg_tpts = time_series_data.shape[0] # Frequency nfreq = int( numpy.min([ self.max_freq, numpy.floor((seg_tpts + self.zeropad) / 2.0) + 1 ])) result_shape, av_result_shape = self.result_shape( self.time_series.data.shape, self.max_freq, self.epoch_length, self.segment_length, self.segment_shift, self.time_series.sample_period, self.zeropad, self.average_segments) cs = numpy.zeros(result_shape, dtype=numpy.complex128) av = numpy.matrix(numpy.zeros(av_result_shape, dtype=numpy.complex128)) coh = numpy.zeros(result_shape, dtype=numpy.complex128) # Apply windowing function if self.window_function is not None: if self.window_function not in SUPPORTED_WINDOWING_FUNCTIONS: self.log.error("Windowing function is: %s" % self.window_function) self.log.error("Must be in: %s" % str(SUPPORTED_WINDOWING_FUNCTIONS)) window_function = eval("".join(("numpy.", self.window_function))) win = window_function(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; time_series = data[i * seg_shift_tpts:i * seg_shift_tpts + seg_tpts, :] if self.detrend_ts: time_series = sp_signal.detrend(time_series, axis=0) datalocfft = numpy.fft.fft(time_series * window_mask, axis=0) datalocfft = numpy.matrix(datalocfft) for f in numpy.arange(nfreq): # for all frequencies if self.npat == 1: if not self.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 self.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 self.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 self.subtract_epoch_average: if self.npat == 1: if not self.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 self.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) self.log.debug("result") self.log.debug(narray_describe(cs)) spectra = spectral.ComplexCoherenceSpectrum( source=self.time_series, array_data=coh, cross_spectrum=cs, epoch_length=self.epoch_length, segment_length=self.segment_length, windowing_function=self.window_function) return spectra
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