Skip to content

Commit 78f3f33

Browse files
Merge pull request #949 from samuelgarcia/rawio_improvement
Rawio improvement
2 parents e3d507f + c5efe78 commit 78f3f33

39 files changed

+1666
-1391
lines changed

doc/source/rawio.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ Then browse the internal header and display information::
7777
nb_block: 1
7878
nb_segment: [1]
7979
signal_channels: [V1]
80-
unit_channels: [Wspk1u, Wspk2u, Wspk4u, Wspk5u ... Wspk29u Wspk30u Wspk31u Wspk32u]
80+
spike_channels: [Wspk1u, Wspk2u, Wspk4u, Wspk5u ... Wspk29u Wspk30u Wspk31u Wspk32u]
8181
event_channels: []
8282

8383
You get the number of blocks and segments per block. You have information
84-
about channels: **signal_channels**, **unit_channels**, **event_channels**.
84+
about channels: **signal_channels**, **spike_channels**, **event_channels**.
8585

8686
All this information is internally available in the *header* dict::
8787

@@ -91,7 +91,7 @@ All this information is internally available in the *header* dict::
9191
event_channels []
9292
nb_segment [1]
9393
nb_block 1
94-
unit_channels [('Wspk1u', 'ch1#0', '', 0.00146484, 0., 0, 30000.)
94+
spike_channels [('Wspk1u', 'ch1#0', '', 0.00146484, 0., 0, 30000.)
9595
('Wspk2u', 'ch2#0', '', 0.00146484, 0., 0, 30000.)
9696
...
9797

@@ -141,7 +141,7 @@ Inspect units channel. Each channel gives a SpikeTrain for each Segment.
141141
Note that for many formats a physical channel can have several units after spike
142142
sorting. So the nb_unit could be more than physical channel or signal channels.
143143

144-
>>> nb_unit = reader.unit_channels_count()
144+
>>> nb_unit = reader.spike_channels_count()
145145
>>> print('nb_unit', nb_unit)
146146
nb_unit 30
147147
>>> for unit_index in range(nb_unit):

examples/read_files_neo_rawio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
print(float_sigs.shape, float_sigs.dtype)
3232
print(sampling_rate, t_start, units)
3333

34-
# Count unit and spike per units
35-
nb_unit = reader.unit_channels_count()
34+
# Count units and spikes per unit
35+
nb_unit = reader.spike_channels_count()
3636
print('nb_unit', nb_unit)
3737
for unit_index in range(nb_unit):
3838
nb_spike = reader.spike_count(block_index=0, seg_index=0, unit_index=unit_index)

neo/io/basefromrawio.py

Lines changed: 79 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -134,31 +134,24 @@ def read_block(self, block_index=0, lazy=False,
134134

135135
bl = Block(**bl_annotations)
136136

137-
# Group for AnalogSignals
137+
# Group for AnalogSignals coming from signal_streams
138138
if create_group_across_segment['AnalogSignal']:
139-
all_channels = self.header['signal_channels']
140-
channel_indexes_list = self.get_group_signal_channel_indexes()
141-
sig_groups = []
142-
for channel_index in channel_indexes_list:
143-
for i, (ind_within, ind_abs) in self._make_signal_channel_subgroups(
144-
channel_index, signal_group_mode=signal_group_mode).items():
145-
group = Group(name='AnalogSignal group {}'.format(i))
146-
# @andrew @ julia @michael : do we annotate group across segment with this arrays ?
147-
group.annotate(ch_names=all_channels[ind_abs]['name'].astype('U')) # ??
148-
group.annotate(channel_ids=all_channels[ind_abs]['id']) # ??
149-
bl.groups.append(group)
150-
sig_groups.append(group)
139+
signal_streams = self.header['signal_streams']
140+
sub_streams = self.get_sub_signal_streams(signal_group_mode)
141+
sub_stream_groups = []
142+
for sub_stream in sub_streams:
143+
stream_index, inner_stream_channels, name = sub_stream
144+
group = Group(name=name, stream_id=signal_streams[stream_index]['id'])
145+
bl.groups.append(group)
146+
sub_stream_groups.append(group)
151147

152148
if create_group_across_segment['SpikeTrain']:
153-
unit_channels = self.header['unit_channels']
149+
spike_channels = self.header['spike_channels']
154150
st_groups = []
155-
for c in range(unit_channels.size):
151+
for c in range(spike_channels.size):
156152
group = Group(name='SpikeTrain group {}'.format(c))
157-
group.annotate(unit_name=unit_channels[c]['name'])
158-
group.annotate(unit_id=unit_channels[c]['id'])
159-
unit_annotations = self.raw_annotations['unit_channels'][c]
160-
unit_annotations = check_annotations(unit_annotations)
161-
group.annotate(**unit_annotations)
153+
group.annotate(unit_name=spike_channels[c]['name'])
154+
group.annotate(unit_id=spike_channels[c]['id'])
162155
bl.groups.append(group)
163156
st_groups.append(group)
164157

@@ -183,7 +176,7 @@ def read_block(self, block_index=0, lazy=False,
183176
for seg in bl.segments:
184177
if create_group_across_segment['AnalogSignal']:
185178
for c, anasig in enumerate(seg.analogsignals):
186-
sig_groups[c].add(anasig)
179+
sub_stream_groups[c].add(anasig)
187180

188181
if create_group_across_segment['SpikeTrain']:
189182
for c, sptr in enumerate(seg.spiketrains):
@@ -231,38 +224,35 @@ def read_segment(self, block_index=0, seg_index=0, lazy=False,
231224
signal_group_mode = self._prefered_signal_group_mode
232225

233226
# annotations
234-
seg_annotations = dict(self.raw_annotations['blocks'][block_index]['segments'][seg_index])
235-
for k in ('signals', 'units', 'events'):
227+
seg_annotations = self.raw_annotations['blocks'][block_index]['segments'][seg_index].copy()
228+
for k in ('signals', 'spikes', 'events'):
236229
seg_annotations.pop(k)
237230
seg_annotations = check_annotations(seg_annotations)
238231

239232
seg = Segment(index=seg_index, **seg_annotations)
240233

241234
# AnalogSignal
242-
signal_channels = self.header['signal_channels']
243-
if signal_channels.size > 0:
244-
channel_indexes_list = self.get_group_signal_channel_indexes()
245-
for channel_indexes in channel_indexes_list:
246-
for i, (ind_within, ind_abs) in self._make_signal_channel_subgroups(
247-
channel_indexes,
248-
signal_group_mode=signal_group_mode).items():
249-
# make a proxy...
250-
anasig = AnalogSignalProxy(rawio=self, global_channel_indexes=ind_abs,
251-
block_index=block_index, seg_index=seg_index)
252-
253-
if not lazy:
254-
# ... and get the real AnalogSIgnal if not lazy
255-
anasig = anasig.load(time_slice=time_slice, strict_slicing=strict_slicing)
256-
# TODO magnitude_mode='rescaled'/'raw'
257-
258-
anasig.segment = seg
259-
seg.analogsignals.append(anasig)
235+
signal_streams = self.header['signal_streams']
236+
sub_streams = self.get_sub_signal_streams(signal_group_mode)
237+
for sub_stream in sub_streams:
238+
stream_index, inner_stream_channels, name = sub_stream
239+
anasig = AnalogSignalProxy(rawio=self, stream_index=stream_index,
240+
inner_stream_channels=inner_stream_channels,
241+
block_index=block_index, seg_index=seg_index)
242+
anasig.name = name
243+
244+
if not lazy:
245+
# ... and get the real AnalogSignal if not lazy
246+
anasig = anasig.load(time_slice=time_slice, strict_slicing=strict_slicing)
247+
248+
anasig.segment = seg
249+
seg.analogsignals.append(anasig)
260250

261251
# SpikeTrain and waveforms (optional)
262-
unit_channels = self.header['unit_channels']
263-
for unit_index in range(len(unit_channels)):
252+
spike_channels = self.header['spike_channels']
253+
for spike_channel_index in range(len(spike_channels)):
264254
# make a proxy...
265-
sptr = SpikeTrainProxy(rawio=self, unit_index=unit_index,
255+
sptr = SpikeTrainProxy(rawio=self, spike_channel_index=spike_channel_index,
266256
block_index=block_index, seg_index=seg_index)
267257

268258
if not lazy:
@@ -286,7 +276,7 @@ def read_segment(self, block_index=0, seg_index=0, lazy=False,
286276
seg.events.append(e)
287277
elif event_channels['type'][chan_ind] == b'epoch':
288278
e = EpochProxy(rawio=self, event_channel_index=chan_ind,
289-
block_index=block_index, seg_index=seg_index)
279+
block_index=block_index, seg_index=seg_index)
290280
if not lazy:
291281
e = e.load(time_slice=time_slice, strict_slicing=strict_slicing)
292282
e.segment = seg
@@ -295,37 +285,50 @@ def read_segment(self, block_index=0, seg_index=0, lazy=False,
295285
seg.create_many_to_one_relationship()
296286
return seg
297287

298-
def _make_signal_channel_subgroups(self, channel_indexes,
299-
signal_group_mode='group-by-same-units'):
288+
def get_sub_signal_streams(self, signal_group_mode='group-by-same-units'):
300289
"""
301-
For some RawIO channel are already splitted in groups.
302-
But in any cases, channel need to be splitted again in sub groups
303-
because they do not have the same units.
304-
305-
They can also be splitted one by one to match previous behavior for
306-
some IOs in older version of neo (<=0.5).
290+
When signal streams don't have homogeneous SI units across channels,
291+
they have to be split in sub streams to construct AnalogSignal objects with unique units.
307292
308-
This method aggregate signal channels with same units or split them all.
293+
For backward compatibility (neo version <= 0.5) sub-streams can also be
294+
used to generate one AnalogSignal per channel.
309295
"""
310-
all_channels = self.header['signal_channels']
311-
if channel_indexes is None:
312-
channel_indexes = np.arange(all_channels.size, dtype=int)
313-
channels = all_channels[channel_indexes]
314-
315-
groups = collections.OrderedDict()
316-
if signal_group_mode == 'group-by-same-units':
317-
all_units = np.unique(channels['units'])
318-
319-
for i, unit in enumerate(all_units):
320-
ind_within, = np.nonzero(channels['units'] == unit)
321-
ind_abs = channel_indexes[ind_within]
322-
groups[i] = (ind_within, ind_abs)
323-
324-
elif signal_group_mode == 'split-all':
325-
for i, chan_index in enumerate(channel_indexes):
326-
ind_within = [i]
327-
ind_abs = channel_indexes[ind_within]
328-
groups[i] = (ind_within, ind_abs)
329-
else:
330-
raise (NotImplementedError)
331-
return groups
296+
signal_streams = self.header['signal_streams']
297+
signal_channels = self.header['signal_channels']
298+
299+
sub_streams = []
300+
for stream_index in range(len(signal_streams)):
301+
stream_id = signal_streams[stream_index]['id']
302+
stream_name = signal_streams[stream_index]['name']
303+
mask = signal_channels['stream_id'] == stream_id
304+
channels = signal_channels[mask]
305+
if signal_group_mode == 'group-by-same-units':
306+
# this does not keep the original order
307+
_, idx = np.unique(channels['units'], return_index=True)
308+
all_units = channels['units'][np.sort(idx)]
309+
310+
if len(all_units) == 1:
311+
# no substream
312+
#  None iwill be transform as slice later
313+
inner_stream_channels = None
314+
name = stream_name
315+
sub_stream = (stream_index, inner_stream_channels, name)
316+
sub_streams.append(sub_stream)
317+
else:
318+
for units in all_units:
319+
inner_stream_channels, = np.nonzero(channels['units'] == units)
320+
chan_names = channels[inner_stream_channels]['name']
321+
name = 'Channels: (' + ' '.join(chan_names) + ')'
322+
sub_stream = (stream_index, inner_stream_channels, name)
323+
sub_streams.append(sub_stream)
324+
elif signal_group_mode == 'split-all':
325+
# mimic all neo <= 0.5 behavior
326+
for i, channel in enumerate(channels):
327+
inner_stream_channels = [i]
328+
name = channels[i]['name']
329+
sub_stream = (stream_index, inner_stream_channels, name)
330+
sub_streams.append(sub_stream)
331+
else:
332+
raise (NotImplementedError)
333+
334+
return sub_streams

0 commit comments

Comments
 (0)