Skip to content

Commit b42c10b

Browse files
authored
Merge pull request #927 from alejoe91/mearecio
Implemented MEArecIO
2 parents da05655 + 8e5e517 commit b42c10b

File tree

8 files changed

+227
-0
lines changed

8 files changed

+227
-0
lines changed

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ jobs:
4040
pip install -r requirements.txt
4141
pip install -r .circleci/requirements_testing.txt
4242
pip install .
43+
# install packages that depend on neo
44+
pip install MEArec
4345
pip freeze
4446
4547
- save_cache:

doc/source/authors.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ and may not be the current affiliation of a contributor.
5353
* Hugo van Kemenade
5454
* Aitor Morales-Gregorio [13]
5555
* Peter N Steinmetz [22]
56+
* Alessio Buccino [23]
5657

5758
1. Centre de Recherche en Neuroscience de Lyon, CNRS UMR5292 - INSERM U1028 - Universite Claude Bernard Lyon 1
5859
2. Unité de Neuroscience, Information et Complexité, CNRS UPR 3293, Gif-sur-Yvette, France
@@ -76,6 +77,7 @@ and may not be the current affiliation of a contributor.
7677
20. Harden Technologies, LLC
7778
21. Institut des Neurosciences Paris-Saclay, CNRS UMR 9197 - Université Paris-Sud, Gif-sur-Yvette, France
7879
22. Neurtex Brain Research Institute, Dallas, TX, USAs
80+
23. Bio Engineering Laboratory, DBSSE, ETH, Basel, Switzerland
7981

8082
If we've somehow missed you off the list we're very sorry - please let us know.
8183

neo/io/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* :attr:`ElanIO`
3131
* :attr:`IgorIO`
3232
* :attr:`IntanIO`
33+
* :attr:`MEArecIO`
3334
* :attr:`KlustaKwikIO`
3435
* :attr:`KwikIO`
3536
* :attr:`MicromedIO`
@@ -132,6 +133,10 @@
132133
133134
.. autoattribute:: extensions
134135
136+
.. autoclass:: neo.io.MEArecIO
137+
138+
.. autoattribute:: extensions
139+
135140
.. autoclass:: neo.io.MicromedIO
136141
137142
.. autoattribute:: extensions
@@ -261,6 +266,7 @@
261266
from neo.io.intanio import IntanIO
262267
from neo.io.klustakwikio import KlustaKwikIO
263268
from neo.io.kwikio import KwikIO
269+
from neo.io.mearecio import MEArecIO
264270
from neo.io.micromedio import MicromedIO
265271
from neo.io.hdf5io import NeoHdf5IO
266272
from neo.io.neomatlabio import NeoMatlabIO
@@ -306,6 +312,7 @@
306312
IntanIO,
307313
KlustaKwikIO,
308314
KwikIO,
315+
MEArecIO,
309316
MicromedIO,
310317
NixIO, # place NixIO before NeoHdf5IO to make it the default for .h5 files
311318
NeoHdf5IO,

neo/io/mearecio.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from neo.io.basefromrawio import BaseFromRaw
2+
from neo.rawio.mearecrawio import MEArecRawIO
3+
4+
5+
class MEArecIO(MEArecRawIO, BaseFromRaw):
6+
__doc__ = MEArecRawIO.__doc__
7+
mode = 'file'
8+
9+
def __init__(self, filename):
10+
MEArecRawIO.__init__(self, filename=filename)
11+
BaseFromRaw.__init__(self, filename)

neo/rawio/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* :attr:`BrainVisionRawIO`
1919
* :attr:`ElanRawIO`
2020
* :attr:`IntanRawIO`
21+
* :attr:`MEArecRawIO`
2122
* :attr:`MicromedRawIO`
2223
* :attr:`NeuralynxRawIO`
2324
* :attr:`NeuroExplorerRawIO`
@@ -58,6 +59,10 @@
5859
5960
.. autoattribute:: extensions
6061
62+
.. autoclass:: neo.rawio.MEArecRawIO
63+
64+
.. autoattribute:: extensions
65+
6166
.. autoclass:: neo.rawio.MicromedRawIO
6267
6368
.. autoattribute:: extensions
@@ -124,6 +129,7 @@
124129
from neo.rawio.elanrawio import ElanRawIO
125130
from neo.rawio.examplerawio import ExampleRawIO
126131
from neo.rawio.intanrawio import IntanRawIO
132+
from neo.rawio.mearecrawio import MEArecRawIO
127133
from neo.rawio.micromedrawio import MicromedRawIO
128134
from neo.rawio.neuralynxrawio.neuralynxrawio import NeuralynxRawIO
129135
from neo.rawio.neuroexplorerrawio import NeuroExplorerRawIO
@@ -147,6 +153,7 @@
147153
ElanRawIO,
148154
IntanRawIO,
149155
MicromedRawIO,
156+
MEArecRawIO,
150157
NeuralynxRawIO,
151158
NeuroExplorerRawIO,
152159
NeuroScopeRawIO,

