Пример #1
0
 def setUpContainer(self):
     """ Return a test EventWaveform to read/write """
     TestElectricalSeriesIO.make_electrode_table(self)
     region = DynamicTableRegion('electrodes', [0, 2], 'the first and third electrodes', self.table)
     sES = SpikeEventSeries('test_sES', ((1, 1, 1), (2, 2, 2)), list(range(2)), region)
     ew = EventWaveform(sES)
     return ew
Пример #2
0
 def setUpContainer(self):
     TestElectricalSeriesIO.make_electrode_table(self)
     region = DynamicTableRegion('electrodes', [0, 2], 'the first and third electrodes', self.table)
     sES = SpikeEventSeries(
         'test_sES', 'a hypothetical source', list(range(10)), list(range(10)), region)
     ew = EventWaveform('test_ew', sES)
     return ew
Пример #3
0
 def test_no_rate(self):
     table = make_electrode_table()
     region = DynamicTableRegion('electrodes', [1, 3],
                                 'the second and fourth electrodes', table)
     data = ((1, 1, 1), (2, 2, 2))
     with self.assertRaises(TypeError):
         SpikeEventSeries('test_sES', data, region, rate=1.)
Пример #4
0
    def test_init(self):
        table = make_electrode_table()
        region = DynamicTableRegion('electrodes', [0, 2],
                                    'the first and third electrodes', table)
        sES = SpikeEventSeries('test_sES', list(range(10)), list(range(10)),
                               region)

        ew = EventWaveform(sES)
        self.assertEqual(ew.spike_event_series['test_sES'], sES)
        self.assertEqual(ew['test_sES'], ew.spike_event_series['test_sES'])
Пример #5
0
 def test_init(self):
     table = make_electrode_table()
     region = DynamicTableRegion('electrodes', [1, 3], 'the second and fourth electrodes', table)
     data = np.zeros(10)
     timestamps = np.arange(10)
     sES = SpikeEventSeries('test_sES', data, timestamps, region)  # noqa: F405
     self.assertEqual(sES.name, 'test_sES')
     # self.assertListEqual(sES.data, data)
     np.testing.assert_array_equal(sES.data, data)
     np.testing.assert_array_equal(sES.timestamps, timestamps)
Пример #6
0
    def test_init(self):
        dev1 = Device('dev1')  # noqa: F405
        group = ElectrodeGroup(  # noqa: F405, F841
            'tetrode1', 'tetrode description', 'tetrode location', dev1)
        table = make_electrode_table()
        region = DynamicTableRegion('electrodes', [0, 2], 'the first and third electrodes', table)
        sES = SpikeEventSeries(  # noqa: F405
            'test_sES', list(range(10)), list(range(10)), region)

        ew = EventWaveform(sES)  # noqa: F405
        self.assertEqual(ew.spike_event_series['test_sES'], sES)
        self.assertEqual(ew['test_sES'], ew.spike_event_series['test_sES'])
Пример #7
0
 def setUpContainer(self):
     """ Return a test EventWaveform to read/write """
     TestElectricalSeriesIO.make_electrode_table(self)
     region = DynamicTableRegion(name='electrodes',
                                 data=[0, 2],
                                 description='the first and third electrodes',
                                 table=self.table)
     sES = SpikeEventSeries(name='test_sES',
                            data=((1, 1, 1), (2, 2, 2)),
                            timestamps=[0., 1.],
                            electrodes=region)
     ew = EventWaveform(sES)
     return ew
Пример #8
0
    def test_show_spike_event_series(self):
        rate = 10.0
        np.random.seed(1234)
        data_len = 1000
        ephys_data = np.random.rand(data_len * 6).reshape((data_len, 2, 3))
        ephys_timestamps = np.arange(data_len) / rate
        ses = SpikeEventSeries(
            name='test_ephys_data',
            data=ephys_data,
            timestamps=ephys_timestamps,
            electrodes=self.electrodes,
            resolution=0.001,
            comments=
            "This data was randomly generated with numpy, using 1234 as the seed",
            description="Random numbers generated with numpy.random.rand")

        assert isinstance(show_spike_event_series(ses), widgets.Widget)
