Beispiel #1
0
def preprocess_raw_data(block_path, config):
    """
    Takes raw data and runs:
    1) CAR
    2) notch filters
    3) Downsampling

    Parameters
    ----------
    block_path : str
        subject file path
    config : dictionary
        'CAR' - Number of channels to use in CAR (default=16)
        'Notch' - Main frequency (Hz) for notch filters (default=60)
        'Downsample' - Downsampling frequency (Hz, default= 400)

    Returns
    -------
    Saves preprocessed signals (LFP) in the current NWB file.
    Only if containers for these data do not exist in the current file.
    """
    subj_path, block_name = os.path.split(block_path)
    block_name = os.path.splitext(block_path)[0]
    start = time.time()

    with NWBHDF5IO(block_path, 'r+', load_namespaces=True) as io:
        nwb = io.read()

        # Storage of processed signals on NWB file -----------------------------
        try:  # if ecephys module already exists
            ecephys_module = nwb.processing['ecephys']
        except:  # creates ecephys ProcessingModule
            ecephys_module = ProcessingModule(
                name='ecephys',
                description='Extracellular electrophysiology data.')
            # Add module to NWB file
            nwb.add_processing_module(ecephys_module)
            print('Created ecephys')

        # LFP: Downsampled and power line signal removed -----------------------
        if 'LFP' in nwb.processing[
                'ecephys'].data_interfaces:  # if LFP already exists
            lfp = nwb.processing['ecephys'].data_interfaces['LFP']
            lfp_ts = nwb.processing['ecephys'].data_interfaces[
                'LFP'].electrical_series['preprocessed']
        else:  # creates LFP data interface container
            lfp = LFP()

            # Data source
            lis = list(nwb.acquisition.keys())
            for i in lis:  # Check if there is ElectricalSeries in acquisition group
                if type(nwb.acquisition[i]).__name__ == 'ElectricalSeries':
                    source = nwb.acquisition[i]
            nChannels = source.data.shape[1]

            # Downsampling
            extraBins0 = 0
            fs = source.rate
            if config['Downsample'] is not None:
                print("Downsampling signals to " + str(config['Downsample']) +
                      " Hz.")
                print("Please wait, this might take around 30 minutes.")
                start = time.time()
                #zeros to pad to make signal lenght a power of 2
                nBins = source.data.shape[0]
                extraBins0 = 2**(np.ceil(np.log2(nBins)).astype('int')) - nBins
                extraZeros = np.zeros(extraBins0)
                rate = config['Downsample']
                #One channel at a time, to improve memory usage for long signals
                for ch in np.arange(nChannels):
                    #1e6 scaling helps with numerical accuracy
                    Xch = source.data[:, ch] * 1e6
                    #Make lenght a power of 2, improves performance
                    Xch = np.append(Xch, extraZeros)
                    Xch = resample(Xch, rate, fs)
                    if ch == 0:
                        X = Xch.reshape(1, -1)
                    else:
                        X = np.append(X, Xch.reshape(1, -1), axis=0)
                print(
                    'Downsampling finished in {} seconds'.format(time.time() -
                                                                 start))
            else:  # No downsample
                rate = fs
                X = source.data[:, :].T * 1e6

            # Subtract CAR
            if config['CAR'] is not None:
                print(
                    "Computing and subtracting Common Average Reference in " +
                    str(config['CAR']) + " channel blocks.")
                start = time.time()
                X = subtract_CAR(X, b_size=config['CAR'])
                print('CAR subtract time for {}: {} seconds'.format(
                    block_name,
                    time.time() - start))

            # Apply Notch filters
            if config['Notch'] is not None:
                print("Applying Notch filtering of " + str(config['Notch']) +
                      " Hz")
                #zeros to pad to make signal lenght a power of 2
                nBins = X.shape[1]
                extraBins1 = 2**(np.ceil(np.log2(nBins)).astype('int')) - nBins
                extraZeros = np.zeros(extraBins1)
                start = time.time()
                for ch in np.arange(nChannels):
                    Xch = np.append(X[ch, :], extraZeros).reshape(1, -1)
                    Xch = linenoise_notch(Xch,
                                          rate,
                                          notch_freq=config['Notch'])
                    if ch == 0:
                        X2 = Xch.reshape(1, -1)
                    else:
                        X2 = np.append(X2, Xch.reshape(1, -1), axis=0)
                print('Notch filter time for {}: {} seconds'.format(
                    block_name,
                    time.time() - start))

                X = np.copy(X2)
                del X2

            #Remove excess bins (because of zero padding on previous steps)
            excessBins = int(np.ceil(extraBins0 * rate / fs) + extraBins1)
            X = X[:, 0:-excessBins]
            X = X.astype('float32')  # signal (nChannels,nSamples)
            X /= 1e6  # Scales signals back to Volt

            # Add preprocessed downsampled signals as an electrical_series
            if config['CAR'] is None:
                car = 'None'
            else:
                car = str(config['CAR'])
            if config['Notch'] is None:
                notch = 'None'
            else:
                notch = str(config['Notch'])
            if config['Downsample'] is None:
                downs = 'No'
            else:
                downs = 'Yes'
            config_comment = 'CAR:' + car + ', Notch:' + notch + ', Downsampled:' + downs
            lfp_ts = lfp.create_electrical_series(name='preprocessed',
                                                  data=X.T,
                                                  electrodes=source.electrodes,
                                                  rate=rate,
                                                  description='',
                                                  comments=config_comment)
            ecephys_module.add_data_interface(lfp)

            # Write LFP to NWB file
            io.write(nwb)
            print('LFP saved in ' + block_path)
