Exemplo n.º 1
0
    def filter(self):
        """Apply the constructed filter.

        Returns
        -------
        A TimeSeries object.

        """
        channel_axis = self.timeseries[self.channels_dim]

        ch0 = self.bipolar_pairs[self.chan_names[0]]
        ch1 = self.bipolar_pairs[self.chan_names[1]]

        sel0 = channel_axis.loc[ch0]
        sel1 = channel_axis.loc[ch1]

        ts0 = self.timeseries.loc[{self.channels_dim: sel0}]
        ts1 = self.timeseries.loc[{self.channels_dim: sel1}]

        dims_bp = list(self.timeseries.dims)

        coords_bp = {
            coord_name: coord
            for coord_name, coord in list(self.timeseries.coords.items())
        }
        coords_bp[self.channels_dim] = self.bipolar_pairs

        ts = TimeSeries(data=ts0.values - ts1.values,
                        dims=dims_bp,
                        coords=coords_bp)
        ts['samplerate'] = self.timeseries['samplerate']

        ts.attrs = self.timeseries.attrs.copy()
        ts.name = self.timeseries.name
        return ts
    def filter(self):
        """
        Applies Butterwoth filter to input time series and returns filtered TimeSeriesX object

        Returns
        -------
        filtered: TimeSeries
            The filtered time series

        """
        time_axis_index = get_axis_index(self.timeseries, axis_name='time')
        filtered_array = buttfilt(self.timeseries,
                                  self.freq_range,
                                  float(self.timeseries['samplerate']),
                                  self.filt_type,
                                  self.order,
                                  axis=time_axis_index)

        coords_dict = {
            coord_name: DataArray(coord.copy())
            for coord_name, coord in list(self.timeseries.coords.items())
        }
        coords_dict['samplerate'] = self.timeseries['samplerate']
        dims = [dim_name for dim_name in self.timeseries.dims]
        filtered_timeseries = TimeSeries(filtered_array,
                                         dims=dims,
                                         coords=coords_dict)

        # filtered_timeseries = TimeSeries(filtered_timeseries)
        filtered_timeseries.attrs = self.timeseries.attrs.copy()
        return filtered_timeseries
Exemplo n.º 3
0
    def read_session_data(self):
        """
        Reads entire session worth of data

        :return: TimeSeries object (channels x events x time) with data for entire session the events dimension has length 1
        """
        brr = self.READER_FILETYPE_DICT[os.path.splitext(self.session_dataroot)[-1]](dataroot=self.session_dataroot, channels=self.channels)
        session_array,read_ok_mask = brr.read()
        self.channel_name = brr.channel_name

        offsets_axis = session_array['offsets']
        number_of_time_points = offsets_axis.shape[0]
        samplerate = float(session_array['samplerate'])
        physical_time_array = np.arange(number_of_time_points) * (1.0 / samplerate)

        # session_array = session_array.rename({'start_offsets': 'events'})

        session_time_series = TimeSeries(session_array.values,
                                         dims=[self.channel_name, 'start_offsets', 'time'],
                                         coords={
                                              self.channel_name: session_array[self.channel_name],
                                              'start_offsets': session_array['start_offsets'],
                                              'time': physical_time_array,
                                              'offsets': ('time', session_array['offsets']),
                                              'samplerate': session_array['samplerate']
                                          }
                                         )
        session_time_series.attrs = session_array.attrs.copy()
        session_time_series.attrs['dataroot'] = self.session_dataroot

        return session_time_series
Exemplo n.º 4
0
def test_init():
    """Test that everything is initialized properly."""
    data = np.random.random((10, 10, 10))
    rate = 1000

    with pytest.raises(AssertionError):
        TimeSeries(data, {})

    with pytest.raises(AssertionError):
        TimeSeries.create(data, None, coords={})

    assert TimeSeries.create(data, None, coords={
        'samplerate': 1
    }).samplerate == 1

    ts = TimeSeries(data, dict(samplerate=rate))
    assert isinstance(ts, xr.DataArray)
    assert ts.shape == (10, 10, 10)
    assert ts['samplerate'] == rate