Пример #9
0
def write_multiple_nwb_files(root_dir='/tmp'):

    rec_datetime = date_parser.parse('03/22/2018 15:35:22')

    block = 'Site1'

    recording_name = '{}_{}'.format('bird', block)

    nwb_file = os.path.join(root_dir, '{}.nwb'.format(recording_name))

    session_desc = "A single recording session"

    exp_desc = "An experiment."

    nf = NWBFile(recording_name,
                 session_desc,
                 'bird',
                 rec_datetime,
                 experimenter='Experi Menter',
                 lab='The Lab',
                 institution='University of Shaz',
                 experiment_description=exp_desc,
                 session_id='bird')

    hemi = 'L'

    electrode_array_name = '16 electrode microwire array on {} hemisphere'.format(
        hemi)
    electrode_array = nf.create_device(name=electrode_array_name, source='')

    # create an electrode group
    egrp_desc = """ The (x,y) locations of the electrodes refer to the distance from region midline and distance
                    from L2A, respectively, in mm.            
                """

    electrode_group = nf.create_electrode_group(
        hemi,
        source=electrode_array_name,
        description=egrp_desc,
        location='{} Hemisphere, Field L, CM, NCM'.format(hemi),
        device=electrode_array)

    # add electrodes to electrode group
    electrode_start = 0
    if hemi == 'R':
        electrode_start = 16
    electrode_numbers = list(np.arange(electrode_start, electrode_start + 16))
    for electrode_number in electrode_numbers:

        nf.add_electrode(electrode_number - 1,
                         x=np.random.randn(),
                         y=np.random.randn(),
                         z=0.0,
                         imp=0.0,
                         location='cortex',
                         filtering='none',
                         description='An electrode',
                         group=electrode_group)

    # create an electrode table region
    etable = nf.create_electrode_table_region(
        electrode_numbers,
        'All electrodes in array for hemisphere {} with LFP'.format(hemi))

    lfp_data = np.random.randn(len(electrode_numbers), 1000)
    sr = 1000.
    t = np.arange(lfp_data.shape[1]) / sr

    # add the raw LFP
    lfp_series_name = 'Multi-electrode LFP on {} hemisphere from block {}'.format(
        hemi, block)
    lfp = ElectricalSeries(
        lfp_series_name,
        electrode_array_name,
        lfp_data,
        etable,
        timestamps=t,
        resolution=1e-12,
        comments='',
        description='Low-passed LFP recorded from microwire array')

    # add the spikes and their waveforms
    electrode_number = 0
    spike_times = [0.5, 1.5, 3.5]
    waveforms = np.random.randn(3, 18)
    sort_type = 'Multi-unit'

    full_unit_name = '{} on electrode {}'.format(sort_type, electrode_number)
    spikes = SpikeEventSeries(
        full_unit_name,
        lfp_series_name,
        waveforms,
        spike_times,
        etable,
        resolution=1e-12,
        conversion=1e6,
        comments='',
        description='',
    )

    print('\tAdding spikes acquisition: {} ({}), waveforms.shape={}'.format(
        full_unit_name, sort_type, str(waveforms.shape)))

    # adding the LFP is fine
    nf.add_acquisition(lfp)

    ########################################################
    # adding even a single spike event series causes the error
    ########################################################
    nf.add_acquisition(spikes)

    print('Writing to {}'.format(nwb_file))
    with NWBHDF5IO(nwb_file, mode='w') as io:
        io.write(nf)

    del nf