Beispiel #2
0
def copy_obj(obj_old, nwb_old, nwb_new):
    """ Creates a copy of obj_old. """

    # ElectricalSeries --------------------------------------------------------
    if type(obj_old) is ElectricalSeries:
        nChannels = obj_old.electrodes.table['x'].data.shape[0]
        elecs_region = nwb_new.electrodes.create_region(
            name='electrodes',
            region=np.arange(nChannels).tolist(),
            description=''
        )
        return ElectricalSeries(
            name=obj_old.name,
            data=obj_old.data[:],
            electrodes=elecs_region,
            rate=obj_old.rate,
            description=obj_old.description
        )

    # DynamicTable ------------------------------------------------------------
    if type(obj_old) is DynamicTable:
        return DynamicTable(
            name=obj_old.name,
            description=obj_old.description,
            colnames=obj_old.colnames,
            columns=obj_old.columns,
        )

    # LFP ---------------------------------------------------------------------
    if type(obj_old) is LFP:
        obj = LFP(name=obj_old.name)
        assert len(obj_old.electrical_series) == 1, (
                'Expected precisely one electrical series, got %i!' %
                len(obj_old.electrical_series))
        els = list(obj_old.electrical_series.values())[0]
        nChannels = els.data.shape[1]

        ####
        # first check for a table among the new file's data_interfaces
        if els.electrodes.table.name in nwb_new.processing[
            'ecephys'].data_interfaces:
            LFP_dynamic_table = nwb_new.processing['ecephys'].data_interfaces[
                els.electrodes.table.name]
        else:
            # othewise use the electrodes as the table
            LFP_dynamic_table = nwb_new.electrodes
        ####

        elecs_region = LFP_dynamic_table.create_region(
            name='electrodes',
            region=[i for i in range(nChannels)],
            description=els.electrodes.description
        )

        obj_ts = obj.create_electrical_series(
            name=els.name,
            comments=els.comments,
            conversion=els.conversion,
            data=els.data[:],
            description=els.description,
            electrodes=elecs_region,
            rate=els.rate,
            resolution=els.resolution,
            starting_time=els.starting_time
        )

        return obj

    # TimeSeries --------------------------------------------------------------
    if type(obj_old) is TimeSeries:
        return TimeSeries(
            name=obj_old.name,
            description=obj_old.description,
            data=obj_old.data[:],
            rate=obj_old.rate,
            resolution=obj_old.resolution,
            conversion=obj_old.conversion,
            starting_time=obj_old.starting_time,
            unit=obj_old.unit
        )

    # DecompositionSeries -----------------------------------------------------
    if type(obj_old) is DecompositionSeries:
        list_columns = []
        for item in obj_old.bands.columns:
            bp = VectorData(
                name=item.name,
                description=item.description,
                data=item.data[:]
            )
            list_columns.append(bp)
        bandsTable = DynamicTable(
            name=obj_old.bands.name,
            description=obj_old.bands.description,
            columns=list_columns,
            colnames=obj_old.bands.colnames
        )
        return DecompositionSeries(
            name=obj_old.name,
            data=obj_old.data[:],
            description=obj_old.description,
            metric=obj_old.metric,
            unit=obj_old.unit,
            rate=obj_old.rate,
            # source_timeseries=lfp,
            bands=bandsTable,
        )

    # Spectrum ----------------------------------------------------------------
    if type(obj_old) is Spectrum:
        file_elecs = nwb_new.electrodes
        nChannels = len(file_elecs['x'].data[:])
        elecs_region = file_elecs.create_region(
            name='electrodes',
            region=np.arange(nChannels).tolist(),
            description=''
        )
        return Spectrum(
            name=obj_old.name,
            frequencies=obj_old.frequencies[:],
            power=obj_old.power,
            electrodes=elecs_region
        )
