Skip to content

Commit a10edc2

Browse files
committed
Fix conflict
2 parents a893a6a + 10c2884 commit a10edc2

File tree

12 files changed

+232
-28
lines changed

12 files changed

+232
-28
lines changed

doc/source/authors.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ and may not be the current affiliation of a contributor.
6060
* Etienne Combrisson [6]
6161
* Ben Dichter [24]
6262
* Elodie Legouée [21]
63+
* Heberto Mayorquin [24]
6364
* Thomas Perret [25]
6465

6566
1. Centre de Recherche en Neuroscience de Lyon, CNRS UMR5292 - INSERM U1028 - Universite Claude Bernard Lyon 1

neo/io/neomatlabio.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
SCIPY_ERR = None
3838

3939
from neo.io.baseio import BaseIO
40-
from neo.core import (Block, Segment, AnalogSignal, Event, Epoch, SpikeTrain,
41-
objectnames, class_by_name)
40+
from neo.core import (Block, Segment, AnalogSignal, IrregularlySampledSignal,
41+
Event, Epoch, SpikeTrain, objectnames, class_by_name)
4242

4343
classname_lower_to_upper = {}
4444
for k in objectnames:
@@ -190,7 +190,8 @@ class NeoMatlabIO(BaseIO):
190190
is_readable = True
191191
is_writable = True
192192

193-
supported_objects = [Block, Segment, AnalogSignal, Epoch, Event, SpikeTrain]
193+
supported_objects = [Block, Segment, AnalogSignal, IrregularlySampledSignal,
194+
Epoch, Event, SpikeTrain]
194195
readable_objects = [Block]
195196
writeable_objects = [Block]
196197

@@ -251,6 +252,10 @@ def write_block(self, bl, **kargs):
251252
anasig_struct = self.create_struct_from_obj(anasig)
252253
seg_struct['analogsignals'].append(anasig_struct)
253254

255+
for irrsig in seg.irregularlysampledsignals:
256+
irrsig_struct = self.create_struct_from_obj(irrsig)
257+
seg_struct['irregularlysampledsignals'].append(irrsig_struct)
258+
254259
for ea in seg.events:
255260
ea_struct = self.create_struct_from_obj(ea)
256261
seg_struct['events'].append(ea_struct)
@@ -348,7 +353,13 @@ def create_ob_from_struct(self, struct, classname):
348353
else:
349354
data_complement["t_start"] = 0.0
350355

351-
ob = cl(arr, **data_complement)
356+
if "times" in (at[0] for at in cl._necessary_attrs) and quantity_attr != "times":
357+
# handle IrregularlySampledSignal
358+
times = getattr(struct, "times")
359+
data_complement["time_units"] = getattr(struct, "times_units")
360+
ob = cl(times, arr, **data_complement)
361+
else:
362+
ob = cl(arr, **data_complement)
352363
else:
353364
ob = cl()
354365

neo/io/neurosharectypesio.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,12 @@ def read_segment(self, import_neuroshare_segment=True,
216216
ctypes.POINTER(ctypes.c_double)))
217217
total_read += pdwContCount.value
218218

219-
signal = pq.Quantity(pData, units=pAnalogInfo.szUnits.decode(), copy=False)
219+
try:
220+
signal = pq.Quantity(pData, units=pAnalogInfo.szUnits.decode(), copy=False)
221+
unit_annotation = None
222+
except LookupError:
223+
signal = pq.Quantity(pData, units='dimensionless', copy=False)
224+
unit_annotation = pAnalogInfo.szUnits.decode()
220225

221226
# t_start
222227
dwIndex = 0
@@ -229,6 +234,8 @@ def read_segment(self, import_neuroshare_segment=True,
229234
name=str(entityInfo.szEntityLabel),
230235
)
231236
anaSig.annotate(probe_info=str(pAnalogInfo.szProbeInfo))
237+
if unit_annotation is not None:
238+
anaSig.annotate(units=unit_annotation)
232239
seg.analogsignals.append(anaSig)
233240

234241
# segment

neo/io/nixio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,7 @@ def _write_spiketrain(self, spiketrain, nixblock, nixgroup):
11671167
return
11681168