Пример #10
0
def AddPlexonSpikeDataToNWB(plx_file_name,
                            nwb_file_name='',
                            nwb_file=None,
                            elec_ids=None,
                            verbose=False,
                            add_units=False,
                            unit_info=None):
    """
        Copies filtered LFP data from the specified Blackrock file to Neurodata Without Borders (NWB) file.
        User must specify the file with the LFP data (at desired sampling rate and with desired filtering).
        Multiple calls can be used to load multiple LFP data from multiple files.

        Typically, the NWB file will already be initialized by calling InitializeNWBFromBlackrock.
        Multiple electrodes can be added at once or with separate calls to this function.

        :param plx_file_name: {str} full path of Plexon file to convert to NWB. If empty, will open dialog.
        :param nwb_file_name: [optional] {str} full path of NWB file to export to. Default is to change blackrock extension to 'nwb'
        :param elec_ids: [optional] {list} List of electrode IDs to copy over. If empty, all are copied
        :param verbose: [optional] {bool} whether to print updates while converting. Default is false.
        :param add_units: [optional] {bool} whether to add waveform labels as units in NWB file. Default is false.
        :param unit_info: [optional] {list} extra information about units
        :return: {str} filename of NWB file (empty if error)
        """
    # Check to see if user specified a Blackrock filename
    if not plx_file_name:  # no file name passed
        # Ask user to specify a file
        if 'app' not in locals():
            app = QApplication([])
        plx_file_name = QFileDialog.getOpenFileName(
            QFileDialog(), "Select File", getcwd(), "Plexon Data File (*.plx)")
        plx_file_name = plx_file_name[0]

    # Check to see if an open NWB file was passed, if so use that
    nwb_io = None
    if nwb_file is None:
        # Check to see if valid nwb_file_name is passed
        if not nwb_file_name:
            nwb_file_name = path.splitext(plx_file_name)[0] + '.nwb'
        if verbose: print("Writing to NWB data file %s" % (nwb_file_name))

        # Initialize the NWB file
        try:
            if not path.isfile(nwb_file_name):
                # Initialize NWB file
                if verbose:
                    print("NWB file doesn't exist. Creating new one: %s..." %
                          (nwb_file_name))
                raise ValueError("NWB file doesn't exist.")
                # InitializeNWBFromPlexon(plx_file_name, nwb_file_name, verbose=verbose) # TODO: write other functions for Plexon

            # Append to existing file
            if verbose:
                print("Opening NWB file %s..." % (nwb_file_name), end='')
            nwb_file_append = True
            nwb_io = NWBHDF5IO(nwb_file_name, mode='a')
            nwb_file = nwb_io.read()
            if verbose: print("done.")
        except:  # catch *all* exceptions
            e = sys.exc_info()[0]
            if nwb_io: nwb_io.close()
            raise FileExistsError("Couldn't open NWB file. Error: %s" % e)

    # Read in data from Plexon file
    try:
        # Requires the raw data to be imported
        if verbose: print("Reading data from PLX file: %s" % (plx_file_name))
        plx_file = neo.io.PlexonIO(plx_file_name)
    except:  # catch *all* exceptions
        e = sys.exc_info()[0]
        warnings.warn("Couldn't open Plexon file. Error: %s" % e, UserWarning)
        return ""

    # Validate the elec_ids list
    if not elec_ids:
        # Grab all of the data
        elec_ids = []
        for i, head in enumerate(plx_file.header['signal_channels']):
            elec_ids.append(head[1])

    # Create processing module for saving ephys data
    if 'ecephys' not in nwb_file.processing.keys():
        if verbose:
            print(
                "Processing module for extracellular electrophysiology (ecephys) does not exist. Creating."
            )
        ecephys_module = ProcessingModule(
            name='ecephys',
            description="Processing module for recordings from %s." %
            (path.split(plx_file_name)[1]))
        nwb_file.add_processing_module(ecephys_module)

    # Create data interface for the spike waveforms
    event_waveform_name = "Spike waveforms from %s" % (
        path.split(plx_file_name)[1])
    if event_waveform_name not in nwb_file.processing[
            'ecephys'].data_interfaces.keys():
        cur_eventwaveform = EventWaveform(name="Spike waveforms from %s" %
                                          (path.split(plx_file_name)[1]))
        if verbose:
            print("Created EventWaveform data interface (%s)" %
                  (cur_eventwaveform.name))
    else:
        cur_eventwaveform = nwb_file.processing['ecephys'].data_interfaces[
            event_waveform_name]

    # Loop through and save spiking data for each electrode --  we are doing this separately for each electrode so that
    # we can save information about the threshold used for each channel in the description.
    if verbose: print("Writing spike waveforms data to NWB.")
    for cur_elec_ind, cur_elec in enumerate(elec_ids):
        # Find indices in the extended header information
        try:
            cur_chan_ind = plx_file.channel_id_to_index([cur_elec])
        except:  # catch *all* exceptions
            print(
                "Couldn't find specified electrode %d in NSx file. Skipping." %
                (cur_elec))
            continue
        cur_chan_ind = cur_chan_ind[0]
        if verbose:
            print("\tWriting spike waveforms for electrode %d." % (cur_elec))

        # Create electrode table
        cur_elec_table_ind = 0
        while (cur_elec_table_ind < len(nwb_file.electrodes)) and (
                nwb_file.electrodes[cur_elec_table_ind, 0] != cur_elec):
            cur_elec_table_ind = cur_elec_table_ind + 1
        if cur_elec_table_ind >= len(nwb_file.electrodes):
            raise ValueError("Couldn't find electrode %d in NWB file list." %
                             (cur_elec))
        electrode_table_region = nwb_file.create_electrode_table_region(
            [cur_elec_table_ind], "Electrode %s" % (cur_elec))

        # Find units from this electrode
        unit_index_list = []
        for i, cur_unit_channel in enumerate(plx_file.header['unit_channels']):
            if cur_unit_channel[0] == plx_file.header['signal_channels'][
                    cur_chan_ind][0]:
                unit_index_list.append(i)
        if not unit_index_list:
            print("\tCan't find any units associated with electrode %d." %
                  (cur_elec))
            continue

        # Grab all of the waveforms
        wf = np.array(
            plx_file.get_spike_raw_waveforms(unit_index=unit_index_list[0]))
        ts = np.array(
            plx_file.get_spike_timestamps(unit_index=unit_index_list[0]))
        # Check the length of these waveforms against what already exists
        spk_wf_len = wf.shape[1]
        for i in np.arange(1, len(unit_index_list)):
            ts = np.concatenate([
                ts,
                np.array(
                    plx_file.get_spike_timestamps(
                        unit_index=unit_index_list[i]))
            ],
                                axis=0)
            cur_wf = np.array(
                plx_file.get_spike_raw_waveforms(
                    unit_index=unit_index_list[i]))
            # Check the length of the waveforms against the limit for this interface
            if cur_wf.shape[1] > spk_wf_len:
                # Clip outer edges of waveform
                if verbose:
                    warnings.warn(
                        "Unit index %d on electrode %d has too many waveform points (%d). Clipping to %d."
                        % (i, cur_elec, wf.shape[1], spk_wf_len))
                wf_clip = int(np.round((cur_wf.shape[1] - spk_wf_len) / 2) - 1)
                cur_wf = cur_wf[:, wf_clip:(wf_clip + spk_wf_len)]
            elif cur_wf.shape[1] < spk_wf_len:
                # Pad with zeros to get to correct size
                if verbose:
                    warnings.warn(
                        "Unit index %d on electrode %d has too many waveform points (%d). Clipping to %d."
                        % (i, cur_elec, wf.shape[1], spk_wf_len))
                temp_wf = np.zeros((cur_wf.shape[0], spk_wf_len))
                wf_pad = int(np.round((spk_wf_len - cur_wf.shape[1]) / 2) - 1)
                temp_wf[:, wf_pad:(wf_pad + wf.shape[1])] = cur_wf
                cur_wf = temp_wf
            # Add current waveforms to overall matrix
            wf = np.concatenate([wf, cur_wf], axis=0)
        wf = wf.squeeze()

        # Write raw data for the electrode
        if verbose:
            print("\tAdding waveforms (%d by %d) to NWB..." % (wf.shape),
                  end='')
        # Create spike event series
        spike_wf = SpikeEventSeries(
            'Spike waveforms for Channel %s' % (cur_elec),
            wf,
            ts,
            electrode_table_region,
            conversion=plx_file.header['signal_channels'][cur_chan_ind]
            [5],  # pulled from header
            description="Spikes waveforms from channel %d in PLX file." %
            (cur_elec))
        # Add to our event waveform module
        cur_eventwaveform.add_spike_event_series(spike_wf)
        if verbose: print("done")

    # Add spike waveform container to NWB file
    if verbose: print("\tAdding spike waveform data to processing module.")
    nwb_file.processing['ecephys'].add(cur_eventwaveform)

    if add_units:
        # Check to see if we have any units already
        if (nwb_file.units is not None) and ('waveform_mean'
                                             in nwb_file.units.colnames):
            unit_wf_len = nwb_file.units['waveform_mean'].data[0].shape[-1]
        else:
            unit_wf_len = np.NaN

        # Check to see if user rating column already exists
        if (not nwb_file.units) or ('UserRating'
                                    not in nwb_file.units.colnames):
            nwb_file.add_unit_column(
                'UserRating', 'Quality of isolation, as rated by user.')
        if (not nwb_file.units) or ('UserComment'
                                    not in nwb_file.units.colnames):
            nwb_file.add_unit_column('UserComment',
                                     'Comments on spike isolation from user.')

        # Add units to the NWB file
        if verbose: print("Adding units to NWB file.")
        for cur_elec_ind, cur_elec in enumerate(elec_ids):
            # Determine the electrode index in NWB file for this electrode id
            cur_elec_table_ind = 0
            while (cur_elec_table_ind < len(nwb_file.electrodes)) and (
                    nwb_file.electrodes[cur_elec_table_ind, 0] != cur_elec):
                cur_elec_table_ind = cur_elec_table_ind + 1
            if cur_elec_table_ind >= len(nwb_file.electrodes):
                raise ValueError(
                    "Couldn't find electrode %d in NWB file list." %
                    (cur_elec))

            # Get list of units for this electrode
            unit_index_list = []
            for i, cur_unit_channel in enumerate(
                    plx_file.header['unit_channels']):
                if cur_unit_channel[0] == plx_file.header['signal_channels'][
                        cur_chan_ind][0]:
                    unit_num = int(cur_unit_channel[1].split('#')[-1])
                    unit_index_list.append(unit_num)
            if not unit_index_list:
                print("\tCan't find any units associated with electrode %d." %
                      (cur_elec))
                continue
            if verbose:
                print("\tElectrode %d, found %d unique units (%s)." %
                      (cur_elec, len(unit_index_list), unit_index_list))

            # Loop through units
            for cur_unit_index, cur_unit in enumerate(unit_index_list):
                #Get wf and ts for this unit
                wf = plx_file.get_spike_raw_waveforms(
                    unit_index=cur_unit_index)
                wf = wf.squeeze()
                ts = plx_file.get_spike_timestamps(unit_index=cur_unit_index)

                # Check the length of the waveforms against the limit for the file
                if np.isnan(unit_wf_len):
                    # Units haven't been written yet, we get to define the length of waveforms
                    unit_wf_len = wf.shape[1]
                if wf.shape[1] > unit_wf_len:
                    # Clip outer edges of waveform
                    if verbose:
                        warnings.warn(
                            "Unit %d on electrode %d has too many waveform points (%d). Clipping to %d."
                            % (cur_unit, cur_elec, wf.shape[1], unit_wf_len))
                    wf_clip = int(
                        np.round((wf.shape[1] - unit_wf_len) / 2) - 1)
                    wf = wf[:, wf_clip:(wf_clip + unit_wf_len)]
                elif wf.shape[1] < unit_wf_len:
                    # Pad with zeros to get to correct size
                    if verbose:
                        warnings.warn(
                            "Unit %d on electrode %d has too few waveform points (%d). Clipping to %d."
                            % (cur_unit, cur_elec, wf.shape[1], unit_wf_len))
                    temp_wf = np.zeros((wf.shape[0], unit_wf_len))
                    wf_pad = int(np.round((unit_wf_len - wf.shape[1]) / 2) - 1)
                    temp_wf[:, wf_pad:(wf_pad + wf.shape[1])] = wf
                    wf = temp_wf

                # Interval over which this unit was observed
                obs_intervals = np.zeros((1, 2))
                obs_intervals[0, 0] = np.min(ts)
                obs_intervals[0, 1] = np.max(ts)

                # Find the index in the unit_info list
                if not unit_info:
                    cur_unit_info = {'UserRating': -1, 'UserComment': ''}
                else:
                    unit_dict = {'electrode': cur_elec, 'unit': cur_unit}
                    i = 0
                    while (i < len(unit_info)) and not all(
                            item in unit_info[i].items()
                            for item in unit_dict.items()):
                        i = i + 1
                    if i >= len(unit_info):
                        cur_unit_info = {'UserRating': -1, 'UserComment': ''}
                    else:
                        cur_unit_info = unit_info[i]
                #Make sure UserComment is a string
                if not isinstance(cur_unit_info['UserComment'], str):
                    warnings.warn(
                        'UserComment for Unit %d on Electrode %d was not valid. Setting to empty.'
                        % (cur_unit, cur_elec))
                    cur_unit_info['UserComment'] = ''
                # Make sure UserComment is a string
                if not isinstance(cur_unit_info['UserRating'], int):
                    warnings.warn(
                        'UserRating for Unit %d on Electrode %d was not valid. Setting to empty.'
                        % (cur_unit, cur_elec))
                    cur_unit_info['UserRating'] = -1

                # Add to NWB file
                nwb_file.add_unit(spike_times=ts,
                                  obs_intervals=obs_intervals,
                                  electrodes=[cur_elec_table_ind],
                                  waveform_mean=np.nanmean(wf, axis=0),
                                  waveform_sd=np.nanstd(wf, axis=0),
                                  UserRating=cur_unit_info['UserRating'],
                                  UserComment=cur_unit_info['UserComment'])
                if verbose: print("\t\tAdded class %s." % (cur_unit))

    # Did we open a file?  Then close it...
    if nwb_io != None:
        # Write the file
        if verbose: print("\tWriting NWB file and closing.")
        nwb_io.write(nwb_file)
        nwb_io.close()
        return nwb_file_name
    else:
        return nwb_file