Beispiel #3
0
def preprocess_raw_data(block_path, config):
    """
    Takes raw data and runs:
    1) CAR
    2) notch filters
    3) Downsampling

    Parameters
    ----------
    block_path : str
        subject file path
    config : dictionary
        'referencing' - tuple specifying electrode referencing (type, options)
            ('CAR', N_channels_per_group)
            ('CMR', N_channels_per_group)
            ('bipolar', INCLUDE_OBLIQUE_NBHD)
        'Notch' - Main frequency (Hz) for notch filters (default=60)
        'Downsample' - Downsampling frequency (Hz, default= 400)

    Returns
    -------
    Saves preprocessed signals (LFP) in the current NWB file.
    Only if containers for these data do not exist in the current file.
    """
    subj_path, block_name = os.path.split(block_path)
    block_name = os.path.splitext(block_path)[0]
    start = time.time()

    with NWBHDF5IO(block_path, 'r+', load_namespaces=True) as io:
        nwb = io.read()

        # Storage of processed signals on NWB file ----------------------------
        if 'ecephys' in nwb.processing:
            ecephys_module = nwb.processing['ecephys']
        else:  # creates ecephys ProcessingModule
            ecephys_module = ProcessingModule(
                name='ecephys',
                description='Extracellular electrophysiology data.')
            # Add module to NWB file
            nwb.add_processing_module(ecephys_module)
            print('Created ecephys')

        # LFP: Downsampled and power line signal removed ----------------------
        if 'LFP' in nwb.processing['ecephys'].data_interfaces:
            warnings.warn(
                'LFP data already exists in the nwb file. Skipping preprocessing.'
            )
        else:  # creates LFP data interface container
            lfp = LFP()

            # Data source
            source_list = [
                acq for acq in nwb.acquisition.values()
                if type(acq) == ElectricalSeries
            ]
            assert len(source_list) == 1, (
                'Not precisely one ElectricalSeries in acquisition!')
            source = source_list[0]
            nChannels = source.data.shape[1]

            # Downsampling
            if config['Downsample'] is not None:
                print("Downsampling signals to " + str(config['Downsample']) +
                      " Hz.")
                print("Please wait...")
                start = time.time()
                # Note: zero padding the signal to make the length
                # a power of 2 won't help, since resample will further pad it
                # (breaking the power of 2)
                nBins = source.data.shape[0]
                rate = config['Downsample']

                # malloc
                T = int(np.ceil(nBins * rate / source.rate))
                X = np.zeros((source.data.shape[1], T))

                # One channel at a time, to improve memory usage for long signals
                for ch in np.arange(nChannels):
                    # 1e6 scaling helps with numerical accuracy
                    Xch = source.data[:, ch] * 1e6
                    X[ch, :] = resample(Xch, rate, source.rate)
                print(
                    'Downsampling finished in {} seconds'.format(time.time() -
                                                                 start))
            else:  # No downsample
                rate = source.rate
                X = source.data[()].T * 1e6

            # re-reference the (scaled by 1e6!) data
            electrodes = source.electrodes
            if config['referencing'] is not None:
                if config['referencing'][0] == 'CAR':
                    print(
                        "Computing and subtracting Common Average Reference in "
                        + str(config['referencing'][1]) + " channel blocks.")
                    start = time.time()
                    X = subtract_CAR(X, b_size=config['referencing'][1])
                    print('CAR subtract time for {}: {} seconds'.format(
                        block_name,
                        time.time() - start))
                elif config['referencing'][0] == 'bipolar':
                    X, bipolarTable, electrodes = get_bipolar_referenced_electrodes(
                        X, electrodes, rate, grid_step=1)

                    # add data interface for the metadata for saving
                    ecephys_module.add_data_interface(bipolarTable)
                    print('bipolarElectrodes stored for saving in ' +
                          block_path)
                else:
                    print('UNRECOGNIZED REFERENCING SCHEME; ', end='')
                    print('SKIPPING REFERENCING!')

            # Apply Notch filters
            if config['Notch'] is not None:
                print("Applying notch filtering of " + str(config['Notch']) +
                      " Hz")
                # Note: zero padding the signal to make the length a power
                # of 2 won't help, since notch filtering will further pad it
                start = time.time()
                for ch in np.arange(nChannels):
                    # NOTE: apply_linenoise_notch takes a signal that is
                    # (n_timePoints, n_channels). The documentation may be wrong
                    Xch = X[ch, :].reshape(-1, 1)
                    Xch = apply_linenoise_notch(Xch, rate)
                    X[ch, :] = Xch[:, 0]
                print('Notch filter time for {}: {} seconds'.format(
                    block_name,
                    time.time() - start))

            X = X.astype('float32')  # signal (nChannels,nSamples)
            X /= 1e6  # Scales signals back to volts

            # Add preprocessed downsampled signals as an electrical_series
            referencing = 'None' if config['referencing'] is None else config[
                'referencing'][0]
            notch = 'None' if config['Notch'] is None else str(config['Notch'])
            downs = 'No' if config['Downsample'] is None else 'Yes'
            config_comment = ('referencing:' + referencing + ', Notch:' +
                              notch + ', Downsampled:' + downs)

            # create an electrical series for the LFP and store it in lfp
            lfp.create_electrical_series(name='preprocessed',
                                         data=X.T,
                                         electrodes=electrodes,
                                         rate=rate,
                                         description='',
                                         comments=config_comment)
            ecephys_module.add_data_interface(lfp)

            # Write LFP to NWB file
            io.write(nwb)
            print('LFP saved in ' + block_path)