Exemplo n.º 5
0
def hilbert_pow(dat_ts, bands=None, pad_to_pow2=False, verbose=True):
    """
    """
    # set default freq bands
    if bands is None:
        bands = freq_bands

    # proc padding
    taxis = dat_ts.get_axis(dat_ts.tdim)
    npts_orig = dat_ts.shape[taxis]
    if pad_to_pow2:
        npts = 2**next_pow2(npts_orig)
    else:
        npts = npts_orig

    # calc the hilbert power
    if verbose:
        sys.stdout.write('Hilbert Bands: ')
        sys.stdout.flush()
    pow = None
    for band in bands:
        if verbose:
            sys.stdout.write('%s ' % band[0])
            sys.stdout.flush()
        p = TimeSeries(np.abs(
            hilbert(dat_ts.filtered(band[1], filt_type='pass'),
                    N=npts,
                    axis=taxis).take(np.arange(npts_orig), axis=taxis)),
                       tdim=dat_ts.tdim,
                       samplerate=dat_ts.samplerate,
                       dims=dat_ts.dims.copy()).add_dim(Dim([band[0]],
                                                            'freqs'))
        if pow is None:
            pow = p
        else:
            pow = pow.extend(p, 'freqs')

    if verbose:
        sys.stdout.write('\n')
        sys.stdout.flush()
    return pow
Exemplo n.º 6
0
    def test_wavelets_synthetic_data(self):
        samplerate = 1000.
        frequency = 180.0
        modulation_frequency = 80.0

        duration = 1.0

        n_points = int(np.round(duration * samplerate))
        x = np.arange(n_points, dtype=np.float)
        y = np.sin(x * (2 * np.pi * frequency / n_points))
        y_mod = np.sin(x * (2 * np.pi * frequency / n_points)) * np.sin(
            x * (2 * np.pi * modulation_frequency / n_points))

        ts = TimeSeries(y, dims=['time'], coords=[x])
        ts['samplerate'] = samplerate
        ts.attrs['samplerate'] = samplerate

        frequencies = [10.0, 30.0, 50.0, 80., 120., 180., 250.0, 300.0, 500.]
        for frequency in frequencies:
            wf = MorletWaveletFilter(timeseries=ts,
                                     freqs=np.array([frequency]),
                                     output='both',
                                     frequency_dim_pos=0,
                                     verbose=True)

            pow_wavelet, phase_wavelet = wf.filter()

            from ptsa.wavelet import phase_pow_multi

            pow_wavelet_ptsa_orig = phase_pow_multi(freqs=[frequency],
                                                    samplerates=samplerate,
                                                    dat=ts.data,
                                                    to_return='power')

            assert_array_almost_equal(
                (pow_wavelet_ptsa_orig - pow_wavelet) / pow_wavelet_ptsa_orig,
                np.zeros_like(pow_wavelet),
                decimal=6)