Пример #11
0
def write_spike_waveforms_single_shank(
    nwbfile: NWBFile,
    session_path: str,
    shankn: int,
    spikes_nsamples: int,
    nchan_on_shank: int,
    stub_test: bool = False,
    compression: Optional[str] = "gzip",
):
    """Write spike waveforms to NWBFile.

    Parameters
    ----------
    nwbfile: pynwb.NWBFile
    session_path: str
    shankn: int
    spikes_nsamples: int
    nchan_on_shank: int
    stub_test: bool, optional
        default: False
    compression: str (optional)
        default: 'gzip'
    """
    session_name = os.path.split(session_path)[1]
    spk_file = os.path.join(session_path,
                            session_name + ".spk.{}".format(shankn))

    assert os.path.isfile(
        spk_file
    ), "No .spk.{} file found at the path location!" "Unable to retrieve spike waveforms.".format(
        shankn)

    group = nwbfile.electrode_groups["shank{}".format(shankn)]
    elec_idx = list(
        np.where(np.array(nwbfile.ec_electrodes["group"]) == group)[0])
    table_region = nwbfile.create_electrode_table_region(
        elec_idx, group.name + " region")

    if stub_test:
        n_stub_spikes = 50
        spks = np.fromfile(
            spk_file,
            dtype=np.int16,
            count=n_stub_spikes * spikes_nsamples * nchan_on_shank,
        ).reshape(n_stub_spikes, spikes_nsamples, nchan_on_shank)
        spk_times = read_spike_times(session_path, shankn)[:n_stub_spikes]
    else:
        spks = np.fromfile(spk_file,
                           dtype=np.int16).reshape(-1, spikes_nsamples,
                                                   nchan_on_shank)
        spk_times = read_spike_times(session_path, shankn)

    if compression:
        data = H5DataIO(spks, compression=compression)
    else:
        data = spks

    spike_event_series = SpikeEventSeries(
        name="SpikeWaveforms{}".format(shankn),
        data=data,
        timestamps=spk_times,
        conversion=1e-6,
        electrodes=table_region,
    )
    check_module(nwbfile, "ecephys").add_data_interface(spike_event_series)