Beispiel #4
0
def preprocess_raw_data(block_path, config):
    """
    Takes raw data and runs:
    1) CAR
    2) notch filters
    3) Downsampling

    Parameters
    ----------
    block_path : str
        subject file path
    config : dictionary
        'referencing' - tuple specifying electrode referencing (type, options)
            ('CAR', N_channels_per_group)
            ('CMR', N_channels_per_group)
            ('bipolar', INCLUDE_OBLIQUE_NBHD)
        'Notch' - Main frequency (Hz) for notch filters (default=60)
        'Downsample' - Downsampling frequency (Hz, default= 400)

    Returns
    -------
    Saves preprocessed signals (LFP) in the current NWB file.
    Only if containers for these data do not exist in the current file.
    """
    subj_path, block_name = os.path.split(block_path)
    block_name = os.path.splitext(block_path)[0]
    start = time.time()

    with NWBHDF5IO(block_path, 'r+', load_namespaces=True) as io:
        nwb = io.read()

        # Storage of processed signals on NWB file ----------------------------
        if 'ecephys' in nwb.processing:
            ecephys_module = nwb.processing['ecephys']
        else:  # creates ecephys ProcessingModule
            ecephys_module = ProcessingModule(
                name='ecephys',
                description='Extracellular electrophysiology data.')
            # Add module to NWB file
            nwb.add_processing_module(ecephys_module)
            print('Created ecephys')

        # LFP: Downsampled and power line signal removed ----------------------
        if 'LFP' in nwb.processing['ecephys'].data_interfaces:
            ######
            # What's the point of this?  Nothing is done with these vars...
            lfp = nwb.processing['ecephys'].data_interfaces['LFP']
            lfp_ts = nwb.processing['ecephys'].data_interfaces[
                'LFP'].electrical_series['preprocessed']
            ######
        else:  # creates LFP data interface container
            lfp = LFP()

            # Data source
            source_list = [
                acq for acq in nwb.acquisition.values()
                if type(acq) == ElectricalSeries
            ]
            assert len(source_list) == 1, (
                'Not precisely one ElectricalSeries in acquisition!')
            source = source_list[0]
            nChannels = source.data.shape[1]

            # Downsampling
            if config['Downsample'] is not None:
                print("Downsampling signals to " + str(config['Downsample']) +
                      " Hz.")
                print("Please wait, this might take around 30 minutes.")
                start = time.time()
                # zeros to pad to make signal length a power of 2
                nBins = source.data.shape[0]
                extraBins0 = 2**(np.ceil(np.log2(nBins)).astype('int')) - nBins
                extraZeros = np.zeros(extraBins0)
                rate = config['Downsample']

                # malloc
                T = int(np.ceil((nBins + extraBins0) * rate / source.rate))
                X = np.zeros((source.data.shape[1], T))

                # One channel at a time, to improve memory usage for long signals
                for ch in np.arange(nChannels):
                    # 1e6 scaling helps with numerical accuracy
                    Xch = source.data[:, ch] * 1e6
                    # Make length a power of 2, improves performance
                    Xch = np.append(Xch, extraZeros)
                    X[ch, :] = resample(Xch, rate, source.rate)
                print(
                    'Downsampling finished in {} seconds'.format(time.time() -
                                                                 start))
            else:  # No downsample
                extraBins0 = 0
                rate = source.rate
                X = source.data[()].T * 1e6

            # re-reference the (scaled by 1e6!) data
            electrodes = source.electrodes
            if config['referencing'] is not None:
                if config['referencing'][0] == 'CAR':
                    print(
                        "Computing and subtracting Common Average Reference in "
                        + str(config['referencing'][1]) + " channel blocks.")
                    start = time.time()
                    X = subtract_CAR(X, b_size=config['referencing'][1])
                    print('CAR subtract time for {}: {} seconds'.format(
                        block_name,
                        time.time() - start))
                elif config['referencing'][0] == 'bipolar':
                    X, bipolarTable, electrodes = get_bipolar_referenced_electrodes(
                        X, electrodes, rate, grid_step=1)

                    # add data interface for the metadata for saving
                    ecephys_module.add_data_interface(bipolarTable)
                    print('bipolarElectrodes stored for saving in ' +
                          block_path)
                else:
                    print('UNRECOGNIZED REFERENCING SCHEME; ', end='')
                    print('SKIPPING REFERENCING!')

            # Apply Notch filters
            if config['Notch'] is not None:
                print("Applying notch filtering of " + str(config['Notch']) +
                      " Hz")
                # zeros to pad to make signal lenght a power of 2
                nBins = X.shape[1]
                extraBins1 = 2**(np.ceil(np.log2(nBins)).astype('int')) - nBins
                extraZeros = np.zeros(extraBins1)
                start = time.time()
                for ch in np.arange(nChannels):
                    Xch = np.append(X[ch, :], extraZeros).reshape(1, -1)
                    Xch = linenoise_notch(Xch,
                                          rate,
                                          notch_freq=config['Notch'])
                    if ch == 0:
                        X2 = Xch.reshape(1, -1)
                    else:
                        X2 = np.append(X2, Xch.reshape(1, -1), axis=0)
                print('Notch filter time for {}: {} seconds'.format(
                    block_name,
                    time.time() - start))

                X = np.copy(X2)
                del X2
            else:
                extraBins1 = 0

            # Remove excess bins (because of zero padding on previous steps)
            excessBins = int(
                np.ceil(extraBins0 * rate / source.rate) + extraBins1)
            X = X[:, 0:-excessBins]
            X = X.astype('float32')  # signal (nChannels,nSamples)
            X /= 1e6  # Scales signals back to volts

            # Add preprocessed downsampled signals as an electrical_series
            referencing = 'None' if config['referencing'] is None else config[
                'referencing'][0]
            notch = 'None' if config['Notch'] is None else str(config['Notch'])
            downs = 'No' if config['Downsample'] is None else 'Yes'
            config_comment = ('referencing:' + referencing + ',Notch:' +
                              notch + ', Downsampled:' + downs)

            # create an electrical series for the LFP and store it in lfp
            lfp_ts = lfp.create_electrical_series(name='preprocessed',
                                                  data=X.T,
                                                  electrodes=electrodes,
                                                  rate=rate,
                                                  description='',
                                                  comments=config_comment)
            ecephys_module.add_data_interface(lfp)

            # Write LFP to NWB file
            io.write(nwb)
            print('LFP saved in ' + block_path)