Exemplo n.º 7
0
def compute_power(events,
                  freqs,
                  wave_num,
                  rel_start_ms,
                  rel_stop_ms,
                  buf_ms=1000,
                  elec_scheme=None,
                  noise_freq=[58., 62.],
                  resample_freq=None,
                  mean_over_time=True,
                  log_power=True,
                  loop_over_chans=True,
                  cluster_pool=None,
                  use_mirror_buf=False,
                  time_bins=None,
                  do_average_ref=False):
    """
    Returns a TimeSeries object of power values with dimensions 'events' x 'frequency' x 'bipolar_pairs/channels' x
    'time', unless mean_over_time is True, then no 'time' dimenstion.

    Parameters
    ----------
    events: pandas.DataFrame
        An events structure that contains eegoffset and eegfile fields
    freqs: np.array or list
        A set of frequencies at which to compute power using morlet wavelet transform
    wave_num: int
        Width of the wavelet in cycles (I THINK)
    rel_start_ms: int
        Initial time (in ms), relative to the onset of each event
    rel_stop_ms: int
        End time (in ms), relative to the onset of each event
    buf_ms:
        Amount of time (in ms) of buffer to add to both the begining and end of the time interval before power
        computation. This buffer is automatically removed before returning the power timeseries.
    elec_scheme: pandas.DataFrame:
        A dataframe of electrode information, returned by load_elec_info(). If the column 'contact' is in the dataframe,
        monopolar electrodes will be loads. If the columns 'contact_1' and 'contact_2' are in the df, bipolar will be
        loaded. You may pass in a subset of rows to only load data for electrodes in those rows.

        If you do not enter an elec_scheme, all monopolar channels will be loaded (but they will not be labeled with
        correct channel tags). Entering a scheme is recommended.
    noise_freq: list
        Stop filter will be applied to the given range. Default=(58. 62)
    resample_freq: float
        Sampling rate to resample to after loading eeg but BEFORE computing power. So be careful. Don't downsample below
        your nyquist.
    mean_over_time: bool
        Whether to mean power over time, and return the power data with no time dimension
    log_power: bool
        Whether to log the power values
    loop_over_chans: bool
        Whether to process each channel independently, or whether to try to do all channels at once. Default is to loop
    cluster_pool: None or ipython cluster helper pool
        If given, will parallelize over channels
    use_mirror_buf: bool
        If True, a mirror buffer will be (used see load_eeg) instead of a normal buffer
    time_bins: list or array
        pairs of start and stop times in which to bin the data
    do_average_ref: bool
        If true, will load eeg and then compute an average reference before computing power. Note: This will load eeg
        for all channels at once, regardless of loop_over_chans or cluster_pool. Will still loop for power computation.
    Returns
    -------
    timeseries object of power values

    """

    # warn people if they set the resample_freq too low
    if (resample_freq is not None) and (resample_freq < (np.max(freqs) * 2.)):
        print('Resampling EEG below nyquist frequency.')
        warnings.warn('Resampling EEG below nyquist frequency.')

    # make freqs a numpy array if it isn't already because PTSA is kind of stupid and can't handle a list of numbers
    if isinstance(freqs, list):
        freqs = np.array(freqs)

    # if doing an average reference, load eeg first
    if do_average_ref:
        eeg_all_chans = load_eeg(events,
                                 rel_start_ms,
                                 rel_stop_ms,
                                 buf_ms=buf_ms,
                                 elec_scheme=elec_scheme,
                                 noise_freq=noise_freq,
                                 resample_freq=resample_freq,
                                 use_mirror_buf=use_mirror_buf,
                                 do_average_ref=do_average_ref)
    else:
        eeg_all_chans = None

    # We will loop over channels if desired or if we are are using a pool to parallelize
    if cluster_pool or loop_over_chans:

        # must enter an elec scheme if we want to loop over channels
        if elec_scheme is None:
            print(
                'elec_scheme must be entered if loop_over_chans is True or using a cluster pool.'
            )
            return

        # put all the inputs into one list. This is so because it is easier to parallize this way. Parallel functions
        # accept one input. The pool iterates over this list.
        arg_list = [
            (events, freqs, wave_num, elec_scheme.iloc[r:r + 1], rel_start_ms,
             rel_stop_ms, buf_ms, noise_freq, resample_freq, mean_over_time,
             log_power, use_mirror_buf, time_bins,
             eeg_all_chans[:, r:r + 1] if eeg_all_chans is not None else None)
            for r in range(elec_scheme.shape[0])
        ]

        # if no pool, just use regular map
        if cluster_pool is not None:
            pow_list = cluster_pool.map(_parallel_compute_power, arg_list)
        else:
            pow_list = list(
                map(
                    _parallel_compute_power,
                    tqdm(arg_list,
                         disable=True if len(arg_list) == 1 else False)))

        # This is the stupidest thing in the world. I should just be able to do concat(pow_list, dim='channels') or
        # concat(pow_list, dim='bipolar_pairs'), but for some reason it breaks. I don't know. So I'm creating a new
        # TimeSeries object

        # concatenate data
        chan_dim = pow_list[0].get_axis_num('channel')
        elecs = np.concatenate([x[x.dims[chan_dim]].data for x in pow_list])
        pow_cat = np.concatenate([x.data for x in pow_list], axis=chan_dim)

        # create new coordinates and Timeseries with concatenated data and electrode info
        new_coords = {
            x: (pow_list[0].coords[x] if x != 'channel' else elecs)
            for x in pow_list[0].coords.keys()
        }
        wave_pow = TimeSeries(data=pow_cat,
                              coords=new_coords,
                              dims=pow_list[0].dims)

    # if not looping, sending all the channels at once
    else:
        arg_list = [
            events, freqs, wave_num, elec_scheme, rel_start_ms, rel_stop_ms,
            buf_ms, noise_freq, resample_freq, mean_over_time, log_power,
            use_mirror_buf, time_bins, eeg_all_chans
        ]
        wave_pow = _parallel_compute_power(arg_list)

    # reorder dims to make events first
    wave_pow = make_events_first_dim(wave_pow)

    return wave_pow