11691169
if isinstance(spiketrain, BaseProxy):
1170-
spiketrain = spiketrain.load()
1170+
spiketrain = spiketrain.load(load_waveforms=True)
11711171

11721172
times = spiketrain.times.magnitude
11731173
tunits = units_to_string(spiketrain.times.units)

neo/io/nwbio.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ def _write_signal(self, nwbfile, signal, electrodes):
561561
additional_metadata["conversion"] = conversion
562562
else:
563563
units = signal.units
564+
if hasattr(signal, 'proxy_for') and signal.proxy_for in [AnalogSignal,
565+
IrregularlySampledSignal]:
566+
signal = signal.load()
564567
if isinstance(signal, AnalogSignal):
565568
sampling_rate = signal.sampling_rate.rescale("Hz")
566569
tS = timeseries_class(
@@ -597,20 +600,26 @@ def _write_signal(self, nwbfile, signal, electrodes):
597600
return tS
598601

599602
def _write_spiketrain(self, nwbfile, spiketrain):
603+
segment = spiketrain.segment
604+
if hasattr(spiketrain, 'proxy_for') and spiketrain.proxy_for is SpikeTrain:
605+
spiketrain = spiketrain.load()
600606
nwbfile.add_unit(spike_times=spiketrain.rescale('s').magnitude,
601607
obs_intervals=[[float(spiketrain.t_start.rescale('s')),
602608
float(spiketrain.t_stop.rescale('s'))]],
603609
_name=spiketrain.name,
604610
# _description=spiketrain.description,
605-
segment=spiketrain.segment.name,
606-
block=spiketrain.segment.block.name)
611+
segment=segment.name,
612+
block=segment.block.name)
607613
# todo: handle annotations (using add_unit_column()?)
608614
# todo: handle Neo Units
609615
# todo: handle spike waveforms, if any (see SpikeEventSeries)
610616
return nwbfile.units
611617

612618
def _write_event(self, nwbfile, event):
613-
hierarchy = {'block': event.segment.block.name, 'segment': event.segment.name}
619+
segment = event.segment
620+
if hasattr(event, 'proxy_for') and event.proxy_for == Event:
621+
event = event.load()
622+
hierarchy = {'block': segment.block.name, 'segment': segment.name}
614623
tS_evt = AnnotationSeries(
615624
name=event.name,
616625
data=event.labels,
@@ -621,13 +630,16 @@ def _write_event(self, nwbfile, event):
621630
return tS_evt
622631

623632
def _write_epoch(self, nwbfile, epoch):
633+
segment = epoch.segment
634+
if hasattr(epoch, 'proxy_for') and epoch.proxy_for == Epoch:
635+
epoch = epoch.load()
624636
for t_start, duration, label in zip(epoch.rescale('s').magnitude,
625637
epoch.durations.rescale('s').magnitude,
626638
epoch.labels):
627639
nwbfile.add_epoch(t_start, t_start + duration, [label], [],
628640
_name=epoch.name,
629-
segment=epoch.segment.name,
630-
block=epoch.segment.block.name)
641+
segment=segment.name,
642+
block=segment.block.name)
631643
return nwbfile.epochs
632644

633645