def transform(block_path, filter='default', bands_vals=None):
    """
    Takes raw LFP data and does the standard Hilbert algorithm:
    1) CAR
    2) notch filters
    3) Hilbert transform on different bands

    Takes about 20 minutes to run on 1 10-min block.

    Parameters
    ----------
    block_path : str
        subject file path
    filter: str, optional
        Frequency bands to filter the signal.
        'default' for Chang lab default values (Gaussian filters)
        'custom' for user defined (Gaussian filters)
    bands_vals: 2D array, necessary only if filter='custom'
        [2,nBands] numpy array with Gaussian filter parameters, where:
        bands_vals[0,:] = filter centers [Hz]
        bands_vals[1,:] = filter sigmas [Hz]

    Returns
    -------
    Saves preprocessed signals (LFP) and spectral power (DecompositionSeries) in
    the current NWB file. Only if containers for these data do not exist in the
    file.
    """
    write_file = 1
    rate = 400.

    # Define filter parameters
    if filter == 'default':
        band_param_0 = bands.chang_lab['cfs']
        band_param_1 = bands.chang_lab['sds']
    elif filter == 'high_gamma':
        band_param_0 = bands.chang_lab['cfs'][(bands.chang_lab['cfs'] > 70)
                                              & (bands.chang_lab['cfs'] < 150)]
        band_param_1 = bands.chang_lab['sds'][(bands.chang_lab['cfs'] > 70)
                                              & (bands.chang_lab['cfs'] < 150)]
        #band_param_0 = [ bands.neuro['min_freqs'][-1] ]  #for hamming window filter
        #band_param_1 = [ bands.neuro['max_freqs'][-1] ]
        #band_param_0 = bands.chang_lab['cfs'][29:37]      #for average of gaussian filters
        #band_param_1 = bands.chang_lab['sds'][29:37]
    elif filter == 'custom':
        band_param_0 = bands_vals[0, :]
        band_param_1 = bands_vals[1, :]

    block_name = os.path.splitext(block_path)[0]

    start = time.time()

    with NWBHDF5IO(block_path, 'a') as io:
        nwb = io.read()

        # Storage of processed signals on NWB file -----------------------------
        if 'ecephys' not in nwb.modules:
            # Add module to NWB file
            nwb.create_processing_module(
                name='ecephys',
                description='Extracellular electrophysiology data.')
        ecephys_module = nwb.modules['ecephys']

        # LFP: Downsampled and power line signal removed
        if 'LFP' in nwb.modules['ecephys'].data_interfaces:
            lfp_ts = nwb.modules['ecephys'].data_interfaces[
                'LFP'].electrical_series['preprocessed']
            X = lfp_ts.data[:].T
            rate = lfp_ts.rate
        else:

            # 1e6 scaling helps with numerical accuracy
            X = nwb.acquisition['ECoG'].data[:].T * 1e6
            fs = nwb.acquisition['ECoG'].rate
            bad_elects = load_bad_electrodes(nwb)
            print('Load time for h5 {}: {} seconds'.format(
                block_name,
                time.time() - start))
            print('rates {}: {} {}'.format(block_name, rate, fs))
            if not np.allclose(rate, fs):
                assert rate < fs
                start = time.time()
                X = resample(X, rate, fs)
                print('resample time for {}: {} seconds'.format(
                    block_name,
                    time.time() - start))

            if bad_elects.sum() > 0:
                X[bad_elects] = np.nan

            # Subtract CAR
            start = time.time()
            X = subtract_CAR(X)
            print('CAR subtract time for {}: {} seconds'.format(
                block_name,
                time.time() - start))

            # Apply Notch filters
            start = time.time()
            X = linenoise_notch(X, rate)
            print('Notch filter time for {}: {} seconds'.format(
                block_name,
                time.time() - start))

            lfp = LFP()
            # Add preprocessed downsampled signals as an electrical_series
            lfp_ts = lfp.create_electrical_series(
                name='preprocessed',
                data=X.T,
                electrodes=nwb.acquisition['ECoG'].electrodes,
                rate=rate,
                description='')
            ecephys_module.add_data_interface(lfp)

        # Spectral band power
        if 'Bandpower_' + filter not in nwb.modules['ecephys'].data_interfaces:

            # Apply Hilbert transform
            X = X.astype('float32')  # signal (nChannels,nSamples)
            nChannels = X.shape[0]
            nSamples = X.shape[1]
            nBands = len(band_param_0)
            Xp = np.zeros((nBands, nChannels,
                           nSamples))  # power (nBands,nChannels,nSamples)
            X_fft_h = None
            for ii, (bp0, bp1) in enumerate(zip(band_param_0, band_param_1)):
                # if filter=='high_gamma':
                #    kernel = hamming(X, rate, bp0, bp1)
                # else:
                kernel = gaussian(X, rate, bp0, bp1)
                X_analytic, X_fft_h = hilbert_transform(X,
                                                        rate,
                                                        kernel,
                                                        phase=None,
                                                        X_fft_h=X_fft_h)
                Xp[ii] = abs(X_analytic).astype('float32')

            # Scales signals back to Volt
            X /= 1e6

            band_param_0V = VectorData(
                name='filter_param_0',
                description='frequencies for bandpass filters',
                data=band_param_0)
            band_param_1V = VectorData(
                name='filter_param_1',
                description='frequencies for bandpass filters',
                data=band_param_1)
            bandsTable = DynamicTable(
                name='bands',
                description='Series of filters used for Hilbert transform.',
                columns=[band_param_0V, band_param_1V],
                colnames=['filter_param_0', 'filter_param_1'])

            # data: (ndarray) dims: num_times * num_channels * num_bands
            Xp = np.swapaxes(Xp, 0, 2)
            decs = DecompositionSeries(
                name='Bandpower_' + filter,
                data=Xp,
                description='Band power estimated with Hilbert transform.',
                metric='power',
                unit='V**2/Hz',
                bands=bandsTable,
                rate=rate,
                source_timeseries=lfp_ts)
            ecephys_module.add_data_interface(decs)
        io.write(nwb)

        print('done', flush=True)