Exemplo n.º 8
0
    def filter(self):
        """resamples time series

        Returns
        -------
        resampled: TimeSeries
            resampled time series with sampling frequency set to resamplerate

        """
        samplerate = float(self.timeseries['samplerate'])

        self.time_axis_index = self.timeseries.get_axis_num(
            self.time_axis_name)

        time_axis = self.timeseries.coords[self.timeseries.dims[
            self.time_axis_index]]

        time_axis_length = len(time_axis)
        new_length = int(
            np.round(time_axis_length * self.resamplerate / samplerate))

        try:
            # time axis can be recarray with one of the arrays being time
            time_axis_data = time_axis.data[self.time_axis_name]
        except (KeyError, IndexError) as excp:
            # if we get here then most likely time axis is ndarray of floats
            time_axis_data = time_axis.data

        time_idx_array = np.arange(len(time_axis))

        if self.round_to_original_timepoints:
            filtered_array, new_time_idx_array = resample(
                self.timeseries.data,
                new_length,
                t=time_idx_array,
                axis=self.time_axis_index)

            # print new_time_axis

            new_time_idx_array = np.rint(new_time_idx_array).astype(np.int)

            new_time_axis = time_axis[new_time_idx_array]

        else:
            filtered_array, new_time_axis = \
                resample(self.timeseries.data,
                         new_length,
                         t=time_axis_data,
                         axis=self.time_axis_index)

        coords = {}
        for i, dim_name in enumerate(self.timeseries.dims):
            if i != self.time_axis_index:
                coords[dim_name] = self.timeseries.coords[dim_name].copy()
            else:
                coords[dim_name] = new_time_axis
        coords['samplerate'] = self.resamplerate

        filtered_timeseries = TimeSeries(filtered_array,
                                         coords=coords,
                                         dims=self.timeseries.dims)
        return filtered_timeseries
Exemplo n.º 9
0
    def filter(self):
        """Apply the constructed filter."""
        time_axis = self.timeseries['time']

        wavelet_dims = self.nontime_sizes + (self.freqs.shape[0], )

        powers_reshaped = np.array([[]], dtype=np.float)
        phases_reshaped = np.array([[]], dtype=np.float)
        wavelets_complex_reshaped = np.array([[]], dtype=np.complex)

        if 'power' in self.output:
            powers_reshaped = np.empty(shape=(np.prod(wavelet_dims),
                                              len(self.timeseries['time'])),
                                       dtype=np.float)
        if 'phase' in self.output:
            phases_reshaped = np.empty(shape=(np.prod(wavelet_dims),
                                              len(self.timeseries['time'])),
                                       dtype=np.float)
        if 'complex' in self.output:
            wavelets_complex_reshaped = np.empty(
                shape=(np.prod(wavelet_dims), len(self.timeseries['time'])),
                dtype=np.complex)

        mt = morlet.MorletWaveletTransformMP(self.cpus)

        timeseries_reshaped = np.ascontiguousarray(
            self.timeseries.data.reshape(
                np.prod(self.nontime_sizes, dtype=int),
                len(self.timeseries['time'])), self.timeseries.data.dtype)

        if self.output == ['power']:
            mt.set_output_type(morlet.POWER)
        if self.output == ['phase']:
            mt.set_output_type(morlet.PHASE)
        if 'power' in self.output and 'phase' in self.output:
            mt.set_output_type(morlet.BOTH)

        # TODO: update to allow outputing complex as well as power/phase
        if self.output == ['complex']:
            mt.set_output_type(morlet.COMPLEX)

        mt.set_signal_array(timeseries_reshaped)
        mt.set_wavelet_pow_array(powers_reshaped)
        mt.set_wavelet_phase_array(phases_reshaped)
        mt.set_wavelet_complex_array(wavelets_complex_reshaped)

        mt.initialize_signal_props(float(self.timeseries['samplerate']))
        mt.initialize_wavelet_props(self.width, self.freqs)
        mt.prepare_run()

        s = time.time()
        mt.compute_wavelets_threads()

        powers_final = None
        phases_final = None
        wavelet_complex_final = None

        if 'power' in self.output:
            powers_final = powers_reshaped.reshape(
                wavelet_dims + (len(self.timeseries['time']), ))
        if 'phase' in self.output:
            phases_final = phases_reshaped.reshape(
                wavelet_dims + (len(self.timeseries['time']), ))
        if 'complex' in self.output:
            wavelet_complex_final = wavelets_complex_reshaped.reshape(
                wavelet_dims + (len(self.timeseries['time']), ))

        coords = {k: v for k, v in list(self.timeseries.coords.items())}
        coords['frequency'] = self.freqs

        powers_ts = None
        phases_ts = None
        wavelet_complex_ts = None

        if powers_final is not None:
            powers_ts = TimeSeries(powers_final,
                                   dims=self.nontime_dims +
                                   ('frequency', 'time'),
                                   coords=coords)
            final_dims = (powers_ts.dims[-2], ) + powers_ts.dims[:-2] + (
                powers_ts.dims[-1], )

            powers_ts = powers_ts.transpose(*final_dims)

        if phases_final is not None:
            phases_ts = TimeSeries(phases_final,
                                   dims=self.nontime_dims +
                                   ('frequency', 'time'),
                                   coords=coords)

            final_dims = (phases_ts.dims[-2], ) + phases_ts.dims[:-2] + (
                phases_ts.dims[-1], )

            phases_ts = phases_ts.transpose(*final_dims)

        if wavelet_complex_final is not None:
            wavelet_complex_ts = TimeSeries(wavelet_complex_final,
                                            dims=self.nontime_dims + (
                                                'frequency',
                                                'time',
                                            ),
                                            coords=coords)

            final_dims = (wavelet_complex_ts.dims[-2],
                          ) + wavelet_complex_ts.dims[:-2] + (
                              wavelet_complex_ts.dims[-1], )

            wavelet_complex_ts = wavelet_complex_ts.transpose(*final_dims)

        if self.verbose:
            print('CPP total time wavelet loop: ', time.time() - s)

        if wavelet_complex_ts is not None:
            return wavelet_complex_ts
        else:
            if powers_ts is None:
                return phases_ts
            elif phases_ts is None:
                return powers_ts
            else:
                return powers_ts.append(phases_ts,
                                        dim=self.output_dim).assign_coords(
                                            output=['power', 'phase'])