neo/rawio/neuroscoperawio.py

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,43 @@
1515
Author: Samuel Garcia
1616
1717
"""
18+
from pathlib import Path
1819

1920
from .baserawio import (BaseRawIO, _signal_channel_dtype, _signal_stream_dtype,
20-
_spike_channel_dtype, _event_channel_dtype)
21+
_spike_channel_dtype, _event_channel_dtype)
2122

2223
import numpy as np
2324
from xml.etree import ElementTree
2425

2526

2627
class NeuroScopeRawIO(BaseRawIO):
27-
extensions = ['xml', 'dat']
28+
extensions = ['xml', 'dat', 'lfp', 'eeg']
2829
rawmode = 'one-file'
2930

30-
def __init__(self, filename=''):
31+
def __init__(self, filename='', binary_file=None):
32+
"""raw reader for Neuroscope
33+
34+
Parameters
35+
----------
36+
filename : str, optional
37+
Usually the file_path of an xml file
38+
binary_file : _type_, optional
39+
The binary data file corresponding to the xml file:
40+
Supported formats: ['.dat', '.lfp', '.eeg']
41+
"""
3142
BaseRawIO.__init__(self)
3243
self.filename = filename
44+
self.binary_file = binary_file
3345

3446
def _source_name(self):
35-
return self.filename.replace('.xml', '').replace('.dat', '')
47+
return Path(self.filename).stem
3648

3749
def _parse_header(self):
38-
filename = self.filename.replace('.xml', '').replace('.dat', '')
50+
# Load the right paths to xml and data
51+
self._resolve_xml_and_data_paths()
3952

40-
tree = ElementTree.parse(filename + '.xml')
53+
# Parse XML-file
54+
tree = ElementTree.parse(self.xml_file_path)
4155
root = tree.getroot()
4256
acq = root.find('acquisitionSystem')
4357
nbits = int(acq.find('nBits').text)
@@ -64,7 +78,8 @@ def _parse_header(self):
6478
else:
6579
raise (NotImplementedError)
6680

67-
self._raw_signals = np.memmap(filename + '.dat', dtype=sig_dtype,
81+
# Extract signal from the data file
82+
self._raw_signals = np.memmap(self.data_file_path, dtype=sig_dtype,
6883
mode='r', offset=0).reshape(-1, nb_channel)
6984

7085
# one unique stream
@@ -122,3 +137,43 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop,
122137
channel_indexes = slice(None)
123138
raw_signals = self._raw_signals[slice(i_start, i_stop), channel_indexes]
124139
return raw_signals
140+
141+
def _resolve_xml_and_data_paths(self):
142+
file_path = Path(self.filename)
143+
supported_data_extensions = ['.dat', '.lfp', '.eeg']
144+
suffix = file_path.suffix
145+
if suffix == '.xml':
146+
xml_file_path = file_path
147+
data_file_path = self.binary_file
148+
# If no binary file provided iterate over the formats
149+
if data_file_path is None:
150+
for extension in supported_data_extensions:
151+
data_file_path = file_path.with_suffix(extension)
152+
if data_file_path.is_file():
153+
break
154+
assert data_file_path.is_file(), "data binary not found for file " \
155+
f"{data_file_path} with supported extensions: {supported_data_extensions}"
156+
elif suffix == '':
157+
xml_file_path = file_path.with_suffix(".xml")
158+
data_file_path = self.binary_file
159+
# Empty suffix to keep testing behavior with non-existing
160+
# file passing for backwards compatibility
161+
if data_file_path is None:
162+
data_file_path = file_path.with_suffix(".dat")
163+
elif suffix in supported_data_extensions:
164+
xml_file_path = file_path.with_suffix(".xml")
165+
data_file_path = file_path
166+
else:
167+
error_string = (
168+
f"Format {suffix} not supported "
169+
f"filename format should be {supported_data_extensions} or .xml"
170+
)
171+
raise KeyError(error_string)
172+
173+
msg = f"xml file not found at the expected location {xml_file_path}"
174+
assert xml_file_path.is_file(), msg
175+
msg = f"binary file not found at the expected location {data_file_path}"
176+
assert data_file_path.is_file(), msg
177+
178+
self.xml_file_path = xml_file_path
179+
self.data_file_path = data_file_path

neo/rawio/spikeglxrawio.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
Some functions are copied from Graham Findlay
4646
"""
4747

48+
import warnings
4849
from .baserawio import (BaseRawIO, _signal_channel_dtype, _signal_stream_dtype,
4950
_spike_channel_dtype, _event_channel_dtype)
5051

@@ -255,6 +256,10 @@ def scan_files(dirname):
255256
info['meta_file'] = str(meta_filename)
256257
info['bin_file'] = str(bin_filename)
257258
info_list.append(info)
259+
260+
# Let see if this will be anoying or not.
261+
if bin_filename.stat().st_size != meta['fileSizeBytes']:
262+
warnings.warn('.meta file has faulty value for .bin file size on disc')
258263

