Skip to content

Commit 8dfb079

Browse files
PeterNSteinmetzPeter N. Steinmetz
andauthored
Nlx single file mode (#957)
* Move constants into class so accessible for testing. * Test of building NcsBlocks. * Handle old files with truncated frequency in header and test. * Change interface on parse versus build. * Tests for PRE4 type and code corrections. Tests for v5.5.1 still failing. * Fix initializer, update loop vars. * Add additional tests on v5.5.1 with 2 blocks * Tests of block construction for incomplete blocks * Fix up single block case. * Remove unneeded classes. Clean up style. * Use private dtype by new private name. * Add test of side effects of read_ncs_files * Use NcsBlocksFactory and logical or. * Fix off by one in range for list. Comments. * Use standard time calculation for last time of block. * Remove test with tolerance over whole length. Fix microsPerSampUsed assignement. Using a tolerance over a longer experiment is not sensitive enough to detect blocks where perhaps a large amount of samples are dropped and there is a small gap afterwards. * Tests of raw io for incomplete records multiple block case. * Update stop times to include time for samples in partially filled records. * PEP and style cleanup. Corrected gap comment. * Line shortening for PEP8. * More PEP8 items. * Correct column error in data tested. Strange this hasn’t caused issues before with other file sets. * Update comment on PRE4 file limitations. * Tests for PRE4 file type in Cheetah v4.0.2. * PEP8 indent corrections. * Small PEP8 correctin. * Proper time opened header for BML and use of frequency. * Download files and activate tests for BML style files. * record start and end times in NcsBlocks for verification later * Code to handle digitallynx with cheetah v1 Like files from Dr. Florian Mormann’s lab. * Fixes to handle split block. * Tests NcsBlockFactory with split blocks and unfilled records. * Tests of data read in ncs files with partial records and split blocks. * Handle testing less data than available in all segments. * Read and test with partial blocks. Test all types in test_neuralyxio. * Read and test with partial blocks. Test all types in test_neuralyxio. * PEP8 fix ups. * Language and function renaming. # Conflicts: # neo/rawio/neuralynxrawio.py * Language and function renaming. * Move constants into class so accessible for testing. * Test of building NcsBlocks. * Handle old files with truncated frequency in header and test. * Change interface on parse versus build. # Conflicts: # neo/rawio/neuralynxrawio.py * Tests for PRE4 type and code corrections. Tests for v5.5.1 still failing. # Conflicts: # neo/rawio/neuralynxrawio.py * Fix initializer, update loop vars. * Add additional tests on v5.5.1 with 2 blocks * Tests of block construction for incomplete blocks * Fix up single block case. * Remove unneeded classes. Clean up style. # Conflicts: # neo/rawio/neuralynxrawio.py * Add test of side effects of read_ncs_files * Use NcsBlocksFactory and logical or. # Conflicts: # neo/rawio/neuralynxrawio.py * Fix off by one in range for list. Comments. * Use standard time calculation for last time of block. * Remove test with tolerance over whole length. Fix microsPerSampUsed assignement. Using a tolerance over a longer experiment is not sensitive enough to detect blocks where perhaps a large amount of samples are dropped and there is a small gap afterwards. * Tests of raw io for incomplete records multiple block case. * Update stop times to include time for samples in partially filled records. * PEP and style cleanup. Corrected gap comment. * Line shortening for PEP8. * More PEP8 items. # Conflicts: # neo/test/rawiotest/test_neuralynxrawio.py * Remove conflict markers. * More conflict resolution for rebase. * verify block function and test * Call renamed functions. * use _verifyBlockStructure to ensure same structure This is hopefully a bit faster than building NcsBlocks for each file. * Properly limit samples compared. * Temporarily remove data test. This matches apdavison’s from commit 2ac9596. I can’t find and revert the commit where I undid that change. * Remove duplication of some older tests. * Comments on structure of segment defining fields. * Factor out NcsBlock object. This to facilitate temporal comparisions needed for segment definitions when channels may have different recording lengths. * Change factory methods to use NcsBlock. * Rewrite tests for NcsBlocks refactoring. * More refactoring of NcsBlocks. * Gather all NcsBlocks first, then build segements. Builds NcsBlocks for all unique structures in all files. Then sets up segment structure based on these. A precursor to more sophisticated segments for record block structures. * change name to _scan_ncs_files rather than read * Proper formatting of number of NcsBlock structures. * Move neuralynxrawio to subdirectory to factor our other classes. * Eliminate WholeTimesPositionBlock as class. * Factor helper classes out in package. * Define data returned by scan_ncs_files and rename. * Return results from scan_ncs_files and unpack them into private atttributes. * Test returned results. * Fix for version 5.4.0 headers without : * Allow gap which is fraction of sample time, rather than fixed micros. This important when sampling rates are lower. * Test changes for Cheetah v5.4.0. * change wording about ambiguity * Add docstring about required signature with sets of channels. * Add comment about restrictions on selected set of channels. * Single file mode and tests. * Clarify use of dirname or filename, but not both. * Test no nev filenames. Co-authored-by: Peter N. Steinmetz <[email protected]>
1 parent bc60370 commit 8dfb079

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

neo/rawio/neuralynxrawio/neuralynxrawio.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,39 @@ class NeuralynxRawIO(BaseRawIO):
4949
_ncs_dtype = [('timestamp', 'uint64'), ('channel_id', 'uint32'), ('sample_rate', 'uint32'),
5050
('nb_valid', 'uint32'), ('samples', 'int16', (NcsSection._RECORD_SIZE))]
5151

52-
def __init__(self, dirname='', keep_original_times=False, **kargs):
52+
def __init__(self, dirname='', filename='', keep_original_times=False, **kargs):
5353
"""
54+
Initialize io for either a directory of Ncs files or a single Ncs file.
55+
5456
Parameters
5557
----------
5658
dirname: str
57-
name of directory containing all files for dataset
59+
name of directory containing all files for dataset. If provided, filename is
60+
ignored.
61+
filename: str
62+
name of a single ncs, nse, nev, or ntt file to include in dataset. If used,
63+
dirname must not be provided.
5864
keep_original_times:
5965
if True, keep original start time as in files,
6066
otherwise set 0 of time to first time in dataset
6167
"""
62-
self.dirname = dirname
68+
if dirname != '':
69+
self.dirname = dirname
70+
self.rawmode = 'one-dir'
71+
elif filename != '':
72+
self.filename = filename
73+
self.rawmode = 'one-file'
74+
else:
75+
raise ValueError("One of dirname or filename must be provided.")
76+
6377
self.keep_original_times = keep_original_times
6478
BaseRawIO.__init__(self, **kargs)
6579

6680
def _source_name(self):
67-
return self.dirname
81+
if self.rawmode == 'one-file':
82+
return self.filename
83+
else:
84+
return self.dirname
6885

6986
def _parse_header(self):
7087

@@ -91,8 +108,15 @@ def _parse_header(self):
91108
unit_annotations = []
92109
event_annotations = []
93110

94-
for filename in sorted(os.listdir(self.dirname)):
95-
filename = os.path.join(self.dirname, filename)
111+
if self.rawmode == 'one-dir':
112+
filenames = sorted(os.listdir(self.dirname))
113+
dirname = self.dirname
114+
else:
115+
dirname, fname = os.path.split(self.filename)
116+
filenames = [fname]
117+
118+
for filename in filenames:
119+
filename = os.path.join(dirname, filename)
96120

97121
_, ext = os.path.splitext(filename)
98122
ext = ext[1:] # remove dot

neo/rawio/neuralynxrawio/nlxheader.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ def _to_bool(txt):
8989
r' At Time: (?P<time>\S+)',
9090
filename_regex=r'## File Name: (?P<filename>\S+)',
9191
datetimeformat='%m/%d/%Y %H:%M:%S.%f'),
92+
# Cheetah version 5.4.0
93+
'v5.4.0': dict(
94+
datetime1_regex=r'## Time Opened \(m/d/y\): (?P<date>\S+)'
95+
r' At Time: (?P<time>\S+)',
96+
datetime2_regex=r'## Time Closed \(m/d/y\): (?P<date>\S+)'
97+
r' At Time: (?P<time>\S+)',
98+
filename_regex=r'## File Name: (?P<filename>\S+)',
99+
datetimeformat='%m/%d/%Y %H:%M:%S.%f'),
92100
# Cheetah version 5 before and including v 5.6.4 as well as version 1
93101
'bv5.6.4': dict(
94102
datetime1_regex=r'## Time Opened \(m/d/y\): (?P<date>\S+)'
@@ -199,6 +207,8 @@ def __init__(self, filename):
199207
hpd = NlxHeader.header_pattern_dicts['bv5.6.4']
200208
elif av < '5':
201209
hpd = NlxHeader.header_pattern_dicts['bv5']
210+
elif av <= '5.4.0':
211+
hpd = NlxHeader.header_pattern_dicts['v5.4.0']
202212
elif av <= '5.6.4':
203213
hpd = NlxHeader.header_pattern_dicts['bv5.6.4']
204214
else:

neo/test/iotest/test_neuralynxio.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def _load_plaindata(self, filename, numSamps):
309309
# np.testing.assert_allclose(plain_data[:numToTest],
310310
# anasig.magnitude[:numToTest, 0] * gain_factor_0,
311311
# rtol=0.01, err_msg=" for file " + filename)
312-
@unittest.skip
312+
@unittest.skip("nse failing for now as per issue #907")
313313
def test_keep_original_spike_times(self):
314314
for session in self.files_to_test:
315315
dirname = self.get_filename_path(session)
@@ -347,9 +347,11 @@ def test_incomplete_block_handling_v632(self):
347347
self.assertEqual(len(block.segments), n_gaps + 1)
348348
# self.assertEqual(len(block.channel_indexes[0].analogsignals), n_gaps + 1)
349349

350-
for t, gt in zip(nio._sigs_t_start, [8408.806811, 8427.832053, 8487.768561]):
350+
for t, gt in zip(nio._ncs_seg_timestamp_limits.t_start, [8408.806811, 8427.832053,
351+
8487.768561]):
351352
self.assertEqual(np.round(t, 4), np.round(gt, 4))
352-
for t, gt in zip(nio._sigs_t_stop, [8427.831990, 8487.768498, 8515.816549]):
353+
for t, gt in zip(nio._ncs_seg_timestamp_limits.t_stop, [8427.831990, 8487.768498,
354+
8515.816549]):
353355
self.assertEqual(np.round(t, 4), np.round(gt, 4))
354356

355357

neo/test/rawiotest/test_neuralynxrawio.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ def test_read_ncs_files_sideeffects(self):
124124
# three blocks of records. Gaps are on the order of 60 microseconds or so.
125125
rawio = NeuralynxRawIO(self.get_filename_path('Cheetah_v6.3.2/incomplete_blocks'))
126126
rawio.parse_header()
127-
# test values here from direct inspection of .ncs file
127+
# test values here from direct inspection of .ncs file, except for 3rd block
128+
# t_stop, which is extended due to events past the last block of ncs records.
128129
self.assertEqual(rawio._nb_segment, 3)
129130
self.assertListEqual(rawio._timestamp_limits, [(8408806811, 8427831990),
130131
(8427832053, 8487768498),
@@ -134,6 +135,41 @@ def test_read_ncs_files_sideeffects(self):
134135
self.assertListEqual(rawio._sigs_t_start, [8408.806811, 8427.832053, 8487.768561])
135136
self.assertEqual(len(rawio._sigs_memmaps), 3) # check only that there are 3 memmaps
136137

138+
def test_single_file_mode(self):
139+
"""
140+
Tests reading of single files.
141+
"""
142+
143+
# test single analog signal channel
144+
fname = self.get_filename_path('Cheetah_v5.6.3/original_data/CSC1.ncs')
145+
rawio = NeuralynxRawIO(filename=fname)
146+
rawio.parse_header()
147+
148+
self.assertEqual(rawio._nb_segment, 2)
149+
self.assertEqual(len(rawio.ncs_filenames), 1)
150+
self.assertEqual(len(rawio.nev_filenames), 0)
151+
sigHdrs = rawio.header['signal_channels']
152+
self.assertEqual(sigHdrs.size, 1)
153+
self.assertEqual(sigHdrs[0][0], 'CSC1')
154+
self.assertEqual(sigHdrs[0][1], 58)
155+
self.assertEqual(len(rawio.header['unit_channels']), 0)
156+
self.assertEqual(len(rawio.header['event_channels']), 0)
157+
158+
# test one single electrode channel
159+
fname = self.get_filename_path('Cheetah_v5.5.1/original_data/STet3a.nse')
160+
rawio = NeuralynxRawIO(filename=fname)
161+
rawio.parse_header()
162+
163+
self.assertEqual(rawio._nb_segment, 1)
164+
self.assertEqual(len(rawio.ncs_filenames), 0)
165+
self.assertEqual(len(rawio.nev_filenames), 0)
166+
seHdrs = rawio.header['unit_channels']
167+
self.assertEqual(len(seHdrs), 1)
168+
self.assertEqual(seHdrs[0][0], 'chSTet3a#8#0')
169+
self.assertEqual(seHdrs[0][1], '0')
170+
self.assertEqual(len(rawio.header['signal_channels']), 0)
171+
self.assertEqual(len(rawio.header['event_channels']), 0)
172+
137173

138174
class TestNcsRecordingType(TestNeuralynxRawIO, unittest.TestCase):
139175
"""

0 commit comments

Comments
 (0)