segment.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. # -*- coding: utf-8 -*-
  2. '''
  3. This module defines :class:`Segment`, a container for data sharing a common
  4. time basis.
  5. :class:`Segment` derives from :class:`Container`,
  6. from :module:`neo.core.container`.
  7. '''
  8. # needed for python 3 compatibility
  9. from __future__ import absolute_import, division, print_function
  10. from datetime import datetime
  11. import numpy as np
  12. from neo.core.container import Container
  13. class Segment(Container):
  14. '''
  15. A container for data sharing a common time basis.
  16. A :class:`Segment` is a heterogeneous container for discrete or continous
  17. data sharing a common clock (time basis) but not necessary the same
  18. sampling rate, start or end time.
  19. *Usage*::
  20. >>> from neo.core import Segment, SpikeTrain, AnalogSignal
  21. >>> from quantities import Hz, s
  22. >>>
  23. >>> seg = Segment(index=5)
  24. >>>
  25. >>> train0 = SpikeTrain(times=[.01, 3.3, 9.3], units='sec', t_stop=10)
  26. >>> seg.spiketrains.append(train0)
  27. >>>
  28. >>> train1 = SpikeTrain(times=[100.01, 103.3, 109.3], units='sec',
  29. ... t_stop=110)
  30. >>> seg.spiketrains.append(train1)
  31. >>>
  32. >>> sig0 = AnalogSignal(signal=[.01, 3.3, 9.3], units='uV',
  33. ... sampling_rate=1*Hz)
  34. >>> seg.analogsignals.append(sig0)
  35. >>>
  36. >>> sig1 = AnalogSignal(signal=[100.01, 103.3, 109.3], units='nA',
  37. ... sampling_period=.1*s)
  38. >>> seg.analogsignals.append(sig1)
  39. *Required attributes/properties*:
  40. None
  41. *Recommended attributes/properties*:
  42. :name: (str) A label for the dataset.
  43. :description: (str) Text description.
  44. :file_origin: (str) Filesystem path or URL of the original data file.
  45. :file_datetime: (datetime) The creation date and time of the original
  46. data file.
  47. :rec_datetime: (datetime) The date and time of the original recording
  48. :index: (int) You can use this to define a temporal ordering of
  49. your Segment. For instance you could use this for trial numbers.
  50. Note: Any other additional arguments are assumed to be user-specific
  51. metadata and stored in :attr:`annotations`.
  52. *Properties available on this object*:
  53. :all_data: (list) A list of all child objects in the :class:`Segment`.
  54. *Container of*:
  55. :class:`Epoch`
  56. :class:`Event`
  57. :class:`AnalogSignal`
  58. :class:`IrregularlySampledSignal`
  59. :class:`SpikeTrain`
  60. '''
  61. _data_child_objects = ('AnalogSignal',
  62. 'Epoch', 'Event',
  63. 'IrregularlySampledSignal', 'SpikeTrain')
  64. _single_parent_objects = ('Block',)
  65. _recommended_attrs = ((('file_datetime', datetime),
  66. ('rec_datetime', datetime),
  67. ('index', int)) +
  68. Container._recommended_attrs)
  69. _repr_pretty_containers = ('analogsignals',)
  70. def __init__(self, name=None, description=None, file_origin=None,
  71. file_datetime=None, rec_datetime=None, index=None,
  72. **annotations):
  73. '''
  74. Initialize a new :class:`Segment` instance.
  75. '''
  76. super(Segment, self).__init__(name=name, description=description,
  77. file_origin=file_origin, **annotations)
  78. self.file_datetime = file_datetime
  79. self.rec_datetime = rec_datetime
  80. self.index = index
  81. # t_start attribute is handled as a property so type checking can be done
  82. @property
  83. def t_start(self):
  84. '''
  85. Time when first signal begins.
  86. '''
  87. t_starts = [sig.t_start for sig in self.analogsignals + self.spiketrains + self.irregularlysampledsignals]
  88. t_starts += [e.times[0] for e in self.epochs + self.events if len(e.times) > 0]
  89. # t_start is not defined if no children are present
  90. if len(t_starts)==0:
  91. return None
  92. t_start = min(t_starts)
  93. return t_start
  94. # t_stop attribute is handled as a property so type checking can be done
  95. @property
  96. def t_stop(self):
  97. '''
  98. Time when last signal ends.
  99. '''
  100. t_stops = [sig.t_stop for sig in self.analogsignals + self.spiketrains + self.irregularlysampledsignals]
  101. t_stops += [e.times[-1] for e in self.epochs + self.events if len(e.times) > 0]
  102. # t_stop is not defined if no children are present
  103. if len(t_stops)==0:
  104. return None
  105. t_stop = max(t_stops)
  106. return t_stop
  107. def take_spiketrains_by_unit(self, unit_list=None):
  108. '''
  109. Return :class:`SpikeTrains` in the :class:`Segment` that are also in a
  110. :class:`Unit` in the :attr:`unit_list` provided.
  111. '''
  112. if unit_list is None:
  113. return []
  114. spiketrain_list = []
  115. for spiketrain in self.spiketrains:
  116. if spiketrain.unit in unit_list:
  117. spiketrain_list.append(spiketrain)
  118. return spiketrain_list
  119. # def take_analogsignal_by_unit(self, unit_list=None):
  120. # '''
  121. # Return :class:`AnalogSignal` objects in the :class:`Segment` that are
  122. # have the same :attr:`channel_index` as any of the :class:`Unit: objects
  123. # in the :attr:`unit_list` provided.
  124. # '''
  125. # if unit_list is None:
  126. # return []
  127. # channel_indexes = []
  128. # for unit in unit_list:
  129. # if unit.channel_indexes is not None:
  130. # channel_indexes.extend(unit.channel_indexes)
  131. # return self.take_analogsignal_by_channelindex(channel_indexes)
  132. #
  133. # def take_analogsignal_by_channelindex(self, channel_indexes=None):
  134. # '''
  135. # Return :class:`AnalogSignal` objects in the :class:`Segment` that have
  136. # a :attr:`channel_index` that is in the :attr:`channel_indexes`
  137. # provided.
  138. # '''
  139. # if channel_indexes is None:
  140. # return []
  141. # anasig_list = []
  142. # for anasig in self.analogsignals:
  143. # if anasig.channel_index in channel_indexes:
  144. # anasig_list.append(anasig)
  145. # return anasig_list
  146. def take_slice_of_analogsignalarray_by_unit(self, unit_list=None):
  147. '''
  148. Return slices of the :class:`AnalogSignal` objects in the
  149. :class:`Segment` that correspond to a :attr:`channel_index` of any of
  150. the :class:`Unit` objects in the :attr:`unit_list` provided.
  151. '''
  152. if unit_list is None:
  153. return []
  154. indexes = []
  155. for unit in unit_list:
  156. if unit.get_channel_indexes() is not None:
  157. indexes.extend(unit.get_channel_indexes())
  158. return self.take_slice_of_analogsignalarray_by_channelindex(indexes)
  159. def take_slice_of_analogsignalarray_by_channelindex(self,
  160. channel_indexes=None):
  161. '''
  162. Return slices of the :class:`AnalogSignalArrays` in the
  163. :class:`Segment` that correspond to the :attr:`channel_indexes`
  164. provided.
  165. '''
  166. if channel_indexes is None:
  167. return []
  168. sliced_sigarrays = []
  169. for sigarr in self.analogsignals:
  170. if sigarr.get_channel_index() is not None:
  171. ind = np.in1d(sigarr.get_channel_index(), channel_indexes)
  172. sliced_sigarrays.append(sigarr[:, ind])
  173. return sliced_sigarrays
  174. def construct_subsegment_by_unit(self, unit_list=None):
  175. '''
  176. Return a new :class:`Segment that contains the :class:`AnalogSignal`,
  177. :class:`AnalogSignal`, and :class:`SpikeTrain`
  178. objects common to both the current :class:`Segment` and any
  179. :class:`Unit` in the :attr:`unit_list` provided.
  180. *Example*::
  181. >>> from neo.core import (Segment, Block, Unit, SpikeTrain,
  182. ... ChannelIndex)
  183. >>>
  184. >>> blk = Block()
  185. >>> chx = ChannelIndex(name='group0')
  186. >>> blk.channel_indexes = [chx]
  187. >>>
  188. >>> for ind in range(5):
  189. ... unit = Unit(name='Unit #%s' % ind, channel_index=ind)
  190. ... chx.units.append(unit)
  191. ...
  192. >>>
  193. >>> for ind in range(3):
  194. ... seg = Segment(name='Simulation #%s' % ind)
  195. ... blk.segments.append(seg)
  196. ... for unit in chx.units:
  197. ... train = SpikeTrain([1, 2, 3], units='ms', t_start=0.,
  198. ... t_stop=10)
  199. ... train.unit = unit
  200. ... unit.spiketrains.append(train)
  201. ... seg.spiketrains.append(train)
  202. ...
  203. >>>
  204. >>> seg0 = blk.segments[-1]
  205. >>> seg1 = seg0.construct_subsegment_by_unit(chx.units[:2])
  206. >>> len(seg0.spiketrains)
  207. 5
  208. >>> len(seg1.spiketrains)
  209. 2
  210. '''
  211. seg = Segment()
  212. seg.spiketrains = self.take_spiketrains_by_unit(unit_list)
  213. seg.analogsignals = \
  214. self.take_slice_of_analogsignalarray_by_unit(unit_list)
  215. #TODO copy others attributes
  216. return seg