neo/rawio/mearecrawio.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
Class for reading data from a MEArec simulated data.
3+
4+
See:
5+
https://mearec.readthedocs.io/en/latest/
6+
https://github.com/alejoe91/MEArec
7+
https://link.springer.com/article/10.1007/s12021-020-09467-7
8+
9+
Author : Alessio Buccino
10+
"""
11+
12+
from .baserawio import (BaseRawIO, _signal_channel_dtype, _unit_channel_dtype,
13+
_event_channel_dtype)
14+
15+
import numpy as np
16+
from copy import deepcopy
17+
18+
19+
class MEArecRawIO(BaseRawIO):
20+
"""
21+
Class for "reading" fake data from a MEArec file.
22+
23+
Usage:
24+
>>> import neo.rawio
25+
>>> r = neo.rawio.MEArecRawIO(filename='mearec.h5')
26+
>>> r.parse_header()
27+
>>> print(r)
28+
>>> raw_chunk = r.get_analogsignal_chunk(block_index=0, seg_index=0,
29+
i_start=0, i_stop=1024, channel_names=channel_names)
30+
>>> float_chunk = reader.rescale_signal_raw_to_float(raw_chunk, dtype='float64',
31+
channel_indexes=[0, 3, 6])
32+
>>> spike_timestamp = reader.spike_timestamps(unit_index=0, t_start=None, t_stop=None)
33+
>>> spike_times = reader.rescale_spike_timestamp(spike_timestamp, 'float64')
34+
35+
"""
36+
extensions = ['h5']
37+
rawmode = 'one-file'
38+
39+
def __init__(self, filename=''):
40+
BaseRawIO.__init__(self)
41+
self.filename = filename
42+
43+
def _source_name(self):
44+
return self.filename
45+
46+
def _parse_header(self):
47+
try:
48+
import MEArec as mr
49+
HAVE_MEAREC = True
50+
except ImportError:
51+
HAVE_MEAREC = False
52+
assert HAVE_MEAREC, 'MEArec is not installed'
53+
self._recgen = mr.load_recordings(recordings=self.filename, return_h5_objects=True,
54+
check_suffix=False,
55+
load=['recordings', 'spiketrains', 'channel_positions'],
56+
load_waveforms=False)
57+
self._sampling_rate = self._recgen.info['recordings']['fs']
58+
self._recordings = self._recgen.recordings
59+
self._num_frames, self._num_channels = self._recordings.shape
60+
sig_channels = []
61+
for c in range(self._num_channels):
62+
ch_name = 'ch{}'.format(c)
63+
chan_id = c + 1
64+
sr = self._sampling_rate # Hz
65+
dtype = self._recordings.dtype
66+
units = 'uV'
67+
gain = 1.
68+
offset = 0.
69+
group_id = 0
70+
sig_channels.append((ch_name, chan_id, sr, dtype, units, gain, offset, group_id))
71+
sig_channels = np.array(sig_channels, dtype=_signal_channel_dtype)
72+
73+
# creating units channels
74+
unit_channels = []
75+
self._spiketrains = self._recgen.spiketrains
76+
for c in range(len(self._spiketrains)):
77+
unit_name = 'unit{}'.format(c)
78+
unit_id = '#{}'.format(c)
79+
# if spiketrains[c].waveforms is not None:
80+
wf_units = ''
81+
wf_gain = 1.
82+
wf_offset = 0.
83+
wf_left_sweep = 0
84+
wf_sampling_rate = self._sampling_rate
85+
unit_channels.append((unit_name, unit_id, wf_units, wf_gain,
86+
wf_offset, wf_left_sweep, wf_sampling_rate))
87+
unit_channels = np.array(unit_channels, dtype=_unit_channel_dtype)
88+
89+
event_channels = []
90+
event_channels = np.array(event_channels, dtype=_event_channel_dtype)
91+
92+
self.header = {}
93+
self.header['nb_block'] = 1
94+
self.header['nb_segment'] = [1]
95+
self.header['signal_channels'] = sig_channels
96+
self.header['unit_channels'] = unit_channels
97+
self.header['event_channels'] = event_channels
98+
99+
self._generate_minimal_annotations()
100+
for block_index in range(1):
101+
bl_ann = self.raw_annotations['blocks'][block_index]
102+
bl_ann['mearec_info'] = deepcopy(self._recgen.info)
103+
104+
def _segment_t_start(self, block_index, seg_index):
105+
all_starts = [[0.]]
106+
return all_starts[block_index][seg_index]
107+
108+
def _segment_t_stop(self, block_index, seg_index):
109+
t_stop = self._num_frames / self._sampling_rate
110+
all_stops = [[t_stop]]
111+
return all_stops[block_index][seg_index]
112+
113+
def _get_signal_size(self, block_index, seg_index, channel_indexes=None):
114+
return self._num_frames
115+
116+
def _get_signal_t_start(self, block_index, seg_index, channel_indexes):
117+
return self._segment_t_start(block_index, seg_index)
118+
119+
def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes):
120+
if i_start is None:
121+
i_start = 0
122+
if i_stop is None:
123+
i_stop = self._num_frames
124+
125+
if channel_indexes is None:
126+
channel_indexes = slice(self._num_channels)
127+
128+
raw_signals = self._recgen.recordings[i_start:i_stop, channel_indexes]
129+
return raw_signals
130+
131+
def _spike_count(self, block_index, seg_index, unit_index):
132+
return len(self._spiketrains[unit_index])
133+
134+
def _get_spike_timestamps(self, block_index, seg_index, unit_index, t_start, t_stop):
135+
spike_timestamps = self._spiketrains[unit_index].times.magnitude
136+
if t_start is None:
137+
t_start = self._segment_t_start(block_index, seg_index)
138+
if t_stop is None:
139+
t_stop = self._segment_t_stop(block_index, seg_index)
140+
timestamp_idxs = np.where((spike_timestamps >= t_start) & (spike_timestamps < t_stop))
141+
142+
return spike_timestamps[timestamp_idxs]
143+
144+
def _rescale_spike_timestamp(self, spike_timestamps, dtype):
145+
return spike_timestamps.astype(dtype)
146+
147+
def _get_spike_raw_waveforms(self, block_index, seg_index, unit_index, t_start, t_stop):
148+
return None
149+
150+
def _event_count(self, block_index, seg_index, event_channel_index):
151+
return None
152+
153+
def _get_event_timestamps(self, block_index, seg_index, event_channel_index, t_start, t_stop):
154+
return None
155+
156+
def _rescale_event_timestamp(self, event_timestamps, dtype):
157+
return None
158+
159+
def _rescale_epoch_duration(self, raw_duration, dtype):
160+
return None

neo/test/iotest/test_mearecio.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
Tests of neo.io.mearecio
3+
"""
4+
5+
import unittest
6+
7+
from neo.io import MEArecIO
8+
from neo.test.iotest.common_io_test import BaseTestIO
9+
10+
11+
class TestMEArecIO(BaseTestIO, unittest.TestCase):
12+
files_to_test = ['mearec_test_10s.h5']
13+
files_to_download = ['mearec_test_10s.h5']
14+
ioclass = MEArecIO
15+
16+
17+
if __name__ == "__main__":
18+
unittest.main()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
Tests of neo.rawio.mearecrawio
3+
4+
"""
5+
6+
import unittest
7+
8+
from neo.rawio.mearecrawio import MEArecRawIO
9+
10+
from neo.test.rawiotest.common_rawio_test import BaseTestRawIO
11+
12+
13+
class TestMEArecRawIO(BaseTestRawIO, unittest.TestCase, ):
14+
rawioclass = MEArecRawIO
15+
files_to_download = ['mearec_test_10s.h5']
16+
entities_to_test = ['mearec_test_10s.h5']
17+
18+
19+
if __name__ == "__main__":
20+
unittest.main()

0 commit comments

Comments
 (0)