Exemplo n.º 10
0
    def read_events_data(self):
        """
        Reads eeg data for individual event

        :return: TimeSeries  object (channels x events x time) with data for individual events
        """
        self.event_ok_mask_sorted = None  # reset self.event_ok_mask_sorted

        evs = self.events

        raw_readers, original_dataroots = self.__create_base_raw_readers()

        # used for restoring original order of the events
        ordered_indices = np.arange(len(evs))
        event_indices_list = []
        events = []

        ts_array_list = []

        event_ok_mask_list = []

        for s, (raw_reader, dataroot) in enumerate(zip(raw_readers, original_dataroots)):

            ts_array, read_ok_mask = raw_reader.read()

            event_ok_mask_list.append(np.all(read_ok_mask,axis=0))

            ind = np.atleast_1d(evs.eegfile == dataroot)
            event_indices_list.append(ordered_indices[ind])
            events.append(evs[ind])

            ts_array_list.append(ts_array)

        if not all([r.channel_name==raw_readers[0].channel_name for r in raw_readers]):
            raise IncompatibleDataError('cannot read monopolar and bipolar data together')

        self.channel_name = raw_readers[0].channel_name

        event_indices_array = np.hstack(event_indices_list)
        event_indices_restore_sort_order_array = event_indices_array.argsort()

        eventdata = xr.concat(ts_array_list, dim='start_offsets')
        samplerate = float(eventdata['samplerate'])
        tdim = np.arange(eventdata.shape[-1]) * (1.0 / samplerate) + (self.start_time - self.buffer_time)
        cdim = eventdata[self.channel_name]
        edim = np.rec.array(np.concatenate(events))

        attrs = eventdata.attrs.copy()
        eventdata = TimeSeries(eventdata.data,
                               dims=[self.channel_name, 'events', 'time'],
                               coords={self.channel_name: cdim,
                                        'events': edim,
                                        'time': tdim,
                                        'samplerate': samplerate
                                        }
                               )

        eventdata.attrs = attrs

        # restoring original order of the events
        eventdata = eventdata[:, event_indices_restore_sort_order_array, :]

        event_ok_mask = np.hstack(event_ok_mask_list)
        event_ok_mask_sorted = event_ok_mask[event_indices_restore_sort_order_array]

        # removing bad events
        if self.remove_bad_events:
            if np.any(~event_ok_mask_sorted):
                warnings.warn("Found some bad events. Removing!", UserWarning)
                self.removed_corrupt_events = True
                self.event_ok_mask_sorted = event_ok_mask_sorted

        eventdata = eventdata[:, event_ok_mask_sorted, :]

        return eventdata