Skip to content

Commit ad38785

Browse files
authored
Merge pull request #1440 from NxNiki/multiple_filenames
NeuralynxIO: support List[str] for filename parameter
2 parents 8d4dc9a + 3621ecf commit ad38785

File tree

4 files changed

+104
-59
lines changed

4 files changed

+104
-59
lines changed

neo/io/neuralynxio.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Author: Julia Sprenger, Carlos Canova
1010
"""
1111

12+
import warnings
1213
from neo.io.basefromrawio import BaseFromRaw
1314
from neo.rawio.neuralynxrawio.neuralynxrawio import NeuralynxRawIO
1415

@@ -29,12 +30,14 @@ class NeuralynxIO(NeuralynxRawIO, BaseFromRaw):
2930

3031
def __init__(
3132
self,
32-
dirname="",
33-
filename="",
33+
dirname,
3434
use_cache=False,
3535
cache_path="same_as_resource",
36-
exclude_filename=None,
36+
include_filenames=None,
37+
exclude_filenames=None,
3738
keep_original_times=False,
39+
filename=None,
40+
exclude_filename=None,
3841
):
3942
"""
4043
Initialise IO instance
@@ -44,6 +47,7 @@ def __init__(
4447
dirname : str
4548
Directory containing data files
4649
filename : str
50+
Deprecated and will be removed. Please use `include_filenames` instead
4751
Name of a single ncs, nse, nev, or ntt file to include in dataset. Will be ignored,
4852
if dirname is provided.
4953
use_cache : bool, optional
@@ -52,24 +56,39 @@ def __init__(
5256
cache_path : str, optional
5357
Folder path to use for cache files.
5458
Default: 'same_as_resource'
55-
exclude_filename: str or list
59+
exclude_filename: None,
60+
Deprecated and will be removed. Please use `exclude_filenames` instead
61+
include_filenames: str or list
62+
Filename or list of filenames to be included. This can be absolute path or path relative to dirname.
63+
exclude_filenames: str or list
5664
Filename or list of filenames to be excluded. Expects base filenames without
5765
directory path.
5866
keep_original_times : bool
5967
Preserve original time stamps as in data files. By default datasets are
6068
shifted to begin at t_start = 0*pq.second.
6169
Default: False
6270
"""
71+
72+
if filename is not None:
73+
warnings.warn('Deprecated and will be removed. Please use `include_filenames` instead')
74+
include_filenames = [filename]
75+
76+
if exclude_filename is not None:
77+
warnings.warn('Deprecated and will be removed. Please use `exclude_filenames` instead')
78+
exclude_filenames = exclude_filename
79+
6380
NeuralynxRawIO.__init__(
6481
self,
6582
dirname=dirname,
66-
filename=filename,
67-
use_cache=use_cache,
68-
cache_path=cache_path,
69-
exclude_filename=exclude_filename,
83+
include_filenames=include_filenames,
84+
exclude_filenames=exclude_filenames,
7085
keep_original_times=keep_original_times,
86+
use_cache=use_cache,
87+
cache_path=cache_path
7188
)
72-
if self.rawmode == "one-file":
73-
BaseFromRaw.__init__(self, filename)
74-
elif self.rawmode == "one-dir":
89+
90+
if self.rawmode == "one-dir":
7591
BaseFromRaw.__init__(self, dirname)
92+
elif self.rawmode == "multiple-files":
93+
BaseFromRaw.__init__(self, include_filenames=include_filenames)
94+

neo/rawio/neuralynxrawio/neuralynxrawio.py

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import os
5858
import pathlib
5959
import copy
60+
import warnings
6061
from collections import namedtuple, OrderedDict
6162

6263
from neo.rawio.neuralynxrawio.ncssections import NcsSection, NcsSectionsFactory
@@ -75,10 +76,12 @@ class NeuralynxRawIO(BaseRawIO):
7576
dirname: str, default: ''
7677
Name of directory containing all files for a dataset. If provided, filename is
7778
ignored. But one of either dirname or filename is required.
78-
filename: str, default: ''
79-
Name of a single ncs, nse, nev, or ntt file to include in dataset. Will be ignored,
80-
if dirname is provided. But one of either dirname or filename is required.
81-
exclude_filename: str | list | None, default: None
79+
include_filenames: str | list | None, default: None
80+
Name of a single ncs, nse, nev or ntt file or list of such files. Will only include
81+
file names in the list. This can be plain filenames or fullpath or path relative to
82+
dirname.
83+
All files should be in a single path.
84+
exclude_filenames: str | list | None, default: None
8285
Name of a single ncs, nse, nev or ntt file or list of such files. Expects plain
8386
filenames (without directory path).
8487
None will search for all file types
@@ -128,32 +131,59 @@ class NeuralynxRawIO(BaseRawIO):
128131
("channel_id", "uint32"),
129132
("sample_rate", "uint32"),
130133
("nb_valid", "uint32"),
131-
("samples", "int16", (NcsSection._RECORD_SIZE)),
134+
("samples", "int16", NcsSection._RECORD_SIZE),
132135
]
133136

134137
def __init__(
135-
self, dirname="", filename="", exclude_filename=None, keep_original_times=False, strict_gap_mode=True, **kargs
138+
self,
139+
dirname="",
140+
include_filenames=None,
141+
exclude_filenames=None,
142+
keep_original_times=False,
143+
strict_gap_mode=True,
144+
filename=None,
145+
exclude_filename=None,
146+
**kargs
136147
):
137148

138-
if dirname != "":
139-
self.dirname = dirname
140-
self.rawmode = "one-dir"
141-
elif filename != "":
142-
self.filename = filename
143-
self.rawmode = "one-file"
149+
if not dirname:
150+
raise ValueError("`dirname` cannot be empty.")
151+
152+
if filename is not None:
153+
include_filenames = [filename]
154+
warnings.warn("`filename` is deprecated and will be removed. Please use `include_filenames` instead")
155+
156+
if exclude_filename is not None:
157+
if isinstance(exclude_filename, str):
158+
exclude_filenames = [exclude_filename]
159+
else:
160+
exclude_filenames = exclude_filename
161+
warnings.warn("`exclude_filename` is deprecated and will be removed. Please use `exclude_filenames` instead")
162+
163+
if include_filenames is None:
164+
include_filenames = []
165+
elif isinstance(include_filenames, str):
166+
include_filenames = [include_filenames]
167+
168+
if exclude_filenames is None:
169+
exclude_filenames = []
170+
elif isinstance(exclude_filenames, str):
171+
exclude_filenames = [exclude_filenames]
172+
173+
if include_filenames:
174+
self.rawmode = 'multiple-files'
144175
else:
145-
raise ValueError("One of dirname or filename must be provided.")
176+
self.rawmode = "one-dir"
146177

178+
self.dirname = dirname
179+
self.include_filenames = include_filenames
180+
self.exclude_filenames = exclude_filenames
147181
self.keep_original_times = keep_original_times
148182
self.strict_gap_mode = strict_gap_mode
149-
self.exclude_filename = exclude_filename
150183
BaseRawIO.__init__(self, **kargs)
151184

152185
def _source_name(self):
153-
if self.rawmode == "one-file":
154-
return self.filename
155-
else:
156-
return self.dirname
186+
return self.dirname
157187

158188
def _parse_header(self):
159189

@@ -183,33 +213,24 @@ def _parse_header(self):
183213
event_annotations = []
184214

185215
if self.rawmode == "one-dir":
186-
filenames = sorted(os.listdir(self.dirname))
187-
dirname = self.dirname
216+
filenames = os.listdir(self.dirname)
188217
else:
189-
if not os.path.isfile(self.filename):
218+
filenames = self.include_filenames
219+
220+
filenames = [f for f in filenames if f not in self.exclude_filenames]
221+
full_filenames = [os.path.join(self.dirname, f) for f in filenames]
222+
223+
for filename in full_filenames:
224+
if not os.path.isfile(filename):
190225
raise ValueError(
191226
f"Provided Filename is not a file: "
192-
f"{self.filename}. If you want to provide a "
227+
f"{filename}. If you want to provide a "
193228
f"directory use the `dirname` keyword"
194229
)
195230

196-
dirname, fname = os.path.split(self.filename)
197-
filenames = [fname]
198-
199-
if not isinstance(self.exclude_filename, (list, set, np.ndarray)):
200-
self.exclude_filename = [self.exclude_filename]
201-
202-
# remove files that were explicitly excluded
203-
if self.exclude_filename is not None:
204-
for excl_file in self.exclude_filename:
205-
if excl_file in filenames:
206-
filenames.remove(excl_file)
207-
208231
stream_props = {} # {(sampling_rate, n_samples, t_start): {stream_id: [filenames]}
209232

210-
for filename in filenames:
211-
filename = os.path.join(dirname, filename)
212-
233+
for filename in full_filenames:
213234
_, ext = os.path.splitext(filename)
214235
ext = ext[1:] # remove dot
215236
ext = ext.lower() # make lower case for comparisons

neo/test/iotest/test_neuralynxio.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,20 +180,21 @@ def test_read_block(self):
180180
block = nio.read_block(signal_group_mode="group-by-same-units")
181181
self.assertEqual(len(block.groups), 1)
182182

183-
def test_read_single_file(self):
183+
def test_include_filenames(self):
184184
filename = self.get_local_path("neuralynx/Cheetah_v5.7.4/original_data/CSC1.ncs")
185-
nio = NeuralynxIO(filename=filename, use_cache=False)
185+
dirname, filename = os.path.split(filename)
186+
nio = NeuralynxIO(dirname=dirname, include_filenames=filename, use_cache=False)
186187
block = nio.read_block()
187188
self.assertTrue(len(block.segments[0].analogsignals) > 0)
188189
self.assertTrue((len(block.segments[0].spiketrains)) == 0)
189190
self.assertTrue((len(block.segments[0].events)) == 0)
190191
self.assertTrue((len(block.segments[0].epochs)) == 0)
191192

192-
def test_exclude_filename(self):
193+
def test_exclude_filenames(self):
193194
dname = self.get_local_path("neuralynx/Cheetah_v5.7.4/original_data/")
194195

195196
# exclude a single file
196-
nio = NeuralynxIO(dirname=dname, exclude_filename="CSC1.ncs", use_cache=False)
197+
nio = NeuralynxIO(dirname=dname, exclude_filenames="CSC1.ncs", use_cache=False)
197198
block = nio.read_block()
198199
self.assertTrue(len(block.segments[0].analogsignals) > 0)
199200
self.assertTrue((len(block.segments[0].spiketrains)) >= 0)
@@ -202,7 +203,7 @@ def test_exclude_filename(self):
202203

203204
# exclude all ncs files from session
204205
exclude_files = [f"CSC{i}.ncs" for i in range(6)]
205-
nio = NeuralynxIO(dirname=dname, exclude_filename=exclude_files, use_cache=False)
206+
nio = NeuralynxIO(dirname=dname, exclude_filenames=exclude_files, use_cache=False)
206207
block = nio.read_block()
207208
self.assertTrue(len(block.segments[0].analogsignals) == 0)
208209
self.assertTrue((len(block.segments[0].spiketrains)) >= 0)

neo/test/rawiotest/test_neuralynxrawio.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22

3+
import os
34
import numpy as np
45

56
from neo.rawio.neuralynxrawio.neuralynxrawio import NeuralynxRawIO
@@ -105,14 +106,15 @@ def test_scan_ncs_files(self):
105106
# check that there are only 3 memmaps
106107
self.assertEqual(len(rawio._sigs_memmaps[seg_idx]), 3)
107108

108-
def test_single_file_mode(self):
109+
def test_include_filenames(self):
109110
"""
110-
Tests reading of single files.
111+
Tests include_filenames with only one file
111112
"""
112113

113114
# test single analog signal channel
114115
fname = self.get_local_path("neuralynx/Cheetah_v5.6.3/original_data/CSC1.ncs")
115-
rawio = NeuralynxRawIO(filename=fname)
116+
dirname, filename = os.path.split(fname)
117+
rawio = NeuralynxRawIO(dirname=dirname, include_filenames=filename)
116118
rawio.parse_header()
117119

118120
self.assertEqual(rawio._nb_segment, 2)
@@ -127,7 +129,8 @@ def test_single_file_mode(self):
127129

128130
# test one single electrode channel
129131
fname = self.get_local_path("neuralynx/Cheetah_v5.5.1/original_data/STet3a.nse")
130-
rawio = NeuralynxRawIO(filename=fname)
132+
dirname, filename = os.path.split(fname)
133+
rawio = NeuralynxRawIO(dirname=dirname, include_filenames=filename)
131134
rawio.parse_header()
132135

133136
self.assertEqual(rawio._nb_segment, 1)
@@ -143,7 +146,7 @@ def test_single_file_mode(self):
143146
def test_exclude_filenames(self):
144147
# exclude single ncs file from session
145148
dname = self.get_local_path("neuralynx/Cheetah_v5.6.3/original_data/")
146-
rawio = NeuralynxRawIO(dirname=dname, exclude_filename="CSC2.ncs")
149+
rawio = NeuralynxRawIO(dirname=dname, exclude_filenames="CSC2.ncs")
147150
rawio.parse_header()
148151

149152
self.assertEqual(rawio._nb_segment, 2)
@@ -157,7 +160,7 @@ def test_exclude_filenames(self):
157160
self.assertEqual(len(rawio.header["event_channels"]), 2)
158161

159162
# exclude multiple files from session
160-
rawio = NeuralynxRawIO(dirname=dname, exclude_filename=["Events.nev", "CSC2.ncs"])
163+
rawio = NeuralynxRawIO(dirname=dname, exclude_filenames=["Events.nev", "CSC2.ncs"])
161164
rawio.parse_header()
162165

163166
self.assertEqual(rawio._nb_segment, 2)
@@ -378,6 +381,7 @@ def test_equality(self):
378381
# test = TestNeuralynxRawIO()
379382
# test.test_scan_ncs_files()
380383
# test.test_exclude_filenames()
384+
# test.test_include_filenames()
381385

382386
# test = TestNcsSectionsFactory()
383387
# test.test_ncsblocks_partial()

0 commit comments

Comments
 (0)