rawio.rst 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. *********
  2. Neo RawIO
  3. *********
  4. .. currentmodule:: neo.rawio
  5. .. _neo_rawio_API:
  6. For performance and memory consumption reasons a new layer has been added to Neo.
  7. In brief:
  8. * **neo.io** is the user-oriented read/write layer. Reading consists of getting a tree
  9. of Neo objects from a data source (file, url, or directory).
  10. When reading, all Neo objects are correctly scaled to the correct units.
  11. Writing consists of making a set of Neo objects persistent in a file format.
  12. * **neo.rawio** is a low-level layer for reading data only. Reading consists of getting
  13. NumPy buffers (often int16/int64) of signals/spikes/events.
  14. Scaling to real values (microV, times, ...) is done in a second step.
  15. Here the underlying objects must be consistent across Blocks and Segments for a given
  16. data source.
  17. The neo.rawio API has been added for developers.
  18. The neo.rawio is close to what could be a C API for reading data but in Python/NumPy.
  19. Not all IOs are implemented in :mod:`neo.rawio` but all classes implemented in :mod:`neo.rawio` are
  20. also available in :mod:`neo.io`.
  21. Possible uses of the :mod:`neo.rawio` API are:
  22. * fast reading chunks of signals in int16 and do the scaling of units (uV)
  23. on a GPU while scaling the zoom. This should improve bandwith HD to RAM
  24. and RAM to GPU memory.
  25. * load only some small chunk of data for heavy computations. For instance
  26. the spike sorting module tridesclous_ does this.
  27. The :mod:`neo.rawio` API is less flexible than :mod:`neo.io` and has some limitations:
  28. * read-only
  29. * AnalogSignals must have the same characteristcs across all Blocks and Segments:
  30. ``sampling_rate``, ``shape[1]``, ``dtype``
  31. * AnalogSignals should all have the same value of ``sampling_rate``, otherwise they won't be read
  32. at the same time.
  33. * Units must have SpikeTrain event if empty across all Block and Segment
  34. * Epoch and Event are processed the same way (with ``durations=None`` for Event).
  35. For an intuitive comparison of :mod:`neo.io` and :mod:`neo.rawio` see:
  36. * :file:`example/read_file_neo_io.py`
  37. * :file:`example/read_file_neo_rawio.py`
  38. One speculative benefit of the :mod:`neo.rawio` API should be that a developer
  39. should be able to code a new RawIO class with little knowledge of the Neo tree of
  40. objects or of the :mod:`quantities` package.
  41. Basic usage
  42. ===========
  43. First create a reader from a class::
  44. >>> from neo.rawio import PlexonRawIO
  45. >>> reader = PlexonRawIO(filename='File_plexon_3.plx')
  46. Then browse the internal header and display information::
  47. >>> reader.parse_header()
  48. >>> print(reader)
  49. PlexonRawIO: File_plexon_3.plx
  50. nb_block: 1
  51. nb_segment: [1]
  52. signal_channels: [V1]
  53. unit_channels: [Wspk1u, Wspk2u, Wspk4u, Wspk5u ... Wspk29u Wspk30u Wspk31u Wspk32u]
  54. event_channels: []
  55. You get the number of blocks and segments per block. You have information
  56. about channels: **signal_channels**, **unit_channels**, **event_channels**.
  57. All this information is internally available in the *header* dict::
  58. >>> for k, v in reader.header.items():
  59. ... print(k, v)
  60. signal_channels [('V1', 0, 1000., 'int16', '', 2.44140625, 0., 0)]
  61. event_channels []
  62. nb_segment [1]
  63. nb_block 1
  64. unit_channels [('Wspk1u', 'ch1#0', '', 0.00146484, 0., 0, 30000.)
  65. ('Wspk2u', 'ch2#0', '', 0.00146484, 0., 0, 30000.)
  66. ...
  67. Read signal chunks of data and scale them::
  68. >>> channel_indexes = None  #could be channel_indexes = [0]
  69. >>> raw_sigs = reader.get_analogsignal_chunk(block_index=0, seg_index=0,
  70. i_start=1024, i_stop=2048, channel_indexes=channel_indexes)
  71. >>> float_sigs = reader.rescale_signal_raw_to_float(raw_sigs, dtype='float64')
  72. >>> sampling_rate = reader.get_signal_sampling_rate()
  73. >>> t_start = reader.get_signal_t_start(block_index=0, seg_index=0)
  74. >>> units =reader.header['signal_channels'][0]['units']
  75. >>> print(raw_sigs.shape, raw_sigs.dtype)
  76. >>> print(float_sigs.shape, float_sigs.dtype)
  77. >>> print(sampling_rate, t_start, units)
  78. (1024, 1) int16
  79. (1024, 1) float64
  80. 1000.0 0.0 V
  81. There are 3 ways to select a subset of channels: by index (0 based), by id or by name.
  82. By index is not ambiguous 0 to n-1 (included), for some IOs channel_names (and sometimes channel_ids) have no guarantees to
  83. be unique, in such cases it would raise an error.
  84. Example with BlackrockRawIO for the file FileSpec2.3001::
  85. >>> raw_sigs = reader.get_analogsignal_chunk(channel_indexes=None) #Take all channels
  86. >>> raw_sigs1 = reader.get_analogsignal_chunk(channel_indexes=[0, 2, 4])) #Take 0 2 and 4
  87. >>> raw_sigs2 = reader.get_analogsignal_chunk(channel_ids=[1, 3, 5]) # Same but with there id (1 based)
  88. >>> raw_sigs3 = reader.get_analogsignal_chunk(channel_names=['chan1', 'chan3', 'chan5'])) # Same but with there name
  89. print(raw_sigs1.shape[1], raw_sigs2.shape[1], raw_sigs3.shape[1])
  90. 3, 3, 3
  91. Inspect units channel. Each channel gives a SpikeTrain for each Segment.
  92. Note that for many formats a physical channel can have several units after spike
  93. sorting. So the nb_unit could be more than physical channel or signal channels.
  94. >>> nb_unit = reader.unit_channels_count()
  95. >>> print('nb_unit', nb_unit)
  96. nb_unit 30
  97. >>> for unit_index in range(nb_unit):
  98. ... nb_spike = reader.spike_count(block_index=0, seg_index=0, unit_index=unit_index)
  99. ... print('unit_index', unit_index, 'nb_spike', nb_spike)
  100. unit_index 0 nb_spike 701
  101. unit_index 1 nb_spike 716
  102. unit_index 2 nb_spike 69
  103. unit_index 3 nb_spike 12
  104. unit_index 4 nb_spike 95
  105. unit_index 5 nb_spike 37
  106. unit_index 6 nb_spike 25
  107. unit_index 7 nb_spike 15
  108. unit_index 8 nb_spike 33
  109. ...
  110. Get spike timestamps only between 0 and 10 seconds and convert them to spike times::
  111. >>> spike_timestamps = reader.spike_timestamps(block_index=0, seg_index=0, unit_index=0,
  112. t_start=0., t_stop=10.)
  113. >>> print(spike_timestamps.shape, spike_timestamps.dtype, spike_timestamps[:5])
  114. (424,) int64 [ 90 420 708 1020 1310]
  115. >>> spike_times = reader.rescale_spike_timestamp( spike_timestamps, dtype='float64')
  116. >>> print(spike_times.shape, spike_times.dtype, spike_times[:5])
  117. (424,) float64 [ 0.003 0.014 0.0236 0.034 0.04366667]
  118. Get spike waveforms between 0 and 10 s::
  119. >>> raw_waveforms = reader.spike_raw_waveforms( block_index=0, seg_index=0, unit_index=0,
  120. t_start=0., t_stop=10.)
  121. >>> print(raw_waveforms.shape, raw_waveforms.dtype, raw_waveforms[0,0,:4])
  122. (424, 1, 64) int16 [-449 -206 34 40]
  123. >>> float_waveforms = reader.rescale_waveforms_to_float(raw_waveforms, dtype='float32', unit_index=0)
  124. >>> print(float_waveforms.shape, float_waveforms.dtype, float_waveforms[0,0,:4])
  125. (424, 1, 64) float32 [-0.65771484 -0.30175781 0.04980469 0.05859375]
  126. Count events per channel::
  127. >>> reader = PlexonRawIO(filename='File_plexon_2.plx')
  128. >>> reader.parse_header()
  129. >>> nb_event_channel = reader.event_channels_count()
  130. nb_event_channel 28
  131. >>> print('nb_event_channel', nb_event_channel)
  132. >>> for chan_index in range(nb_event_channel):
  133. ... nb_event = reader.event_count(block_index=0, seg_index=0, event_channel_index=chan_index)
  134. ... print('chan_index',chan_index, 'nb_event', nb_event)
  135. chan_index 0 nb_event 1
  136. chan_index 1 nb_event 0
  137. chan_index 2 nb_event 0
  138. chan_index 3 nb_event 0
  139. ...
  140. Read event timestamps and times for chanindex=0 and with time limits (t_start=None, t_stop=None)::
  141. >>> ev_timestamps, ev_durations, ev_labels = reader.event_timestamps(block_index=0, seg_index=0, event_channel_index=0,
  142. t_start=None, t_stop=None)
  143. >>> print(ev_timestamps, ev_durations, ev_labels)
  144. [1268] None ['0']
  145. >>> ev_times = reader.rescale_event_timestamp(ev_timestamps, dtype='float64')
  146. >>> print(ev_times)
  147. [ 0.0317]
  148. .. _tridesclous: https://github.com/tridesclous/tridesclous