def convert_from_old_neo(old_file, bird_name, electrode_df):
    root_dir, fname = os.path.split(old_file)

    rec_date = bird_info[bird_name]['recording_date']
    rec_start = bird_info[bird_name]['recording_start']
    rec_end = bird_info[bird_name]['recording_end']

    rec_datetime = date_parser.parse('{} {}'.format(rec_date, rec_start))

    # create an electrode array, electrode groups, and electrode table for the electrodes
    i = electrode_df.bird == bird_name
    edf = electrode_df[i]

    for block, gdf in edf.groupby(['block']):

        lfp_series = dict()
        spike_series = dict()

        print('*************** Processing block {}'.format(block))

        if bird_name == 'WhiWhi4522M' and block == 'Site1':
            continue

        # get the LFP and spike data for the block
        hf = h5py.File(old_file, 'r')
        block_data = read_old_neo(hf, block)
        hf.close()

        recording_name = '{}_{}_{}'.format(bird_name, block,
                                           block_data['protocol'])

        nwb_file = os.path.join(root_dir, '{}.nwb'.format(recording_name))

        session_desc = """ A single recording session, roughly one hour long, at a single depth, in a series of
                           recordings from the auditory "cortex" of zebra finch {}. Block {}, stimulus protocol
                           {}.        
                       """.format(bird_name, block, block_data['protocol'])

        exp_desc = """ A randomly interleaved mixture of Zebra finch vocalizations, songs, and modulation limited
                       noise played to a Zebra finch under urethane anaesthesia in an acute experiment. Two 16 microwire
                       electrode arrays were implanted, one in each hemisphere. Experiments  designed and performed by
                       Julie Elie, vocal repertoire recorded by Julie Elie. Data converted to NWB by Mike Schachter. For
                       a full description of methods, please consult and also cite the following publications:

                       Elie, J. E., & Theunissen, F. E. (2015). Meaning in the avian auditory cortex: neural
                       representation of communication calls. European Journal of Neuroscience, 41(5), 546-567.

                       Elie, J. E., & Theunissen, F. E. (2016). The vocal repertoire of the domesticated zebra finch:
                       a data-driven approach to decipher the information-bearing acoustic features of communication
                       signals. Animal cognition, 19(2), 285-315. 
                   """

        nf = NWBFile(recording_name,
                     session_desc,
                     bird_name,
                     rec_datetime,
                     experimenter='Julie Elie',
                     lab='Theunissen Lab',
                     institution='UC Berkeley',
                     experiment_description=exp_desc,
                     session_id=bird_name)

        # create the electrodes and electrode tables
        for hemi, ggdf in gdf.groupby(['hemisphere']):

            electrode_array_name = '16 electrode microwire array on {} hemisphere'.format(
                hemi)
            electrode_array = nf.create_device(name=electrode_array_name,
                                               source='')

            # create an electrode group
            egrp_desc = """ The (x,y) locations of the electrodes refer to the distance from region midline and distance
                            from L2A, respectively, in mm.            
                        """

            electrode_group = nf.create_electrode_group(
                hemi,
                source=electrode_array_name,
                description=egrp_desc,
                location='{} Hemisphere, Field L, CM, NCM'.format(hemi),
                device=electrode_array)

            # add electrodes to electrode group
            for row_idx, row in ggdf.iterrows():
                electrode_number = row['electrode'] - 1
                dist_l2a = row['dist_l2a']
                dist_midline = row['dist_midline']
                region = row['region']

                if bird_name == 'GreBlu9508M':
                    dist_l2a *= 4  # correct for the error in the original data

                nf.add_electrode(electrode_number,
                                 x=dist_midline,
                                 y=dist_l2a,
                                 z=0.0,
                                 imp=0.0,
                                 location=region,
                                 filtering='none',
                                 description='Row {}, Column {}'.format(
                                     row['row'], row['col']),
                                 group=electrode_group)

            # create an electrode table region
            electrode_numbers = list(
                np.array(sorted(ggdf.electrode.unique())) - 1)
            print('electrode_numbers=', electrode_numbers)
            etable = nf.create_electrode_table_region(
                electrode_numbers,
                'All electrodes in array for hemisphere {} with LFP'.format(
                    hemi))

            lfp_data = np.array([
                block_data['electrodes'][e + 1]['lfp']
                for e in electrode_numbers
            ])
            sr = block_data['sample_rate']
            t = np.arange(lfp_data.shape[1]) / sr

            # add the raw LFP
            lfp_series_name = 'Multi-electrode LFP on {} hemisphere from block {}'.format(
                hemi, block)
            lfp = ElectricalSeries(
                lfp_series_name,
                electrode_array_name,
                lfp_data,
                etable,
                timestamps=t,
                resolution=1e-12,
                comments='',
                description='Low-passed LFP recorded from microwire array')

            lfp_series[hemi] = lfp

            # add the spikes and their waveforms
            for row_idx, row in ggdf.iterrows():

                # electrode_number ranges from 1-32, same as the keys in block_data['electrodes']
                electrode_number = row['electrode']

                for k, unit_data in enumerate(
                        block_data['electrodes'][electrode_number]['units']):
                    spike_times = unit_data['spike_times']
                    waveforms = unit_data['waveforms']
                    unit_name = unit_data['name']
                    e_num = unit_data['electrode']

                    print('{} electrode_number={}({}), e_num={}, k={}'.format(
                        unit_name, electrode_number, electrode_number - 1,
                        e_num, k))

                    assert e_num == electrode_number

                    if unit_name.startswith('RF Sort'):
                        xarr = unit_name.split(' ')
                        unit_num = xarr[-1]
                        sort_type = 'Spike-sorted unit {}'.format(unit_num)

                    elif unit_name.startswith('Chan'):
                        if 'Code0' not in unit_name:
                            print(
                                'Skipping multi-unit channel with non-zero sort code'
                            )
                            continue
                        sort_type = 'Multi-unit'
                    else:
                        raise Exception(
                            'Unknown unit name: {}'.format(unit_name))

                    full_unit_name = '{} on block {} and electrode {}'.format(
                        sort_type, block, e_num - 1)
                    spikes = SpikeEventSeries(
                        full_unit_name,
                        lfp_series_name,
                        waveforms,
                        spike_times,
                        etable,
                        resolution=1e-12,
                        conversion=1e6,
                        comments='',
                        description='',
                    )

                    print(
                        '\tAdding spikes acquisition: {} ({}), waveforms.shape={}'
                        .format(full_unit_name, sort_type,
                                str(waveforms.shape)))

                    spike_series[(hemi, electrode_number, unit_name)] = spikes

        all_series = list()
        all_series.extend(lfp_series.values())
        all_series.extend(spike_series.values())
        print('len(all_series)=', len(all_series))
        nf.add_acquisition(all_series)

        # create trials for each stim presentation
        nf.add_trial_column('stim_id',
                            'The ID of the sound played during the trial.')
        nf.add_trial_column(
            'trial', 'The number of times this stimulus has been presented.')
        for stim_epoch_data in block_data['epochs']:
            stim_id = stim_epoch_data['stim_id']
            trial_num = stim_epoch_data['trial']
            stime = stim_epoch_data['start']
            etime = stim_epoch_data['end']
            nf.add_trial({
                'start': stime,
                'end': etime,
                'stim_id': stim_id,
                'trial': trial_num
            })

        print('Writing to {}'.format(nwb_file))
        with NWBHDF5IO(nwb_file, mode='w') as io:
            io.write(nf)

        del nf