259264
# the segment index will depend on both 'gate_num' and 'trigger_num'
260265
# so we order by 'gate_num' then 'trigger_num'

neo/test/iotest/test_neomatlabio.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
"""
44

55
import unittest
6-
6+
from numpy.testing import assert_array_equal
77
import quantities as pq
8+
9+
from neo.core.analogsignal import AnalogSignal
10+
from neo.core.irregularlysampledsignal import IrregularlySampledSignal
811
from neo import Block, Segment, SpikeTrain
912
from neo.test.iotest.common_io_test import BaseTestIO
1013
from neo.io.neomatlabio import NeoMatlabIO, HAVE_SCIPY
@@ -21,8 +24,12 @@ def test_write_read_single_spike(self):
2124
seg = Segment('segment1')
2225
spiketrain1 = SpikeTrain([1] * pq.s, t_stop=10 * pq.s, sampling_rate=1 * pq.Hz)
2326
spiketrain1.annotate(yep='yop')
27+
sig1 = AnalogSignal([4, 5, 6] * pq.A, sampling_period=1 * pq.ms)
28+
irrsig1 = IrregularlySampledSignal([0, 1, 2] * pq.ms, [4, 5, 6] * pq.A)
2429
block1.segments.append(seg)
2530
seg.spiketrains.append(spiketrain1)
31+
seg.analogsignals.append(sig1)
32+
seg.irregularlysampledsignals.append(irrsig1)
2633

2734
# write block
2835
filename = self.get_local_path('matlabiotestfile.mat')
@@ -36,6 +43,14 @@ def test_write_read_single_spike(self):
3643
self.assertEqual(block1.segments[0].spiketrains[0],
3744
block2.segments[0].spiketrains[0])
3845

46+
assert_array_equal(block1.segments[0].analogsignals[0],
47+
block2.segments[0].analogsignals[0])
48+
49+
assert_array_equal(block1.segments[0].irregularlysampledsignals[0].magnitude,
50+
block2.segments[0].irregularlysampledsignals[0].magnitude)
51+
assert_array_equal(block1.segments[0].irregularlysampledsignals[0].times,
52+
block2.segments[0].irregularlysampledsignals[0].times)
53+
3954
# test annotations
4055
spiketrain2 = block2.segments[0].spiketrains[0]
4156
assert 'yep' in spiketrain2.annotations

neo/test/iotest/test_neuroshareio.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ def setUp(self):
6565
raise unittest.SkipTest("Not currently supported on OS X")
6666

6767
def test_with_multichannel(self):
68-
filename0 = self.get_local_path(self.files_to_test[0])
69-
reader = NeuroshareIO(filename0, self.dllname)
70-
blocks = reader.read()
71-
n = len(blocks[0].segments[0].analogsignals)
72-
assert n == 2, \
73-
'For {} , nb AnalogSignal: {} (should be 2)'.format(self.files_to_download[0], n)
68+
for filename in self.files_to_test:
69+
filename = self.get_local_path(filename)
70+
reader = NeuroshareIO(filename, self.dllname)
71+
blocks = reader.read()
72+
n = len(blocks[0].segments[0].analogsignals)
73+
assert n > 0, f'Expect signals in file {filename}'
7474

7575

7676
if __name__ == "__main__":

neo/test/iotest/test_nixio.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,10 @@ def compare_signal_dalist(self, neosig, nixdalist):
168168
def compare_eests_mtags(self, eestlist, mtaglist):
169169
self.assertEqual(len(eestlist), len(mtaglist))
170170
for eest in eestlist:
171-
if isinstance(eest, (EventProxy, EpochProxy, SpikeTrainProxy)):
171+
if isinstance(eest, (EventProxy, EpochProxy)):
172172
eest = eest.load()
173+
elif isinstance(eest, SpikeTrainProxy):
174+
eest = eest.load(load_waveforms=True)
173175
mtag = mtaglist[eest.annotations["nix_name"]]
174176
if isinstance(eest, Epoch):
175177
self.compare_epoch_mtag(eest, mtag)

0 commit comments

Comments
 (0)