# -*- coding: utf-8 -*- """ Tools for IO coder: * Creating RecordingChannel and making links with AnalogSignals and SPikeTrains """ import collections import numpy as np from neo.core import (AnalogSignal, Block, Epoch, Event, IrregularlySampledSignal, ChannelIndex, Segment, SpikeTrain, Unit) #def finalize_block(block): # populate_RecordingChannel(block) # block.create_many_to_one_relationship() # Special case this tricky many-to-many relationship # we still need links from recordingchannel to analogsignal # for chx in block.channel_indexes: # for rc in chx.recordingchannels: # rc.create_many_to_one_relationship() # def populate_RecordingChannel(bl, remove_from_annotation=True): # """ # When a Block is # Block>Segment>AnalogSIgnal # this function auto create all RecordingChannel following these rules: # * when 'channel_index ' is in AnalogSIgnal the corresponding # RecordingChannel is created. # * 'channel_index ' is then set to None if remove_from_annotation # * only one ChannelIndex is created # # It is a utility at the end of creating a Block for IO. # # Usage: # >>> populate_RecordingChannel(a_block) # """ # recordingchannels = {} # for seg in bl.segments: # for anasig in seg.analogsignals: # if getattr(anasig, 'channel_index', None) is not None: # ind = int(anasig.channel_index) # if ind not in recordingchannels: # recordingchannels[ind] = RecordingChannel(index=ind) # if 'channel_name' in anasig.annotations: # channel_name = anasig.annotations['channel_name'] # recordingchannels[ind].name = channel_name # if remove_from_annotation: # anasig.annotations.pop('channel_name') # recordingchannels[ind].analogsignals.append(anasig) # anasig.recordingchannel = recordingchannels[ind] # if remove_from_annotation: # anasig.channel_index = None # # indexes = np.sort(list(recordingchannels.keys())).astype('i') # names = np.array([recordingchannels[idx].name for idx in indexes], # dtype='S') # chx = ChannelIndex(name='all channels', # index=indexes, # channel_names=names) # bl.channel_indexes.append(chx) # for ind in indexes: # # many to many relationship # chx.recordingchannels.append(recordingchannels[ind]) # recordingchannels[ind].channel_indexes.append(chx) #def iteritems(D): # try: # return D.iteritems() # Python 2 # except AttributeError: # return D.items() # Python 3 class LazyList(collections.MutableSequence): """ An enhanced list that can load its members on demand. Behaves exactly like a regular list for members that are Neo objects. Each item should contain the information that ``load_lazy_cascade`` needs to load the respective object. """ _container_objects = set( [Block, Segment, ChannelIndex, Unit]) _neo_objects = _container_objects.union( [AnalogSignal, Epoch, Event, IrregularlySampledSignal, SpikeTrain]) def __init__(self, io, lazy, items=None): """ :param io: IO instance that can load items. :param lazy: Lazy parameter with which the container object using the list was loaded. :param items: Optional, initial list of items. """ if items is None: self._data = [] else: self._data = items self._lazy = lazy self._io = io def __getitem__(self, index): item = self._data.__getitem__(index) if isinstance(index, slice): return LazyList(self._io, item) if type(item) in self._neo_objects: return item loaded = self._io.load_lazy_cascade(item, self._lazy) self._data[index] = loaded return loaded def __delitem__(self, index): self._data.__delitem__(index) def __len__(self): return self._data.__len__() def __setitem__(self, index, value): self._data.__setitem__(index, value) def insert(self, index, value): self._data.insert(index, value) def append(self, value): self._data.append(value) def reverse(self): self._data.reverse() def extend(self, values): self._data.extend(values) def remove(self, value): self._data.remove(value) def __str__(self): return '<' + self.__class__.__name__ + '>' + self._data.__str__() def __repr__(self): return '<' + self.__class__.__name__ + '>' + self._data.__repr__()