Beispiel #6
0
def copy_obj(obj_old, nwb_old, nwb_new):
    """ Creates a copy of obj_old. """

    # ElectricalSeries --------------------------------------------------------
    if type(obj_old) is ElectricalSeries:
        # If reference electrodes table is bipolar scheme
        if isinstance(obj_old.electrodes.table, BipolarSchemeTable):
            bst_old = obj_old.electrodes.table
            bst_old_df = bst_old.to_dataframe()
            bst_new = nwb_new.lab_meta_data['ecephys_ext'].bipolar_scheme_table

            for id, row in bst_old_df.iterrows():
                index_anodes = row['anodes'].index.tolist()
                index_cathodes = row['cathodes'].index.tolist()
                bst_new.add_row(anodes=index_anodes, cathodes=index_cathodes)
            bst_new.anodes.table = nwb_new.electrodes
            bst_new.cathodes.table = nwb_new.electrodes

            # if there are custom columns
            new_cols = list(bst_old_df.columns)
            default_cols = ['anodes', 'cathodes']
            [new_cols.remove(col) for col in default_cols]
            for col in new_cols:
                col_data = list(bst_old[col].data[:])
                bst_new.add_column(name=str(col),
                                   description=str(bst_old[col].description),
                                   data=col_data)

            elecs_region = DynamicTableRegion(name='electrodes',
                                              data=bst_old_df.index.tolist(),
                                              description='desc',
                                              table=bst_new)
        else:
            region = np.array(obj_old.electrodes.table.id[:])[
                obj_old.electrodes.data[:]].tolist()
            elecs_region = nwb_new.create_electrode_table_region(
                name='electrodes', region=region, description='')
        els = ElectricalSeries(name=obj_old.name,
                               data=obj_old.data[:],
                               electrodes=elecs_region,
                               rate=obj_old.rate,
                               description=obj_old.description)
        return els

    # DynamicTable ------------------------------------------------------------
    if type(obj_old) is DynamicTable:
        return DynamicTable(
            name=obj_old.name,
            description=obj_old.description,
            colnames=obj_old.colnames,
            columns=obj_old.columns,
        )

    # LFP ---------------------------------------------------------------------
    if type(obj_old) is LFP:
        obj = LFP(name=obj_old.name)
        assert len(obj_old.electrical_series) == 1, (
            'Expected precisely one electrical series, got %i!' %
            len(obj_old.electrical_series))
        els = list(obj_old.electrical_series.values())[0]

        ####
        # first check for a table among the new file's data_interfaces
        if els.electrodes.table.name in nwb_new.processing[
                'ecephys'].data_interfaces:
            LFP_dynamic_table = nwb_new.processing['ecephys'].data_interfaces[
                els.electrodes.table.name]
        else:
            # othewise use the electrodes as the table
            LFP_dynamic_table = nwb_new.electrodes
        ####

        region = np.array(
            els.electrodes.table.id[:])[els.electrodes.data[:]].tolist()
        elecs_region = LFP_dynamic_table.create_region(
            name='electrodes',
            region=region,
            description=els.electrodes.description)

        obj.create_electrical_series(name=els.name,
                                     comments=els.comments,
                                     conversion=els.conversion,
                                     data=els.data[:],
                                     description=els.description,
                                     electrodes=elecs_region,
                                     rate=els.rate,
                                     resolution=els.resolution,
                                     starting_time=els.starting_time)

        return obj

    # TimeSeries --------------------------------------------------------------
    if type(obj_old) is TimeSeries:
        return TimeSeries(name=obj_old.name,
                          description=obj_old.description,
                          data=obj_old.data[:],
                          rate=obj_old.rate,
                          resolution=obj_old.resolution,
                          conversion=obj_old.conversion,
                          starting_time=obj_old.starting_time,
                          unit=obj_old.unit)

    # DecompositionSeries -----------------------------------------------------
    if type(obj_old) is DecompositionSeries:
        list_columns = []
        for item in obj_old.bands.columns:
            bp = VectorData(name=item.name,
                            description=item.description,
                            data=item.data[:])
            list_columns.append(bp)
        bandsTable = DynamicTable(name=obj_old.bands.name,
                                  description=obj_old.bands.description,
                                  columns=list_columns,
                                  colnames=obj_old.bands.colnames)
        return DecompositionSeries(
            name=obj_old.name,
            data=obj_old.data[:],
            description=obj_old.description,
            metric=obj_old.metric,
            unit=obj_old.unit,
            rate=obj_old.rate,
            # source_timeseries=lfp,
            bands=bandsTable,
        )

    # Spectrum ----------------------------------------------------------------
    if type(obj_old) is Spectrum:
        file_elecs = nwb_new.electrodes
        nChannels = len(file_elecs['x'].data[:])
        elecs_region = file_elecs.create_region(
            name='electrodes',
            region=np.arange(nChannels).tolist(),
            description='')
        return Spectrum(name=obj_old.name,
                        frequencies=obj_old.frequencies[:],
                        power=obj_old.power,
                        electrodes=elecs_region)

    # Survey tables ------------------------------------------------------------
    if obj_old.neurodata_type == 'SurveyTable':
        if obj_old.name == 'nrs_survey_table':
            n_rows = len(obj_old['nrs_pain_intensity_rating'].data)
            for i in range(n_rows):
                nrs_survey_table.add_row(
                    **{c: obj_old[c][i]
                       for c in obj_old.colnames})
            return nrs_survey_table

        elif obj_old.name == 'vas_survey_table':
            n_rows = len(obj_old['vas_pain_intensity_rating'].data)
            for i in range(n_rows):
                vas_survey_table.add_row(
                    **{c: obj_old[c][i]
                       for c in obj_old.colnames})
            return vas_survey_table

        elif obj_old.name == 'mpq_survey_table':
            n_rows = len(obj_old['throbbing'].data)
            for i in range(n_rows):
                mpq_survey_table.add_row(
                    **{c: obj_old[c][i]
                       for c in obj_old.colnames})
            return mpq_survey_table