Пример #13
0
    # For each unit k
    for k in np.arange(3):
        
        data = f_info['wf'][i,k]
        timestamps = np.ravel(f_info['spikes'][i,k])
        
        # For units with no spike, the data array shape is saved as (48,0).
        # So, we transpose it
        if timestamps.shape==(0,):
            data = data.T
        
        # Create SpikeEventSeries container
        ephys_ts_M1 = SpikeEventSeries(name='M1 Spike Events electrode {0} and unit {1}'.format(i,k),
                                    data=data,
                                    timestamps=timestamps,
                                    electrodes=electrode_table_region_M1,
                                    resolution=4.096e-05,
                                    conversion=1e-6,
                                    description=description,
                                    comments=comments)
        
        # Store spike waveform data
        nwb.add_acquisition(ephys_ts_M1)


# S1

description = 'Spike event waveform "snippets" of S1. Each waveform corresponds to a timestamp in "spikes".'
comments = 'Waveform samples are in microvolts.'

# For each electrode i
for i in np.arange(96,192):
Пример #14
0
def write_spike_waveforms(nwbfile,
                          session_path,
                          shankn,
                          stub=False,
                          compression='gzip'):
    """

    Parameters
    ----------
    nwbfile: pynwb.NWBFiles
    session_path: str
    shankn: int
    stub: bool, optional
        default: False
    compression: str (optional)

    Returns
    -------

    """

    session_name = os.path.split(session_path)[1]
    xml_filepath = os.path.join(session_path, session_name + '.xml')

    group = nwbfile.electrode_groups['shank' + str(shankn)]
    elec_idx = list(
        np.where(np.array(nwbfile.ec_electrodes['group']) == group)[0])
    table_region = nwbfile.create_electrode_table_region(
        elec_idx, group.name + ' region')

    nchan = len(elec_idx)
    soup = load_xml(xml_filepath)
    nsamps = int(soup.spikes.nSamples.string)

    if stub:
        spks = np.random.randn(10, nsamps, nchan)
        spike_times = np.arange(10)
    else:
        spk_file = os.path.join(session_path,
                                session_name + '.spk.' + str(shankn))
        if not os.path.isfile(spk_file):
            print('spike waveforms for shank{} not found'.format(shankn))
            return
        spks = np.fromfile(spk_file, dtype=np.int16).reshape(-1, nsamps, nchan)
        spike_times = read_spike_times(session_path, shankn)
    if compression:
        data = H5DataIO(spks, compression=compression)
    else:
        data = spks

    spike_event_series = SpikeEventSeries(name='SpikeEventSeries' +
                                          str(shankn),
                                          data=data,
                                          timestamps=spike_times,
                                          electrodes=table_region)

    #if 'shank' + str(shankn) in nwbfile.electrode_groups:
    #    nwbfile.electrode_groups['shank' + str(shankn)].event_waveform = EventWaveform(
    #        spike_event_series=spike_event_series)

    check_module(nwbfile, 'ecephys').add_data_interface(spike_event_series)