def populate_recordingchannelgroup( self, with_waveforms=True, with_features=False, ): """ Populate neo.RecordingChannelGroup with new sorting. It delete previous Unit and SpikeTrain and populate with new ones. Arguments: * with_waveforms : True (default) waveforms of spikes are saved included in SpikeTrain * with_features : True (default) feature_waveforms of spikes are saved included in SpikeTrain """ rcg = self.rcg bl = rcg.block for u, unit in enumerate(rcg.units): for s, seg in enumerate(bl.segments): for sptr in unit.spiketrains: if list_contains(seg.spiketrains, sptr): list_remove(seg.spiketrains, sptr) rcg.units = [] self.refresh_cluster_names() self.refresh_colors() for c, name in self.cluster_names.items(): #~ color = '#' #~ for e in self.cluster_colors[c]: #~ colorhex = str(hex(int(e*255))).replace('0x','') #~ if len(colorhex) == 1: #~ colorhex = '0'+colorhex #~ color += colorhex color = color_utils.mpl_to_html(self.cluster_colors[c]) unit = neo.Unit(name=name, color=color) rcg.units.append(unit) for s, seg in enumerate(bl.segments): sptr = neo.SpikeTrain(self.get_spike_times(s, c, units='s'), t_start=self.seg_t_start[s], t_stop=self.seg_t_stop[s], color=color) if with_waveforms: sptr.waveforms = self.get_spike_waveforms( s, c) * self.wf_units sptr.sampling_rate = self.wf_sampling_rate sptr.left_sweep = (self.left_sweep / self.wf_sampling_rate).rescale('s') if with_features: sptr.annotations[ 'waveform_features'] = self.get_spike_features(s, c) sptr.sort() unit.spiketrains.append(sptr) seg.spiketrains.append(sptr) sptr.unit = unit sptr.segment = seg return rcg
def generate_block(n_segments=3, n_channels=8, n_units=3, data_samples=1000, feature_samples=100): """ Generate a block with a single recording channel group and a number of segments, recording channels and units with associated analog signals and spike trains. """ feature_len = feature_samples / data_samples # Create container and grouping objects segments = [neo.Segment(index=i) for i in range(n_segments)] chx = neo.ChannelIndex(index=1, name='T0') for i in range(n_channels): rc = neo.RecordingChannel(name='C%d' % i, index=i) rc.channelindexes = [chx] chx.recordingchannels.append(rc) units = [neo.Unit('U%d' % i) for i in range(n_units)] chx.units = units block = neo.Block() block.segments = segments block.channel_indexes = [chx] # Create synthetic data for seg in segments: feature_pos = np.random.randint(0, data_samples - feature_samples) # Analog signals: Noise with a single sinewave feature wave = 3 * np.sin(np.linspace(0, 2 * np.pi, feature_samples)) for rc in chx.recordingchannels: sig = np.random.randn(data_samples) sig[feature_pos:feature_pos + feature_samples] += wave signal = neo.AnalogSignal(sig * pq.mV, sampling_rate=1 * pq.kHz) seg.analogsignals.append(signal) rc.analogsignals.append(signal) # Spike trains: Random spike times with elevated rate in short period feature_time = feature_pos / data_samples for u in units: random_spikes = np.random.rand(20) feature_spikes = np.random.rand(5) * feature_len + feature_time spikes = np.hstack([random_spikes, feature_spikes]) train = neo.SpikeTrain(spikes * pq.s, 1 * pq.s) seg.spiketrains.append(train) u.spiketrains.append(train) block.create_many_to_one_relationship() return block
def create_hierarchy(cls, many_to_many): b = neo.Block() for ns in range(cls.SEGMENTS): b.segments.append(neo.Segment()) channels = [] if many_to_many: channels = [ neo.RecordingChannel(name='Shared %d' % i, index=i + cls.CHANNELS) for i in range(cls.CHANNELS / 2) ] for ng in range(cls.CHANNEL_GROUPS): rcg = neo.RecordingChannelGroup() for nu in range(cls.UNITS): unit = neo.Unit() for ns in range(cls.SEGMENTS): spike = neo.Spike(0 * pq.s) unit.spikes.append(spike) b.segments[ns].spikes.append(spike) st = neo.SpikeTrain([] * pq.s, 0 * pq.s) unit.spiketrains.append(st) b.segments[ns].spiketrains.append(st) rcg.units.append(unit) if not many_to_many: for nc in range(cls.CHANNELS): rc = neo.RecordingChannel(name='Single %d' % nc, index=nc) rc.recordingchannelgroups.append(rcg) rcg.recordingchannels.append(rc) else: for nc in range(cls.CHANNELS): if nc % 2 == 0: rc = neo.RecordingChannel(name='Single %d' % (nc / 2), index=nc / 2) else: rc = channels[nc / 2] rc.recordingchannelgroups.append(rcg) rcg.recordingchannels.append(rc) rcg.channel_indexes = sp.array( [c.index for c in rcg.recordingchannels]) rcg.channel_names = sp.array( [c.name for c in rcg.recordingchannels]) b.recordingchannelgroups.append(rcg) try: neo.io.tools.create_many_to_one_relationship(b) except AttributeError: b.create_many_to_one_relationship() return b
def get_unit(unit_id, channel_idx): unit = None for u in channel_idx.units: if u.annotations['unit_id'] == unit_id: unit = u if unit is None: unit = neo.Unit(unit_id=unit_id, channel_id=channel_idx.annotations['channel_id']) channel_idx.units.append(unit) return unit
def start(self, current, selections): current.progress.begin('Creating correlogram') if self.data_source == 0: d = current.spike_trains_by_unit() else: # Prepare dictionary for cross_correlogram(): # One entry of spike trains for each selection d = {} for s in selections: d[neo.Unit(s.name)] = s.spike_trains() plot.cross_correlogram( d, self.bin_size * pq.ms, self.cut_off * pq.ms, border_correction=self.border_correction, per_second=self.count_per == 0, square=self.square, progress=current.progress)
def start(self, current, selections): current.progress.begin('Creating Interspike Interval Histogram') if self.data_source == 0: d = current.spike_trains_by_unit() else: # Prepare dictionary for isi(): # One entry of spike trains for each selection d = {} for s in selections: d[neo.Unit(s.name)] = s.spike_trains() current.progress.done() plot.isi(d, self.bin_size * pq.ms, self.cut_off * pq.ms, self.diagram_type == 0, time_unit=pq.ms)
def test_remove_spiketrain(self): unit = neo.Unit() segment = neo.Segment() s = neo.Spike(0 * pq.s) unit.spikes.append(s) segment.spikes.append(s) s.unit = unit s.segment = segment st = neo.SpikeTrain([] * pq.s, 0 * pq.s) unit.spiketrains.append(st) segment.spiketrains.append(st) st.unit = unit st.segment = segment tools.remove_from_hierarchy(st) self.assertTrue(s in unit.spikes) self.assertTrue(s in segment.spikes) self.assertFalse(st in unit.spiketrains) self.assertFalse(st in segment.spiketrains)
def start(self, current, selections): current.progress.begin('Creating spike density estimation') # Prepare quantities start = float(self.start_time) * self.unit stop = None if self.stop_enabled: stop = float(self.stop) * self.unit kernel_size = float(self.kernel_size) * self.unit optimize_steps = 0 if self.optimize_enabled: optimize_steps = self.optimize_steps minimum_kernel = self.minimum_kernel * self.unit maximum_kernel = self.maximum_kernel * self.unit # Load data events = None if self.data_source == 0: trains = current.spike_trains_by_unit() if self.align_enabled: events = current.labeled_events(self.align) else: # Prepare dictionaries for psth(): # One entry of spike trains for each selection, # an event for each segment occuring in any selection trains = {} if self.align_enabled: events = {} for s in selections: trains[neo.Unit(s.name)] = s.spike_trains() if self.align_enabled: events.update(s.labeled_events(self.align)) if events: for s in events: # Align on first event in each segment events[s] = events[s][0] plot.sde( trains, events, start, stop, kernel_size, optimize_steps, minimum_kernel, maximum_kernel, None, self.unit, current.progress)
def start(self, current, selections): # Prepare quantities start = float(self.start_time) * pq.ms stop = None if self.stop_enabled: stop = float(self.stop) * pq.ms bin_size = float(self.bin_size) * pq.ms # Load data current.progress.begin('Creating PSTH') events = None if self.data_source == 0: trains = current.spike_trains_by_unit() if self.align_enabled: events = current.labeled_events(self.align) else: # Prepare dictionaries for psth(): # One entry of spike trains for each selection, # an event for each segment occuring in any selection trains = {} if self.align_enabled: events = {} for s in selections: trains[neo.Unit(s.name)] = s.spike_trains() if self.align_enabled: events.update(s.labeled_events(self.align)) if events: for s in events: # Align on first event in each segment events[s] = events[s][0] plot.psth(trains, events, start, stop, bin_size, rate_correction=True, time_unit=pq.ms, bar_plot=self.diagram_type == 0, progress=current.progress)
def test_load_save(): n_channels = 5 n_samples = 20 n_spikes = 50 fname = '/tmp/test_phy.exdir' if os.path.exists(fname): shutil.rmtree(fname) wf = np.random.random((n_spikes, n_channels, n_samples)) ts = np.sort(np.random.random(n_spikes)) t_stop = np.ceil(ts[-1]) sptr = neo.SpikeTrain(times=ts, units='s', waveforms=wf * pq.V, t_stop=t_stop, **{'group_id': 0}) blk = neo.Block() seg = neo.Segment() seg.duration = t_stop blk.segments.append(seg) chx = neo.ChannelIndex(index=range(n_channels), **{'group_id': 0}) blk.channel_indexes.append(chx) sptr.channel_index = chx unit = neo.Unit() unit.spiketrains.append(sptr) chx.units.append(unit) seg.spiketrains.append(sptr) epo = neo.Epoch() if os.path.exists(fname): shutil.rmtree(fname) io = neo.ExdirIO(fname) io.write_block(blk) wfswap = wf.swapaxes(1, 2) m = NeoModel(fname, overwrite=True) assert np.array_equal(m.spike_times, ts) assert np.array_equal(m.waveforms, wfswap) m.save() m2 = NeoModel(fname, overwrite=True) assert np.array_equal(m2.spike_times, ts) assert np.array_equal(m2.waveforms, wfswap) assert np.array_equal(m2.features, m.features) assert np.array_equal(m2.amplitudes, m.amplitudes) assert np.array_equal(m2.spike_clusters, m.spike_clusters)
def build_block(data_file): """(plaintextfile_path) -> neo.core.block.Block Thie function reads a plain text file (data_file) with the data exported (per waveform) from Plexon Offline Sorter after sorting and returns a neo.Block with spiketrains ordered in any number of 10 minute segments. For practicality and Plexon management of channels names, units and channels have been ignored in the block structure.""" raw_data = pd.read_csv(data_file, sep=',', header=0, usecols=[0, 1, 2]) ord_times = raw_data.groupby(['Channel Name', 'Unit'])['Timestamp'] new_block = neo.Block() chx = neo.ChannelIndex(index=None, name='MEA_60') new_block.channel_indexes.append(chx) # Next line will not work properly if last spike happens # exactly at the end of the recording num_segments = range(int(raw_data['Timestamp'].max() // 600 + 1)) for ind in num_segments: seg = neo.Segment(name='segment {}'.format(ind), index=ind) new_block.segments.append(seg) for name, group in ord_times: time_stamps = ord_times.get_group(name).values inter = 600 # Number of seconds in 10 minutes first_seg = neo.SpikeTrain( time_stamps[time_stamps < inter], units='sec', t_start=0, t_stop=inter) new_block.segments[0].spiketrains.append(first_seg) new_unit = neo.Unit(name=name) sptrs = [first_seg] for seg in num_segments[1:-1]: seg_train = neo.SpikeTrain(time_stamps[(time_stamps > seg * inter) & (time_stamps < ((seg + 1) * inter))], units='sec', t_start=(seg * inter), t_stop=((seg + 1) * inter)) new_block.segments[seg].spiketrains.append(seg_train) sptrs.append(seg_train) last_seg = neo.SpikeTrain(time_stamps[time_stamps > (num_segments[-1] * inter)], units='sec', t_start=(num_segments[-1]) * inter, t_stop=((num_segments[-1] + 1) * inter)) new_block.segments[num_segments[-1]].spiketrains.append(last_seg) sptrs.append(last_seg) new_unit.spiketrains = sptrs chx.units.append(new_unit) return new_block
import neo import quantities as pq import numpy as np import nixio as nix from neo.io import NixIO block = neo.Block() chn_index = neo.ChannelIndex([0, 1, 2], channel_names=["a", "b", "c"], channel_ids=[1, 2, 3]) block.channel_indexes.append(chn_index) unit = neo.Unit(name="x", description="contain1st") chn_index.units.append(unit) seg = neo.Segment() asig = neo.AnalogSignal(name="signal", signal=[1.1, 1.2, 1.5], units="mV", sampling_rate=1 * pq.Hz) seg.analogsignals.append(asig) asig2 = neo.AnalogSignal(name="signal2", signal=[1.1, 1.2, 2.5], units="mV", sampling_rate=1 * pq.Hz) seg.analogsignals.append(asig2) irasig = neo.IrregularlySampledSignal(name="irsignal", signal=np.random.random((100, 2)), units="mV", times=np.cumsum( np.random.random(100) * pq.s)) seg.irregularlysampledsignals.append(irasig)
def save(self, spike_clusters=None, groups=None, *labels): if spike_clusters is None: spike_clusters = self.spike_clusters assert spike_clusters.shape == self.spike_clusters.shape # assert spike_clusters.dtype == self.spike_clusters.dtype # TODO check if this is necessary self.spike_clusters = spike_clusters blk = neo.Block() seg = neo.Segment(name='Segment_{}'.format(self.segment_num), index=self.segment_num) # seg.duration = self.duration blk.segments.append(seg) metadata = self.chx.annotations if labels: metadata.update({name: values for name, values in labels}) chx = neo.ChannelIndex(index=self.chx.index, name=self.chx.name, **metadata) blk.channel_indexes.append(chx) try: wf_units = self.sptrs[0].waveforms.units except AttributeError: wf_units = pq.dimensionless clusters = np.unique(spike_clusters) self.cluster_groups = groups or self.cluster_groups for sc in clusters: mask = self.spike_clusters == sc waveforms = np.swapaxes(self.waveforms[mask], 1, 2) * wf_units sptr = neo.SpikeTrain(times=self.spike_times[mask] * pq.s, waveforms=waveforms, sampling_rate=self.sample_rate * pq.Hz, name='cluster #%i' % sc, t_stop=self.duration, t_start=self.start_time, **{'cluster_id': sc, 'cluster_group': self.cluster_groups[sc].lower(), 'kk2_metadata': self.kk2_metadata}) sptr.channel_index = chx unt = neo.Unit(name='Unit #{}'.format(sc), **{'cluster_id': sc, 'cluster_group': self.cluster_groups[sc].lower()}) unt.spiketrains.append(sptr) chx.units.append(unt) seg.spiketrains.append(sptr) # save block to file try: io = neo.get_io(self.save_path, mode=self.mode) except Exception: io = neo.get_io(self.save_path) io.write_block(blk) if hasattr(io, 'close'): io.close() if self.output_ext == '.exdir': # save features and masks group = exdir.File(directory=self.save_path) self._exdir_save_group = self._find_exdir_channel_group( group["processing"]['electrophysiology']) # TODO not use elphys name if self._exdir_save_group is None: raise IOError('Can not find a dirctory corresponding to ' + 'channel_group {}'.format(self.channel_group)) self.save_features_masks(spike_clusters)
class DataProvider(object): """ Defines all methods that should be implemented by a selection/data provider class. A `DataProvider` encapsulates access to a selection of data. It can be used by plugins to acesss data currently selected in the GUI or in saved selections. It also contains an attribute `progress`, a :class:`spykeutils.progress_indicator.ProgressIndicator` that can be used to report the progress of an operation (and is used by methods of this class if they can lead to processing times of half a second or more). This class serves as an abstract base class and should not be instantiated.""" _factories = {} no_unit = neo.Unit(name='No Unit') no_segment = neo.Segment(name='No segment') no_channel = neo.RecordingChannel(name='No recording channel') no_channelgroup = neo.RecordingChannelGroup( name='No recording channel group') no_unit.annotate(unique_id=-1) no_segment.annotate(unique_id=-1) no_channel.annotate(unique_id=-1) no_channelgroup.annotate(unique_id=-1) def __init__(self, name, progress): self.name = name self.progress = progress def _invert_indices(self, dictionary): """ Invert the indices of a dictionary of dictionaries. """ dict_type = type(dictionary) ret = dict_type() for i1 in dictionary: for i2 in dictionary[i1]: if not i2 in ret: ret[i2] = dict_type() ret[i2][i1] = dictionary[i1][i2] return ret def blocks(self): """ Return a list of selected Block objects. The returned objects will contain all regular references, not just to selected objects. """ return [] def segments(self): """ Return a list of selected Segment objects. The returned objects will contain all regular references, not just to selected objects. """ return [] def recording_channel_groups(self): """ Return a list of selected RecordingChannelGroup objects. The returned objects will contain all regular references, not just to selected objects. """ return [] def recording_channels(self): """ Return a list of selected RecordingChannel objects. The returned objects will contain all regular references, not just to selected objects. """ return [] def units(self): """ Return a list of selected Unit objects. The returned objects will contain all regular references, not just to selected objects. """ return [] def selection_blocks(self): """ Return a list of selected blocks. The returned blocks will contain references to all other selected elements further down in the object hierarchy, but no references to elements which are not selected. The returned hierarchy is a copy, so changes made to it will not persist. The main purpose of this function is to provide an object hierarchy that can be saved to a neo file. It is not recommended to use it for data processing, the respective functions that return objects lower in the hierarchy are better suited for that purpose. """ return [] def spike_trains(self): """ Return a list of :class:`neo.core.SpikeTrain` objects. """ return [] def spike_trains_by_unit(self): """ Return a dictionary (indexed by Unit) of lists of :class:`neo.core.SpikeTrain` objects. If spike trains not attached to a Unit are selected, their dicionary key will be ``DataProvider.no_unit``. """ return {} def spike_trains_by_segment(self): """ Return a dictionary (indexed by Segment) of lists of :class:`neo.core.SpikeTrain` objects. If spike trains not attached to a Segment are selected, their dictionary key will be ``DataProvider.no_segment``. """ return {} def spike_trains_by_unit_and_segment(self): """ Return a dictionary (indexed by Unit) of dictionaries (indexed by Segment) of :class:`neo.core.SpikeTrain` objects. If there are multiple spike trains in one Segment for the same Unit, only the first will be contained in the returned dictionary. If spike trains not attached to a Unit or Segment are selected, their dictionary key will be ``DataProvider.no_unit`` or ``DataProvider.no_segment``, respectively. """ return {} def spike_trains_by_segment_and_unit(self): """ Return a dictionary (indexed by Unit) of dictionaries (indexed by Segment) of :class:`neo.core.SpikeTrain` objects. If there are multiple spike trains in one Segment for the same Unit, only the first will be contained in the returned dictionary. If spike trains not attached to a Unit or Segment are selected, their dictionary key will be ``DataProvider.no_unit`` or ``DataProvider.no_segment``, respectively. """ return self._invert_indices(self.spike_trains_by_unit_and_segment()) def spikes(self): """ Return a list of :class:`neo.core.Spike` objects. """ return [] def spikes_by_unit(self): """ Return a dictionary (indexed by Unit) of lists of :class:`neo.core.Spike` objects. If spikes not attached to a Unit are selected, their dicionary key will be ``DataProvider.no_unit``. """ return {} def spikes_by_segment(self): """ Return a dictionary (indexed by Segment) of lists of :class:`neo.core.Spike` objects. If spikes not attached to a Segment are selected, their dictionary key will be ``DataProvider.no_segment``. """ return {} def spikes_by_unit_and_segment(self): """ Return a dictionary (indexed by Unit) of dictionaries (indexed by Segment) of :class:`neo.core.Spike` lists. If there are multiple spikes in one Segment for the same Unit, only the first will be contained in the returned dictionary. If spikes not attached to a Unit or Segment are selected, their dictionary key will be ``DataProvider.no_unit`` or ``DataProvider.no_segment``, respectively. """ return {} def spikes_by_segment_and_unit(self): """ Return a dictionary (indexed by Segment) of dictionaries (indexed by Unit) of lists of :class:`neo.core.Spike` lists. If spikes not attached to a Unit or Segment are selected, their dictionary key will be ``DataProvider.no_unit`` or ``DataProvider.no_segment``, respectively. """ return self._invert_indices(self.spikes_by_unit_and_segment()) def events(self, include_array_events=True): """ Return a dictionary (indexed by Segment) of lists of Event objects. :param bool include_array_events: Determines if EventArray objects should be converted to Event objects and included in the returned list. """ return {} def labeled_events(self, label, include_array_events=True): """ Return a dictionary (indexed by Segment) of lists of Event objects with the given label. :param str label: The name of the Event objects to be returnded :param bool include_array_events: Determines if EventArray objects should be converted to Event objects and included in the returned list. """ return [] def event_arrays(self): """ Return a dictionary (indexed by Segment) of lists of EventArray objects. """ return {} def epochs(self, include_array_epochs=True): """ Return a dictionary (indexed by Segment) of lists of Epoch objects. :param bool include_array_epochs: Determines if EpochArray objects should be converted to Epoch objects and included in the returned list. """ return {} def labeled_epochs(self, label, include_array_epochs=True): """ Return a dictionary (indexed by Segment) of lists of Epoch objects with the given label. :param str label: The name of the Epoch objects to be returnded :param bool include_array_epochs: Determines if EpochArray objects should be converted to Epoch objects and included in the returned list. """ return [] def epoch_arrays(self): """ Return a dictionary (indexed by Segment) of lists of EpochArray objects. """ return {} def analog_signals(self, conversion_mode=1): """ Return a list of :class:`neo.core.AnalogSignal` objects. :param int conversion_mode: Determines what signals are returned: 1. AnalogSignal objects only 2. AnalogSignal objects extracted from AnalogSignalArrays only 3. Both AnalogSignal objects and extracted AnalogSignalArrays """ return [] def analog_signals_by_segment(self, conversion_mode=1): """ Return a dictionary (indexed by Segment) of lists of :class:`neo.core.AnalogSignal` objects. If analog signals not attached to a Segment are selected, their dictionary key will be ``DataProvider.no_segment``. :param int conversion_mode: Determines what signals are returned: 1. AnalogSignal objects only 2. AnalogSignal objects extracted from AnalogSignalArrays only 3. Both AnalogSignal objects and extracted AnalogSignalArrays """ return {} def analog_signals_by_channel(self, conversion_mode=1): """ Return a dictionary (indexed by RecordingChannel) of lists of :class:`neo.core.AnalogSignal` objects. If analog signals not attached to a RecordingChannel are selected, their dictionary key will be ``DataProvider.no_channel``. :param int conversion_mode: Determines what signals are returned: 1. AnalogSignal objects only 2. AnalogSignal objects extracted from AnalogSignalArrays only 3. Both AnalogSignal objects and extracted AnalogSignalArrays """ return {} def analog_signals_by_channel_and_segment(self, conversion_mode=1): """ Return a dictionary (indexed by RecordingChannel) of dictionaries (indexed by Segment) of :class:`neo.core.AnalogSignal` lists. If analog signals not attached to a Segment or RecordingChannel are selected, their dictionary key will be ``DataProvider.no_segment`` or ``DataProvider.no_channel``, respectively. :param int conversion_mode: Determines what signals are returned: 1. AnalogSignal objects only 2. AnalogSignal objects extracted from AnalogSignalArrays only 3. Both AnalogSignal objects and extracted AnalogSignalArrays """ return {} def analog_signals_by_segment_and_channel(self, conversion_mode=1): """ Return a dictionary (indexed by Segment) of dictionaries (indexed by RecordingChannel) of :class:`neo.core.AnalogSignal` lists. If analog signals not attached to a Segment or RecordingChannel are selected, their dictionary key will be ``DataProvider.no_segment`` or ``DataProvider.no_channel``, respectively. :param int conversion_mode: Determines what signals are returned: 1. AnalogSignal objects only 2. AnalogSignal objects extracted from AnalogSignalArrays only 3. Both AnalogSignal objects and extracted AnalogSignalArrays """ return self._invert_indices( self.analog_signals_by_channel_and_segment(conversion_mode)) def analog_signal_arrays(self): """ Return a list of :class:`neo.core.AnalogSignalArray` objects. """ return [] def analog_signal_arrays_by_segment(self): """ Return a dictionary (indexed by Segment) of lists of :class:`neo.core.AnalogSignalArray` objects. If analog signals arrays not attached to a Segment are selected, their dictionary key will be ``DataProvider.no_segment``. """ return {} def analog_signal_arrays_by_channelgroup(self): """ Return a dictionary (indexed by RecordingChannelGroup) of lists of :class:`neo.core.AnalogSignalArray` objects. If analog signals arrays not attached to a RecordingChannel are selected, their dictionary key will be ``DataProvider.no_channelgroup``. """ return {} def analog_signal_arrays_by_channelgroup_and_segment(self): """ Return a dictionary (indexed by RecordingChannelGroup) of dictionaries (indexed by Segment) of :class:`neo.core.AnalogSignalArray` objects. If there are multiple analog signals in one RecordingChannel for the same Segment, only the first will be contained in the returned dictionary. If analog signal arrays not attached to a Segment or RecordingChannelGroup are selected, their dictionary key will be ``DataProvider.no_segment`` or ``DataProvider.no_channelgroup``, respectively. """ return {} def analog_signal_arrays_by_segment_and_channelgroup(self): """ Return a dictionary (indexed by RecordingChannelGroup) of dictionaries (indexed by Segment) of :class:`neo.core.AnalogSignalArray` objects. If there are multiple analog signals in one RecordingChannel for the same Segment, only the first will be contained in the returned dictionary. If analog signal arrays not attached to a Segment or RecordingChannelGroup are selected, their dictionary key will be ``DataProvider.no_segment`` or ``DataProvider.no_channelgroup``, respectively. """ return self._invert_indices( self.analog_signal_arrays_by_channelgroup_and_segment()) def refresh_view(self): """ Refresh associated views of the data. Use this method if when you change the neo hierarchy on which the selection is based (e.g. adding or removing objects). It will ensure that all current views on the data are updated, for example in Spyke Viewer. """ pass def data_dict(self): """ Return a dictionary with all information to serialize the object. """ return {} @classmethod def from_data(cls, data, progress=None): """ Create a new `DataProvider` object from a dictionary. This method is mostly for internal use. The respective type of `DataProvider` (e.g. :class:`spykeviewer.plugin_framework.data_provider_neo.DataProviderNeo` has to be imported in the environment where this function is called. :param dict data: A dictionary containing data from a `DataProvider` object, as returned by :func:`data_dict`. :param ProgressIndicator progress: The object where loading progress will be indicated. """ if progress: return cls._factories[data['type']](data, progress) return cls._factories[data['type']](data)
def generate_spike_trains(exdir_path, openephys_rec, source='klusta'): import neo if source == 'klusta': # TODO acquire features and masks print('Generating spike trains from KWIK file') exdir_file = exdir.File(exdir_path) acquisition = exdir_file["acquisition"] openephys_session = acquisition.attrs["openephys_session"] klusta_directory = op.join(str(acquisition.directory), openephys_session, 'klusta') n = 0 for root, dirs, files in os.walk(klusta_directory): for f in files: if not f.endswith('_klusta.kwik'): continue n += 1 kwikfile = op.join(root, f) kwikio = neo.io.KwikIO(filename=kwikfile, ) blk = kwikio.read_block(raw_data_units='uV') seg = blk.segments[0] try: exdirio = neo.io.ExdirIO(exdir_path) exdirio.write_block(blk) except Exception: print('WARNING: unable to convert\n', kwikfile) if n == 0: raise IOError('.kwik file cannot be found in ' + klusta_directory) elif source == 'openephys': exdirio = neo.io.ExdirIO(exdir_path) for oe_group in openephys_rec.channel_groups: channel_ids = [ch.id for ch in oe_group.channels] channel_index = [ch.index for ch in oe_group.channels] chx = neo.ChannelIndex(name='channel group {}'.format(oe_group.id), channel_ids=channel_ids, index=channel_index, group_id=oe_group.id) for sptr in oe_group.spiketrains: unit = neo.Unit(cluster_group='unsorted', cluster_id=sptr.attrs['cluster_id'], name=sptr.attrs['name']) unit.spiketrains.append( neo.SpikeTrain(times=sptr.times, waveforms=sptr.waveforms, sampling_rate=sptr.sample_rate, t_stop=sptr.t_stop, **sptr.attrs)) chx.units.append(unit) exdirio.write_channelindex(chx, start_time=0 * pq.s, stop_time=openephys_rec.duration) elif source == 'kilosort': print('Generating spike trains from KiloSort') exdirio = neo.io.ExdirIO(exdir_path) exdir_file = exdir.File(exdir_path) openephys_directory = op.join( str(exdir_file["acquisition"].directory), exdir_file["acquisition"].attrs["openephys_session"]) # iterate over channel groups. As there are no channel associated with # any spiking unit (TO BE IMPLEMENTED), everything is written to # channel_group 0 for oe_group in openephys_rec.channel_groups: channel_ids = [ch.id for ch in oe_group.channels] channel_index = [ch.index for ch in oe_group.channels] chx = neo.ChannelIndex(name='channel group {}'.format(oe_group.id), channel_ids=channel_ids, index=channel_index, group_id=oe_group.id) # load output spt = np.load(op.join(openephys_directory, 'spike_times.npy')).flatten() spc = np.load(op.join(openephys_directory, 'spike_clusters.npy')).flatten() try: cgroup = np.loadtxt(op.join(openephys_directory, 'cluster_group.tsv'), dtype=[('cluster_id', 'i4'), ('group', 'U8')], skiprows=1) if cgroup.shape == (0, ): raise FileNotFoundError except FileNotFoundError: # manual corrections didn't happen; cgroup = np.array(list( zip(np.unique(spc), ['unsorted'] * np.unique(spc).size)), dtype=[('cluster_id', 'i4'), ('group', 'U8')]) for id, grp in cgroup: unit = neo.Unit(cluster_group=str(grp), cluster_id=id, name=id) unit.spiketrains.append( neo.SpikeTrain( times=(spt[spc == id].astype(float) / openephys_rec.sample_rate).simplified, t_stop=openephys_rec.duration, )) chx.units.append(unit) exdirio.write_channelindex(chx, start_time=0 * pq.s, stop_time=openephys_rec.duration) break else: raise ValueError(source + ' not supported')
def generate_block_for_sorting( nb_segment=5, nb_recordingchannel=4, sampling_rate=10.e3 * pq.Hz, duration=6. * pq.s, nb_unit=6, spikerate_range=[.5 * pq.Hz, 12 * pq.Hz], t_start=0. * pq.s, noise_ratio=0.2, use_memmap_path=None, ): bl = neo.Block() for s in range(nb_segment): bl.segments.append(neo.Segment(name='Segment {}'.format(s))) rcg = neo.RecordingChannelGroup(name='polytrode', ) rcg.block = bl bl.recordingchannelgroups.append(rcg) for r in range(nb_recordingchannel): rc = neo.RecordingChannel(name='RC {}'.format(r), index=r) rcg.recordingchannels.append(rc) rc.recordingchannelgroups.append(rcg) # generate spiketrain for u in range(nb_unit): unit = neo.Unit(name='Unit #{}'.format(u)) rcg.units.append(unit) spikerate = np.random.rand() * np.diff( spikerate_range) + spikerate_range[0].magnitude spike_times = [] for seg in bl.segments: spike_times.append( np.random.rand(int((spikerate * (duration)).simplified)) * duration + t_start) n_total = np.sum(times.size for times in spike_times) all_waveforms = stupid_waveform_generator(n_total, nb_recordingchannel, sampling_rate) * pq.mV n = 0 for s, seg in enumerate(bl.segments): #~ print u, 's', s, spike_times[s].size sptr = neo.SpikeTrain(spike_times[s], t_start=t_start, t_stop=t_start + duration) sptr.waveforms = all_waveforms[n:n + spike_times[s].size, :, :] sptr.sampling_rate = sampling_rate sptr.left_sweep = (sptr.waveforms.shape[2] / sampling_rate).rescale('ms') / 2. seg.spiketrains.append(sptr) unit.spiketrains.append(sptr) n += spike_times[s].size # generate signal = noise + spike waveform for i, seg in enumerate(bl.segments): for j, rc in enumerate(rcg.recordingchannels): sig_size = int((duration * sampling_rate).simplified) if use_memmap_path: filename = os.path.join(use_memmap_path, 'signal {} {}'.format(i, j)) signal = np.memmap( filename, dtype=float, mode='w+', offset=0, shape=sig_size, ) signal[:] = noise_ratio * np.random.randn(sig_size) else: signal = noise_ratio * np.random.randn(sig_size) #~ anasig = neo.AnalogSignal(signal = signal, units = 'mV', sampling_rate = sampling_rate, t_start = t_start, channel_index = j) for sptr in seg.spiketrains: for k, time in enumerate(sptr): wf = sptr.waveforms[k, j, :] pos = int(((time - t_start) * sampling_rate).simplified) if pos + wf.size < signal.size: signal[pos:pos + wf.size] += wf anasig = neo.AnalogSignal(signal=signal, units='mV', sampling_rate=sampling_rate, t_start=t_start, channel_index=j) seg.analogsignals.append(anasig) rc.analogsignals.append(anasig) try: neo.io.tools.create_many_to_one_relationship(bl) except: bl.create_many_to_one_relationship() return bl