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
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
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.)
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'])
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)
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'])
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
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)
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
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
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
# 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):
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)