channelindex.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # -*- coding: utf-8 -*-
  2. '''
  3. This module defines :class:`ChannelIndex`, a container for multiple
  4. data channels.
  5. :class:`ChannelIndex` 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. import numpy as np
  11. import quantities as pq
  12. from neo.core.container import Container
  13. class ChannelIndex(Container):
  14. '''
  15. A container for indexing/grouping data channels.
  16. This container has several purposes:
  17. * Grouping all :class:`AnalogSignal`\s inside a :class:`Block`
  18. across :class:`Segment`\s;
  19. * Indexing a subset of the channels within an :class:`AnalogSignal`;
  20. * Container of :class:`Unit`\s. A neuron discharge (:class:`Unit`)
  21. can be seen by several electrodes (e.g. 4 for tetrodes).
  22. *Usage 1* multi :class:`Segment` recording with 2 electrode arrays::
  23. >>> from neo.core import (Block, Segment, ChannelIndex,
  24. ... AnalogSignal)
  25. >>> from quantities import nA, kHz
  26. >>> import numpy as np
  27. >>>
  28. >>> # create a Block with 3 Segment and 2 ChannelIndex objects
  29. ... blk = Block()
  30. >>> for ind in range(3):
  31. ... seg = Segment(name='segment %d' % ind, index=ind)
  32. ... blk.segments.append(seg)
  33. ...
  34. >>> for ind in range(2):
  35. ... chx = ChannelIndex(name='Array probe %d' % ind,
  36. ... index=np.arange(64))
  37. ... blk.channel_indexes.append(chx)
  38. ...
  39. >>> # Populate the Block with AnalogSignal objects
  40. ... for seg in blk.segments:
  41. ... for chx in blk.channel_indexes:
  42. ... a = AnalogSignal(np.random.randn(10000, 64)*nA,
  43. ... sampling_rate=10*kHz)
  44. ... chx.analogsignals.append(a)
  45. ... seg.analogsignals.append(a)
  46. *Usage 2* grouping channels::
  47. >>> from neo.core import Block, ChannelIndex
  48. >>> import numpy as np
  49. >>> from quantities import mV, kHz
  50. >>>
  51. >>> # Create a Block
  52. ... blk = Block()
  53. >>> blk.segments.append(Segment())
  54. >>>
  55. >>> # Create a signal with 8 channels
  56. ... sig = AnalogSignal(np.random.randn(1000, 8)*mV, sampling_rate=10*kHz)
  57. ... blk.segments[0].append(sig)
  58. ...
  59. >>> # Create a new ChannelIndex which groups three channels from the signal
  60. ... chx = ChannelIndex(channel_names=np.array(['ch1', 'ch4', 'ch6']),
  61. ... index=np.array([0, 3, 5])
  62. >>> chx.analogsignals.append(sig)
  63. >>> blk.channel_indexes.append(chx)
  64. *Usage 3* dealing with :class:`Unit` objects::
  65. >>> from neo.core import Block, ChannelIndex, Unit
  66. >>>
  67. >>> # Create a Block
  68. >>> blk = Block()
  69. >>>
  70. >>> # Create a new ChannelIndex and add it to the Block
  71. >>> chx = ChannelIndex(name='octotrode A')
  72. >>> blk.channel_indexes.append(chx)
  73. >>>
  74. >>> # create several Unit objects and add them to the
  75. >>> # ChannelIndex
  76. ... for ind in range(5):
  77. ... unit = Unit(name = 'unit %d' % ind,
  78. ... description='after a long and hard spike sorting')
  79. ... chx.units.append(unit)
  80. *Required attributes/properties*:
  81. :channel_indexes: (numpy.array 1D dtype='i')
  82. Index of each channel in the attached signals.
  83. *Recommended attributes/properties*:
  84. :name: (str) A label for the dataset.
  85. :description: (str) Text description.
  86. :file_origin: (str) Filesystem path or URL of the original data file.
  87. :channel_names: (numpy.array 1D dtype='S')
  88. Names for each recording channel.
  89. :coordinates: (quantity array 2D (x, y, z))
  90. Physical or logical coordinates of all channels.
  91. Note: Any other additional arguments are assumed to be user-specific
  92. metadata and stored in :attr:`annotations`.
  93. *Container of*:
  94. :class:`AnalogSignal`
  95. :class:`IrregularlySampledSignal`
  96. :class:`Unit`
  97. '''
  98. _container_child_objects = ('Unit',)
  99. _data_child_objects = ('AnalogSignal', 'IrregularlySampledSignal')
  100. _single_parent_objects = ('Block',)
  101. _necessary_attrs = (('index', np.ndarray, 1, np.dtype('i')),)
  102. _recommended_attrs = ((('channel_names', np.ndarray, 1, np.dtype('S')),
  103. ('channel_ids', np.ndarray, 1, np.dtype('i')),
  104. ('coordinates', pq.Quantity, 2)) +
  105. Container._recommended_attrs)
  106. def __init__(self, index, channel_names=None, channel_ids=None,
  107. name=None, description=None, file_origin=None,
  108. coordinates=None, **annotations):
  109. '''
  110. Initialize a new :class:`ChannelIndex` instance.
  111. '''
  112. # Inherited initialization
  113. # Sets universally recommended attributes, and places all others
  114. # in annotations
  115. super(ChannelIndex, self).__init__(name=name,
  116. description=description,
  117. file_origin=file_origin,
  118. **annotations)
  119. # Defaults
  120. if channel_names is None:
  121. channel_names = np.array([], dtype='S')
  122. if channel_ids is None:
  123. channel_ids = np.array([], dtype='i')
  124. # Store recommended attributes
  125. self.channel_names = np.array(channel_names)
  126. self.channel_ids = np.array(channel_ids)
  127. self.index = np.array(index)
  128. self.coordinates = coordinates
  129. def __getitem__(self, i):
  130. '''
  131. Get the item or slice :attr:`i`.
  132. '''
  133. index = self.index.__getitem__(i)
  134. if self.channel_names.size > 0:
  135. channel_names = self.channel_names[index]
  136. if not channel_names.shape:
  137. channel_names = [channel_names]
  138. else:
  139. channel_names = None
  140. if self.channel_ids.size > 0:
  141. channel_ids = self.channel_ids[index]
  142. if not channel_ids.shape:
  143. channel_ids = [channel_ids]
  144. else:
  145. channel_ids = None
  146. obj = ChannelIndex(index=np.arange(index.size),
  147. channel_names=channel_names,
  148. channel_ids=channel_ids)
  149. obj.block = self.block
  150. return obj