segment.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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 +
  88. self.spiketrains + self.irregularlysampledsignals]
  89. t_starts += [e.times[0] for e in self.epochs + self.events if len(e.times) > 0]
  90. # t_start is not defined if no children are present
  91. if len(t_starts) == 0:
  92. return None
  93. t_start = min(t_starts)
  94. return t_start
  95. # t_stop attribute is handled as a property so type checking can be done
  96. @property
  97. def t_stop(self):
  98. '''
  99. Time when last signal ends.
  100. '''
  101. t_stops = [sig.t_stop for sig in self.analogsignals +
  102. self.spiketrains + self.irregularlysampledsignals]
  103. t_stops += [e.times[-1] for e in self.epochs + self.events if len(e.times) > 0]
  104. # t_stop is not defined if no children are present
  105. if len(t_stops) == 0:
  106. return None
  107. t_stop = max(t_stops)
  108. return t_stop
  109. def take_spiketrains_by_unit(self, unit_list=None):
  110. '''
  111. Return :class:`SpikeTrains` in the :class:`Segment` that are also in a
  112. :class:`Unit` in the :attr:`unit_list` provided.
  113. '''
  114. if unit_list is None:
  115. return []
  116. spiketrain_list = []
  117. for spiketrain in self.spiketrains:
  118. if spiketrain.unit in unit_list:
  119. spiketrain_list.append(spiketrain)
  120. return spiketrain_list
  121. # def take_analogsignal_by_unit(self, unit_list=None):
  122. # '''
  123. # Return :class:`AnalogSignal` objects in the :class:`Segment` that are
  124. # have the same :attr:`channel_index` as any of the :class:`Unit: objects
  125. # in the :attr:`unit_list` provided.
  126. # '''
  127. # if unit_list is None:
  128. # return []
  129. # channel_indexes = []
  130. # for unit in unit_list:
  131. # if unit.channel_indexes is not None:
  132. # channel_indexes.extend(unit.channel_indexes)
  133. # return self.take_analogsignal_by_channelindex(channel_indexes)
  134. #
  135. # def take_analogsignal_by_channelindex(self, channel_indexes=None):
  136. # '''
  137. # Return :class:`AnalogSignal` objects in the :class:`Segment` that have
  138. # a :attr:`channel_index` that is in the :attr:`channel_indexes`
  139. # provided.
  140. # '''
  141. # if channel_indexes is None:
  142. # return []
  143. # anasig_list = []
  144. # for anasig in self.analogsignals:
  145. # if anasig.channel_index in channel_indexes:
  146. # anasig_list.append(anasig)
  147. # return anasig_list
  148. def take_slice_of_analogsignalarray_by_unit(self, unit_list=None):
  149. '''
  150. Return slices of the :class:`AnalogSignal` objects in the
  151. :class:`Segment` that correspond to a :attr:`channel_index` of any of
  152. the :class:`Unit` objects in the :attr:`unit_list` provided.
  153. '''
  154. if unit_list is None:
  155. return []
  156. indexes = []
  157. for unit in unit_list:
  158. if unit.get_channel_indexes() is not None:
  159. indexes.extend(unit.get_channel_indexes())
  160. return self.take_slice_of_analogsignalarray_by_channelindex(indexes)
  161. def take_slice_of_analogsignalarray_by_channelindex(self,
  162. channel_indexes=None):
  163. '''
  164. Return slices of the :class:`AnalogSignalArrays` in the
  165. :class:`Segment` that correspond to the :attr:`channel_indexes`
  166. provided.
  167. '''
  168. if channel_indexes is None:
  169. return []
  170. sliced_sigarrays = []
  171. for sigarr in self.analogsignals:
  172. if sigarr.get_channel_index() is not None:
  173. ind = np.in1d(sigarr.get_channel_index(), channel_indexes)
  174. sliced_sigarrays.append(sigarr[:, ind])
  175. return sliced_sigarrays
  176. def construct_subsegment_by_unit(self, unit_list=None):
  177. '''
  178. Return a new :class:`Segment that contains the :class:`AnalogSignal`,
  179. :class:`AnalogSignal`, and :class:`SpikeTrain`
  180. objects common to both the current :class:`Segment` and any
  181. :class:`Unit` in the :attr:`unit_list` provided.
  182. *Example*::
  183. >>> from neo.core import (Segment, Block, Unit, SpikeTrain,
  184. ... ChannelIndex)
  185. >>>
  186. >>> blk = Block()
  187. >>> chx = ChannelIndex(name='group0')
  188. >>> blk.channel_indexes = [chx]
  189. >>>
  190. >>> for ind in range(5):
  191. ... unit = Unit(name='Unit #%s' % ind, channel_index=ind)
  192. ... chx.units.append(unit)
  193. ...
  194. >>>
  195. >>> for ind in range(3):
  196. ... seg = Segment(name='Simulation #%s' % ind)
  197. ... blk.segments.append(seg)
  198. ... for unit in chx.units:
  199. ... train = SpikeTrain([1, 2, 3], units='ms', t_start=0.,
  200. ... t_stop=10)
  201. ... train.unit = unit
  202. ... unit.spiketrains.append(train)
  203. ... seg.spiketrains.append(train)
  204. ...
  205. >>>
  206. >>> seg0 = blk.segments[-1]
  207. >>> seg1 = seg0.construct_subsegment_by_unit(chx.units[:2])
  208. >>> len(seg0.spiketrains)
  209. 5
  210. >>> len(seg1.spiketrains)
  211. 2
  212. '''
  213. seg = Segment()
  214. seg.spiketrains = self.take_spiketrains_by_unit(unit_list)
  215. seg.analogsignals = \
  216. self.take_slice_of_analogsignalarray_by_unit(unit_list)
  217. # TODO copy others attributes
  218. return seg