From 0f7639bd37b91b2b706f1e22921d9d44d59a7d2b Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:46:38 +0100 Subject: [PATCH 01/40] Simon's changes Co-Authored-By: Simon Essink --- neo/io/nestio.py | 81 +++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index de1cab2c0..eab69eb01 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -61,7 +61,7 @@ class NestIO(BaseIO): extensions = ['gdf', 'dat'] mode = 'file' - def __init__(self, filenames=None): + def __init__(self, filenames=None, additional_parameters={}): """ Parameters ---------- @@ -84,7 +84,7 @@ def __init__(self, filenames=None): raise ValueError('Received multiple files with "%s" ' 'extension. Can only load single file of ' 'this type.' % ext) - self.avail_IOs[ext] = ColumnIO(filename) + self.avail_IOs[ext] = ColumnIO(filename, additional_parameters) self.avail_formats[ext] = path def __read_analogsignals(self, gid_list, time_unit, t_start=None, @@ -113,13 +113,17 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, # defining standard column order for internal usage # [id_column, time_column, value_column1, value_column2, ...] - column_ids = [id_column, time_column] + value_columns + column_ids = [id_column, time_column] + if value_columns is not None: + column_ids += value_columns for i, cid in enumerate(column_ids): if cid is None: column_ids[i] = -1 # assert that no single column is assigned twice - column_list = [id_column, time_column] + value_columns + column_list = [id_column, time_column] + if value_columns is not None: + column_list += value_columns column_list_no_None = [c for c in column_list if c is not None] if len(np.unique(column_list_no_None)) < len(column_list_no_None): raise ValueError( @@ -165,22 +169,23 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, # recording only after 1 sampling_period anasig_start_time = 1. * sampling_period - # create one analogsignal per value column requested - for v_id, value_column in enumerate(value_columns): - signal = data[ - selected_ids[0]:selected_ids[1], value_column] - - # create AnalogSignal objects and annotate them with - # the neuron ID - analogsignal_list.append(AnalogSignal( - signal * value_units[v_id], - sampling_period=sampling_period, - t_start=anasig_start_time, - id=i, - type=value_types[v_id])) - # check for correct length of analogsignal - assert (analogsignal_list[-1].t_stop - == anasig_start_time + len(signal) * sampling_period) + if value_columns is not None: + # create one analogsignal per value column requested + for v_id, value_column in enumerate(value_columns): + signal = data[ + selected_ids[0]:selected_ids[1], value_column] + + # create AnalogSignal objects and annotate them with + # the neuron ID + analogsignal_list.append(AnalogSignal( + signal * value_units[v_id], + sampling_period=sampling_period, + t_start=anasig_start_time, + id=i, + type=value_types[v_id])) + # check for correct length of analogsignal + assert (analogsignal_list[-1].t_stop + == anasig_start_time + len(signal) * sampling_period) return analogsignal_list def __read_spiketrains(self, gdf_id_list, time_unit, @@ -190,9 +195,9 @@ def __read_spiketrains(self, gdf_id_list, time_unit, Internal function for reading multiple spiketrains at once. This function is called by read_spiketrain() and read_segment(). """ - - if 'gdf' not in self.avail_IOs: - raise ValueError('Can not load spiketrains. No GDF file provided.') + ext = list(self.avail_IOs.keys())[0] + # if 'gdf' not in self.avail_IOs: + # raise ValueError('Can not load spiketrains. No GDF file provided.') # assert that the file contains spike times if time_column is None: @@ -220,7 +225,7 @@ def __read_spiketrains(self, gdf_id_list, time_unit, self._get_conditions_and_sorting(id_column, time_column, gdf_id_list, t_start, t_stop) - data = self.avail_IOs['gdf'].get_columns( + data = self.avail_IOs[ext].get_columns( column_ids=column_ids, condition=condition, condition_column=condition_column, @@ -293,7 +298,10 @@ def _check_input_values_parameters(self, value_columns, value_types, adjusted list of [value_columns, value_types, value_units] """ if value_columns is None: - raise ValueError('No value column provided.') + warnings.warn('No value column was provided.') + value_types = None + value_units = None + return value_columns, value_types, value_units if isinstance(value_columns, int): value_columns = [value_columns] if value_types is None: @@ -361,7 +369,7 @@ def _check_input_sampling_period(self, sampling_period, time_column, if sampling_period is None: if time_column is not None: data_sampling = np.unique( - np.diff(sorted(np.unique(data[:, 1])))) + np.diff(sorted(np.unique(data[:, time_column])))) if len(data_sampling) > 1: raise ValueError('Different sampling distances found in ' 'data set (%s)' % data_sampling) @@ -372,7 +380,7 @@ def _check_input_sampling_period(self, sampling_period, time_column, 'column id provided.') sampling_period = pq.CompoundUnit(str(dt) + '*' + time_unit.units.u_symbol) - elif not isinstance(sampling_period, pq.UnitQuantity): + elif not isinstance(sampling_period, pq.Quantity): raise ValueError("sampling_period is not specified as a unit.") return sampling_period @@ -546,7 +554,7 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, value_columns=value_columns_dat, value_types=value_types, value_units=value_units) - if 'gdf' in self.avail_formats: + # if 'gdf' in self.avail_formats: seg.spiketrains = self.__read_spiketrains( gid_list, time_unit, @@ -660,20 +668,23 @@ class ColumnIO: Class for reading an ASCII file containing multiple columns of data. ''' - def __init__(self, filename): + def __init__(self, filename, additional_parameters): """ filename: string, path to ASCII file to read. """ self.filename = filename + # Were the next few lines thought as a speed improvement? + # Only works if the first line of the file consists of + # values and not a header as is with the new NEST versions. + # read the first line to check the data type (int or float) of the data - f = open(self.filename) - line = f.readline() + # f = open(self.filename) + # line = f.readline() - additional_parameters = {} - if '.' not in line: - additional_parameters['dtype'] = np.int32 + # if '.' not in line: + # additional_parameters['dtype'] = np.int32 self.data = np.loadtxt(self.filename, **additional_parameters) @@ -707,7 +718,7 @@ def get_columns(self, column_ids='all', condition=None, column_ids = np.array(column_ids) if column_ids is not None: - if max(column_ids) >= len(self.data) - 1: + if max(column_ids) > len(self.data) - 1: raise ValueError('Can not load column ID %i. File contains ' 'only %i columns' % (max(column_ids), len(self.data))) From 1cbf80c7ae720aa9a7618ae5f2ea4a8e0a20b017 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:50:53 +0100 Subject: [PATCH 02/40] Ignore file header if there is one Co-Authored-By: Robin Gutzen --- neo/io/nestio.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index eab69eb01..eb8ddbaf1 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -675,18 +675,29 @@ def __init__(self, filename, additional_parameters): self.filename = filename - # Were the next few lines thought as a speed improvement? - # Only works if the first line of the file consists of - # values and not a header as is with the new NEST versions. - # read the first line to check the data type (int or float) of the data - # f = open(self.filename) - # line = f.readline() + f = open(self.filename) + line = f.readline() + header_size = 0 + + # Check how many header lines the file has so they can be ignored + while line: + if line[0].isdigit(): + break + else: + header_size += 1 + line = f.readline() + + # Warn user when the header is removed + if header_size > 0: + warnings.warn(f'Ignoring {str(header_size)} header lines.') - # if '.' not in line: - # additional_parameters['dtype'] = np.int32 + additional_parameters = {} + if '.' not in line: + additional_parameters['dtype'] = np.int32 - self.data = np.loadtxt(self.filename, **additional_parameters) + self.data = np.loadtxt(self.filename, skiprows=header_size, + **additional_parameters) if len(self.data.shape) == 1: self.data = self.data[:, np.newaxis] From b52282b15ef8995c56f700422f0223cd950eb8ba Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:53:23 +0100 Subject: [PATCH 03/40] change author list Co-Authored-By: Robin Gutzen Co-Authored-By: Simon Essink Co-Authored-By: Jasper Albers --- neo/io/nestio.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index eb8ddbaf1..d21677ed5 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -7,12 +7,11 @@ Supported: Read -Authors: Julia Sprenger, Maximilian Schmidt, Johanna Senk +Authors: Julia Sprenger, Maximilian Schmidt, Johanna Senk, +Simon Essink, Robin Gutzen, Jasper Albers, Aitor Morales-Gregorio """ -# needed for Python3 compatibility - import os.path import warnings from datetime import datetime From 1a734db10c680b729cfaa71b6ffc7638a3f560d7 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 13:10:56 +0100 Subject: [PATCH 04/40] Introduce new keyword so that which type of signal will be read is no longer dependent on the file format. Previous behaviour lead to the IO trying to always read spikes from .gdf files and analogsignals from .dat files, which is not necessarily the case with Nest 3.2 (possibly earlier) --- neo/io/nestio.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index d21677ed5..522d8cfd1 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -60,18 +60,23 @@ class NestIO(BaseIO): extensions = ['gdf', 'dat'] mode = 'file' - def __init__(self, filenames=None, additional_parameters={}): + def __init__(self, filenames=None, target_object='SpikeTrain', + additional_parameters={}): """ Parameters ---------- filenames: string or list of strings, default=None The filename or list of filenames to load. + target_object : string, default='SpikeTrain' + The type of neo object that should be read out from the input. + Options are: 'SpikeTrain', 'AnalogSignal' """ if isinstance(filenames, str): filenames = [filenames] self.filenames = filenames + self.target_object = target_object self.avail_formats = {} self.avail_IOs = {} @@ -541,7 +546,7 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, # in practice, there won't be much difference # Load analogsignals and attach to Segment - if 'dat' in self.avail_formats: + if 'AnalogSignal' is self.target_object: seg.analogsignals = self.__read_analogsignals( gid_list, time_unit, @@ -553,7 +558,7 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, value_columns=value_columns_dat, value_types=value_types, value_units=value_units) - # if 'gdf' in self.avail_formats: + if 'SpikeTrain' is self.target_object: seg.spiketrains = self.__read_spiketrains( gid_list, time_unit, From 42137a82ec440826d2bf3311b05ef665ff3d60b7 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 14:26:13 +0100 Subject: [PATCH 05/40] turn additional_parameters into a kwarg --- neo/io/nestio.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 522d8cfd1..f7fcf878b 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -672,7 +672,7 @@ class ColumnIO: Class for reading an ASCII file containing multiple columns of data. ''' - def __init__(self, filename, additional_parameters): + def __init__(self, filename, additional_parameters={}): """ filename: string, path to ASCII file to read. """ @@ -696,7 +696,6 @@ def __init__(self, filename, additional_parameters): if header_size > 0: warnings.warn(f'Ignoring {str(header_size)} header lines.') - additional_parameters = {} if '.' not in line: additional_parameters['dtype'] = np.int32 From de0b5129bb671b5dc40d98545570cd0ebf9d8e0e Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:36 +0100 Subject: [PATCH 06/40] remove support for lists --- neo/io/nestio.py | 54 +++++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index f7fcf878b..669f7449e 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -37,7 +37,7 @@ class NestIO(BaseIO): >>> files = ['membrane_voltages-1261-0.dat', 'spikes-1258-0.gdf'] - >>> r = NestIO(filenames=files) + >>> r = NestIO(filename=files) >>> seg = r.read_segment(gid_list=[], t_start=400 * pq.ms, t_stop=600 * pq.ms, id_column_gdf=0, time_column_gdf=1, @@ -57,39 +57,27 @@ class NestIO(BaseIO): write_params = None # writing is not supported name = 'nest' - extensions = ['gdf', 'dat'] + supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filenames=None, target_object='SpikeTrain', + def __init__(self, filename=None, target_object='SpikeTrain', additional_parameters={}): """ Parameters ---------- - filenames: string or list of strings, default=None - The filename or list of filenames to load. - target_object : string, default='SpikeTrain' + filename: string or list of strings, default=None + The filename or list of filename to load. + target_object : string or list of strings, default='SpikeTrain' The type of neo object that should be read out from the input. Options are: 'SpikeTrain', 'AnalogSignal' """ + if target_object not in self.supported_target_objects: + raise ValueError(f'{obj} is not a valid object type. ' + f'Valid values are {self.objects}.') - if isinstance(filenames, str): - filenames = [filenames] - - self.filenames = filenames + self.filename = filename self.target_object = target_object - self.avail_formats = {} - self.avail_IOs = {} - - for filename in filenames: - path, ext = os.path.splitext(filename) - ext = ext.strip('.') - if ext in self.extensions: - if ext in self.avail_IOs: - raise ValueError('Received multiple files with "%s" ' - 'extension. Can only load single file of ' - 'this type.' % ext) - self.avail_IOs[ext] = ColumnIO(filename, additional_parameters) - self.avail_formats[ext] = path + self.IO = ColumnIO(filename, additional_parameters) def __read_analogsignals(self, gid_list, time_unit, t_start=None, t_stop=None, sampling_period=None, @@ -100,10 +88,6 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, Internal function called by read_analogsignal() and read_segment(). """ - if 'dat' not in self.avail_formats: - raise ValueError('Can not load analogsignals. No DAT file ' - 'provided.') - # checking gid input parameters gid_list, id_column = self._check_input_gids(gid_list, id_column) # checking time input parameters @@ -143,7 +127,7 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, t_start, t_stop) # loading raw data columns - data = self.avail_IOs['dat'].get_columns( + data = self.IO.get_columns( column_ids=column_ids, condition=condition, condition_column=condition_column, @@ -199,10 +183,6 @@ def __read_spiketrains(self, gdf_id_list, time_unit, Internal function for reading multiple spiketrains at once. This function is called by read_spiketrain() and read_segment(). """ - ext = list(self.avail_IOs.keys())[0] - # if 'gdf' not in self.avail_IOs: - # raise ValueError('Can not load spiketrains. No GDF file provided.') - # assert that the file contains spike times if time_column is None: raise ValueError('Time column is None. No spike times to ' @@ -229,7 +209,7 @@ def __read_spiketrains(self, gdf_id_list, time_unit, self._get_conditions_and_sorting(id_column, time_column, gdf_id_list, t_start, t_stop) - data = self.avail_IOs[ext].get_columns( + data = self.IO.get_columns( column_ids=column_ids, condition=condition, condition_column=condition_column, @@ -540,13 +520,13 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, gid_list = [None] # create an empty Segment - seg = Segment(file_origin=",".join(self.filenames)) - seg.file_datetime = datetime.fromtimestamp(os.stat(self.filenames[0]).st_mtime) + seg = Segment(file_origin=",".join(self.filename)) + seg.file_datetime = datetime.fromtimestamp(os.stat(self.filename).st_mtime) # todo: rather than take the first file for the timestamp, we should take the oldest # in practice, there won't be much difference # Load analogsignals and attach to Segment - if 'AnalogSignal' is self.target_object: + if 'AnalogSignal' == self.target_object: seg.analogsignals = self.__read_analogsignals( gid_list, time_unit, @@ -558,7 +538,7 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, value_columns=value_columns_dat, value_types=value_types, value_units=value_units) - if 'SpikeTrain' is self.target_object: + if 'SpikeTrain' == self.target_object: seg.spiketrains = self.__read_spiketrains( gid_list, time_unit, From f73f38d8cafb3aa60d716e4b7e3d024889539fdc Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:50 +0100 Subject: [PATCH 07/40] pass the correct kwargs in the tests --- neo/test/iotest/test_nestio.py | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 6d0fe6012..4559cb448 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -28,7 +28,7 @@ def test_read_analogsignal(self): - with GIDs, with time as integer """ filename = self.get_local_path('nest/0gid-1time-2gex-3Vm-1261-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, sampling_period=pq.ms, lazy=False, id_column=0, time_column=1, @@ -39,7 +39,7 @@ def test_read_analogsignal(self): value_types='V_m') filename = self.get_local_path('nest/0gid-1time_in_steps-2Vm-1263-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -52,7 +52,7 @@ def test_read_analogsignal(self): value_types='V_m') filename = self.get_local_path('nest/0gid-1time-2Vm-1259-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -71,7 +71,7 @@ def test_id_column_none_multiple_neurons(self): units. """ filename = self.get_local_path('nest/0time-1255-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) with self.assertRaises(ValueError): r.read_analogsignal(t_stop=1000. * pq.ms, lazy=False, sampling_period=pq.ms, @@ -87,7 +87,7 @@ def test_values(self): """ filename = self.get_local_path('nest/0gid-1time-2gex-3Vm-1261-0.dat') id_to_test = 1 - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') seg = r.read_segment(gid_list=[id_to_test], t_stop=1000. * pq.ms, sampling_period=pq.ms, lazy=False, @@ -105,7 +105,7 @@ def test_read_segment(self): Tests if signals are correctly stored in a segment. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') id_list_to_test = range(1, 10) seg = r.read_segment(gid_list=id_list_to_test, @@ -130,7 +130,7 @@ def test_read_block(self): Tests if signals are correctly stored in a block. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') id_list_to_test = range(1, 10) blk = r.read_block(gid_list=id_list_to_test, @@ -152,7 +152,7 @@ def test_wrong_input(self): - User specifies t_start < 1.*sampling_period """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') with self.assertRaises(ValueError): r.read_segment(t_stop=1000. * pq.ms, lazy=False, id_column_dat=0, time_column_dat=1) @@ -175,7 +175,7 @@ def test_t_start_t_stop(self): Test for correct t_start and t_stop values of AnalogSignalArrays. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') t_start_targ = 450. * pq.ms t_stop_targ = 480. * pq.ms @@ -194,7 +194,7 @@ def test_notimeid(self): Test for warning, when no time column id was provided. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') t_start_targ = 450. * pq.ms t_stop_targ = 460. * pq.ms @@ -222,7 +222,7 @@ def test_multiple_value_columns(self): Test for simultaneous loading of multiple columns from dat file. """ filename = self.get_local_path('nest/0gid-1time-2Vm-3Iex-4Iin-1264-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') sampling_period = pq.CompoundUnit('5*ms') seg = r.read_segment(gid_list=[1001], @@ -233,7 +233,7 @@ def test_multiple_value_columns(self): def test_single_gid(self): filename = self.get_local_path('nest/N1-0gid-1time-2Vm-1265-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') anasig = r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -243,7 +243,7 @@ def test_single_gid(self): def test_no_gid(self): filename = self.get_local_path('nest/N1-0time-1Vm-1266-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') anasig = r.read_analogsignal(gid=None, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -254,7 +254,7 @@ def test_no_gid(self): def test_no_gid_no_time(self): filename = self.get_local_path('nest/N1-0Vm-1267-0.dat') - r = NestIO(filenames=filename) + r = NestIO(filename=filename, target_object='AnalogSignal') anasig = r.read_analogsignal(gid=None, sampling_period=pq.ms, lazy=False, id_column=None, time_column=None, @@ -276,14 +276,14 @@ def test_read_spiketrain(self): - with GIDs, with times as integers in time steps """ filename = self.get_local_path('nest/0time-1255-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) r.read_spiketrain(t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column=None, time_column=0) r.read_segment(t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=None, time_column_gdf=0) filename = self.get_local_path('nest/0time_in_steps-1257-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) r.read_spiketrain(t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), lazy=False, id_column=None, time_column=0) @@ -292,14 +292,14 @@ def test_read_spiketrain(self): id_column_gdf=None, time_column_gdf=0) filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=0, time_column_gdf=1) r.read_segment(gid_list=[1], t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=0, time_column_gdf=1) filename = self.get_local_path('nest/0gid-1time_in_steps-1258-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), lazy=False, id_column=0, time_column=1) @@ -313,7 +313,7 @@ def test_read_integer(self): in time steps in the file. """ filename = self.get_local_path('nest/0time_in_steps-1257-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) st = r.read_spiketrain(gdf_id=None, t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), @@ -328,7 +328,7 @@ def test_read_integer(self): filename = self.get_local_path('nest/0gid-1time_in_steps-1258-0.gdf') r = NestIO( - filenames=filename) + filename=filename) st = r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), @@ -347,7 +347,7 @@ def test_read_float(self): are stored as floats in the file. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) st = r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column=0, time_column=1) @@ -364,7 +364,7 @@ def test_values(self): """ id_to_test = 1 filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) seg = r.read_segment(gid_list=[id_to_test], t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -381,7 +381,7 @@ def test_read_segment(self): Tests if spiketrains are correctly stored in a segment. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) id_list_to_test = range(1, 10) seg = r.read_segment(gid_list=id_list_to_test, t_start=400. * pq.ms, @@ -400,7 +400,7 @@ def test_read_segment_accepts_range(self): Tests if spiketrains can be retrieved by specifying a range of GDF IDs. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) seg = r.read_segment(gid_list=(10, 39), t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -413,7 +413,7 @@ def test_read_segment_range_is_reasonable(self): the first one of the range. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) seg = r.read_segment(gid_list=(10, 10), t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -429,7 +429,7 @@ def test_read_spiketrain_annotates(self): Tests if correct annotation is added when reading a spike train. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) ID = 7 st = r.read_spiketrain(gdf_id=ID, t_start=400. * pq.ms, t_stop=500. * pq.ms) @@ -440,7 +440,7 @@ def test_read_segment_annotates(self): Tests if correct annotation is added when reading a segment. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) IDs = (5, 11) sts = r.read_segment(gid_list=(5, 11), t_start=400. * pq.ms, t_stop=500. * pq.ms) @@ -452,7 +452,7 @@ def test_adding_custom_annotation(self): Tests if custom annotation is correctly added. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) st = r.read_spiketrain(gdf_id=0, t_start=400. * pq.ms, t_stop=500. * pq.ms, layer='L23', population='I') @@ -468,7 +468,7 @@ def test_wrong_input(self): - User does not make any specifications. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) with self.assertRaises(ValueError): r.read_segment(t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -481,7 +481,7 @@ def test_t_start_t_stop(self): Tests if the t_start and t_stop arguments are correctly processed. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) t_stop_targ = 490. * pq.ms t_start_targ = 410. * pq.ms @@ -502,7 +502,7 @@ def test_t_start_undefined_raises_error(self): Tests if undefined t_start, i.e., t_start=None raises error. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) with self.assertRaises(ValueError): r.read_spiketrain(gdf_id=1, t_stop=500. * pq.ms, lazy=False, id_column=0, time_column=1) @@ -515,7 +515,7 @@ def test_t_stop_undefined_raises_error(self): Tests if undefined t_stop, i.e., t_stop=None raises error. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) with self.assertRaises(ValueError): r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, lazy=False, id_column=0, time_column=1) @@ -529,7 +529,7 @@ def test_gdf_id_illdefined_raises_error(self): empty list) raises error. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) with self.assertRaises(ValueError): r.read_spiketrain(gdf_id=[], t_start=400. * pq.ms, t_stop=500. * pq.ms) @@ -545,7 +545,7 @@ def test_read_segment_can_return_empty_spiketrains(self): returned. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) seg = r.read_segment(gid_list=[], t_start=400.4 * pq.ms, t_stop=400.5 * pq.ms) for st in seg.spiketrains: @@ -557,36 +557,36 @@ def test_read_spiketrain_can_return_empty_spiketrain(self): time range. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filenames=filename) + r = NestIO(filename=filename) st = r.read_spiketrain(gdf_id=0, t_start=400. * pq.ms, t_stop=410. * pq.ms) self.assertEqual(st.size, 0) -class TestNestIO_multiple_signal_types(BaseTestIO, unittest.TestCase): - ioclass = NestIO - entities_to_test = [] - entities_to_download = [ - 'nest' - ] - - def test_read_analogsignal_and_spiketrain(self): - """ - Test if spiketrains and analogsignals can be read simultaneously - using read_segment - """ - files = ['nest/0gid-1time-2gex-3Vm-1261-0.dat', - 'nest/0gid-1time_in_steps-1258-0.gdf'] - filenames = [self.get_local_path(e) for e in files] - - r = NestIO(filenames=filenames) - seg = r.read_segment(gid_list=[], t_start=400 * pq.ms, - t_stop=600 * pq.ms, - id_column_gdf=0, time_column_gdf=1, - id_column_dat=0, time_column_dat=1, - value_columns_dat=2) - self.assertEqual(len(seg.spiketrains), 50) - self.assertEqual(len(seg.analogsignals), 50) +# class TestNestIO_multiple_signal_types(BaseTestIO, unittest.TestCase): +# ioclass = NestIO +# entities_to_test = [] +# entities_to_download = [ +# 'nest' +# ] +# +# def test_read_analogsignal_and_spiketrain(self): +# """ +# Test if spiketrains and analogsignals can be read simultaneously +# using read_segment +# """ +# files = ['nest/0gid-1time-2gex-3Vm-1261-0.dat', +# 'nest/0gid-1time_in_steps-1258-0.gdf'] +# filenames = [self.get_local_path(e) for e in files] +# +# r = NestIO(filenames=filenames) +# seg = r.read_segment(gid_list=[], t_start=400 * pq.ms, +# t_stop=600 * pq.ms, +# id_column_gdf=0, time_column_gdf=1, +# id_column_dat=0, time_column_dat=1, +# value_columns_dat=2) +# self.assertEqual(len(seg.spiketrains), 50) +# self.assertEqual(len(seg.analogsignals), 50) class TestColumnIO(BaseTestIO, unittest.TestCase): @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename) + self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') def test_no_arguments(self): """ From 5a9abcf17b690baf0efc67e0d7750288380cfcd8 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 18:58:14 +0100 Subject: [PATCH 08/40] minor fixes --- neo/test/iotest/test_nestio.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 4559cb448..51a8c7e81 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -73,7 +73,7 @@ def test_id_column_none_multiple_neurons(self): filename = self.get_local_path('nest/0time-1255-0.gdf') r = NestIO(filename=filename) with self.assertRaises(ValueError): - r.read_analogsignal(t_stop=1000. * pq.ms, lazy=False, + r.read_spiketrain(t_stop=1000. * pq.ms, lazy=False, sampling_period=pq.ms, id_column=None, time_column=0, value_column=1) @@ -160,7 +160,7 @@ def test_wrong_input(self): r.read_segment() with self.assertRaises(ValueError): r.read_segment(gid_list=[1], t_stop=1000. * pq.ms, - sampling_period=1. * pq.ms, lazy=False, + sampling_period=1., lazy=False, id_column_dat=0, time_column_dat=1, value_columns_dat=2, value_types='V_m') @@ -351,7 +351,7 @@ def test_read_float(self): st = r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column=0, time_column=1) - self.assertTrue(st.magnitude.dtype == float) + self.assertTrue(st.magnitude.dtype == np.float_) seg = r.read_segment(gid_list=[1], t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=0, time_column_gdf=1) @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') + self.testIO = ColumnIO(filename=filename) def test_no_arguments(self): """ From 496552c02e4562a5d23bd9693aee52502500c651 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 18:58:56 +0100 Subject: [PATCH 09/40] Explicitly read as floats if a dot is found --- neo/io/nestio.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 669f7449e..ab8a3426d 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -678,6 +678,8 @@ def __init__(self, filename, additional_parameters={}): if '.' not in line: additional_parameters['dtype'] = np.int32 + else: + additional_parameters['dtype'] = np.float self.data = np.loadtxt(self.filename, skiprows=header_size, **additional_parameters) From cf655709b482b452006a845115a99cb3a6eadcc5 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:46:38 +0100 Subject: [PATCH 10/40] Simon's changes Co-Authored-By: Simon Essink --- neo/io/nestio.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index ab8a3426d..a7e9a9efe 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -75,9 +75,23 @@ def __init__(self, filename=None, target_object='SpikeTrain', raise ValueError(f'{obj} is not a valid object type. ' f'Valid values are {self.objects}.') - self.filename = filename - self.target_object = target_object - self.IO = ColumnIO(filename, additional_parameters) + if isinstance(filenames, str): + filenames = [filenames] + + self.filenames = filenames + self.avail_formats = {} + self.avail_IOs = {} + + for filename in filenames: + path, ext = os.path.splitext(filename) + ext = ext.strip('.') + if ext in self.extensions: + if ext in self.avail_IOs: + raise ValueError('Received multiple files with "%s" ' + 'extention. Can only load single file of ' + 'this type.' % ext) + self.avail_IOs[ext] = ColumnIO(filename, additional_parameters) + self.avail_formats[ext] = path def __read_analogsignals(self, gid_list, time_unit, t_start=None, t_stop=None, sampling_period=None, @@ -659,6 +673,10 @@ def __init__(self, filename, additional_parameters={}): self.filename = filename + # Were the next few lines thought as a speed improvement? + # Only works if the first line of the file consists of + # values and not a header as is with the new NEST versions. + # read the first line to check the data type (int or float) of the data f = open(self.filename) line = f.readline() From a20b5d9a43fde03a1293f1be5b829f42ba5a946b Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:46:27 +0100 Subject: [PATCH 11/40] Simon's changes Co-Authored-By: Simon Essink --- neo/io/nestio.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index a7e9a9efe..376bce0a5 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -197,6 +197,10 @@ def __read_spiketrains(self, gdf_id_list, time_unit, Internal function for reading multiple spiketrains at once. This function is called by read_spiketrain() and read_segment(). """ + ext = list(self.avail_IOs.keys())[0] + # if 'gdf' not in self.avail_IOs: + # raise ValueError('Can not load spiketrains. No GDF file provided.') + # assert that the file contains spike times if time_column is None: raise ValueError('Time column is None. No spike times to ' From 448185c5ae53bef4d7f656ff14aea9e83c93ba82 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:50:53 +0100 Subject: [PATCH 12/40] Ignore file header if there is one Co-Authored-By: Robin Gutzen --- neo/io/nestio.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 376bce0a5..9dd8b62ce 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -677,10 +677,6 @@ def __init__(self, filename, additional_parameters={}): self.filename = filename - # Were the next few lines thought as a speed improvement? - # Only works if the first line of the file consists of - # values and not a header as is with the new NEST versions. - # read the first line to check the data type (int or float) of the data f = open(self.filename) line = f.readline() @@ -698,10 +694,9 @@ def __init__(self, filename, additional_parameters={}): if header_size > 0: warnings.warn(f'Ignoring {str(header_size)} header lines.') + additional_parameters = {} if '.' not in line: additional_parameters['dtype'] = np.int32 - else: - additional_parameters['dtype'] = np.float self.data = np.loadtxt(self.filename, skiprows=header_size, **additional_parameters) From e47886b64e81acd4c5c28b71f02c0946fd3ee49f Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 13:10:56 +0100 Subject: [PATCH 13/40] Introduce new keyword so that which type of signal will be read is no longer dependent on the file format. Previous behaviour lead to the IO trying to always read spikes from .gdf files and analogsignals from .dat files, which is not necessarily the case with Nest 3.2 (possibly earlier) --- neo/io/nestio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 9dd8b62ce..d49032f13 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -60,7 +60,7 @@ class NestIO(BaseIO): supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filename=None, target_object='SpikeTrain', + def __init__(self, filenames=None, target_object='SpikeTrain', additional_parameters={}): """ Parameters @@ -79,6 +79,7 @@ def __init__(self, filename=None, target_object='SpikeTrain', filenames = [filenames] self.filenames = filenames + self.target_object = target_object self.avail_formats = {} self.avail_IOs = {} From fcd0e7dce08f5264d6131dab80ecf62e9fe5ec6c Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 14:26:13 +0100 Subject: [PATCH 14/40] turn additional_parameters into a kwarg --- neo/io/nestio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index d49032f13..c4a951b70 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -695,7 +695,6 @@ def __init__(self, filename, additional_parameters={}): if header_size > 0: warnings.warn(f'Ignoring {str(header_size)} header lines.') - additional_parameters = {} if '.' not in line: additional_parameters['dtype'] = np.int32 From 6a8221290b383f6aa23d1e9437b8ef902fb56dfb Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:36 +0100 Subject: [PATCH 15/40] remove support for lists --- neo/io/nestio.py | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index c4a951b70..669f7449e 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -60,7 +60,7 @@ class NestIO(BaseIO): supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filenames=None, target_object='SpikeTrain', + def __init__(self, filename=None, target_object='SpikeTrain', additional_parameters={}): """ Parameters @@ -75,24 +75,9 @@ def __init__(self, filenames=None, target_object='SpikeTrain', raise ValueError(f'{obj} is not a valid object type. ' f'Valid values are {self.objects}.') - if isinstance(filenames, str): - filenames = [filenames] - - self.filenames = filenames + self.filename = filename self.target_object = target_object - self.avail_formats = {} - self.avail_IOs = {} - - for filename in filenames: - path, ext = os.path.splitext(filename) - ext = ext.strip('.') - if ext in self.extensions: - if ext in self.avail_IOs: - raise ValueError('Received multiple files with "%s" ' - 'extention. Can only load single file of ' - 'this type.' % ext) - self.avail_IOs[ext] = ColumnIO(filename, additional_parameters) - self.avail_formats[ext] = path + self.IO = ColumnIO(filename, additional_parameters) def __read_analogsignals(self, gid_list, time_unit, t_start=None, t_stop=None, sampling_period=None, @@ -198,10 +183,6 @@ def __read_spiketrains(self, gdf_id_list, time_unit, Internal function for reading multiple spiketrains at once. This function is called by read_spiketrain() and read_segment(). """ - ext = list(self.avail_IOs.keys())[0] - # if 'gdf' not in self.avail_IOs: - # raise ValueError('Can not load spiketrains. No GDF file provided.') - # assert that the file contains spike times if time_column is None: raise ValueError('Time column is None. No spike times to ' From c0ae66f09088597a9ec56642ab80907236340db4 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:50 +0100 Subject: [PATCH 16/40] pass the correct kwargs in the tests --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 51a8c7e81..d1f5cfde2 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename) + self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') def test_no_arguments(self): """ From 1b8eff5d9e2c1bd1595559fffa123f5fbd2e02f7 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 18:58:14 +0100 Subject: [PATCH 17/40] minor fixes --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index d1f5cfde2..51a8c7e81 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') + self.testIO = ColumnIO(filename=filename) def test_no_arguments(self): """ From 5f353e5f8200e12fbcb486f7f833dec6944c4980 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 18:58:56 +0100 Subject: [PATCH 18/40] Explicitly read as floats if a dot is found --- neo/io/nestio.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 669f7449e..ab8a3426d 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -678,6 +678,8 @@ def __init__(self, filename, additional_parameters={}): if '.' not in line: additional_parameters['dtype'] = np.int32 + else: + additional_parameters['dtype'] = np.float self.data = np.loadtxt(self.filename, skiprows=header_size, **additional_parameters) From 0a3fd1fcd5214dbee4c61c313a817e7d92565e0c Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Fri, 17 Feb 2023 17:29:52 +0100 Subject: [PATCH 19/40] Update NestIO to work with multiple files of the same type at once --- neo/io/nestio.py | 216 ++++++++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 97 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index ab8a3426d..5a9fb838e 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -37,7 +37,7 @@ class NestIO(BaseIO): >>> files = ['membrane_voltages-1261-0.dat', 'spikes-1258-0.gdf'] - >>> r = NestIO(filename=files) + >>> r = NestIO(filenames=files) >>> seg = r.read_segment(gid_list=[], t_start=400 * pq.ms, t_stop=600 * pq.ms, id_column_gdf=0, time_column_gdf=1, @@ -60,24 +60,32 @@ class NestIO(BaseIO): supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filename=None, target_object='SpikeTrain', + def __init__(self, filenames=None, target_object='SpikeTrain', additional_parameters={}): """ Parameters ---------- - filename: string or list of strings, default=None + filenames: string or list of strings, default=None The filename or list of filename to load. target_object : string or list of strings, default='SpikeTrain' The type of neo object that should be read out from the input. Options are: 'SpikeTrain', 'AnalogSignal' """ if target_object not in self.supported_target_objects: - raise ValueError(f'{obj} is not a valid object type. ' + raise ValueError(f'{target_object} is not a valid object type. ' f'Valid values are {self.objects}.') - self.filename = filename + # Ensure right dimensionality + if isinstance(filenames, str): + filenames = [filenames] + + # Turn kwargs to attributes + self.filenames = filenames self.target_object = target_object - self.IO = ColumnIO(filename, additional_parameters) + + self.IOs = [] + for filename in filenames: + self.IOs.append(ColumnIO(filename, additional_parameters)) def __read_analogsignals(self, gid_list, time_unit, t_start=None, t_stop=None, sampling_period=None, @@ -126,54 +134,59 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, gid_list, t_start, t_stop) - # loading raw data columns - data = self.IO.get_columns( - column_ids=column_ids, - condition=condition, - condition_column=condition_column, - sorting_columns=sorting_column) - - sampling_period = self._check_input_sampling_period(sampling_period, - time_column, - time_unit, - data) - analogsignal_list = [] - - # extracting complete gid list for anasig generation - if (gid_list == []) and id_column is not None: - gid_list = np.unique(data[:, id_column]) - # generate analogsignals for each neuron ID - for i in gid_list: - selected_ids = self._get_selected_ids( - i, id_column, time_column, t_start, t_stop, time_unit, - data) - - # extract starting time of analogsignal - if (time_column is not None) and data.size: - anasig_start_time = data[selected_ids[0], 1] * time_unit - else: - # set t_start equal to sampling_period because NEST starts - # recording only after 1 sampling_period - anasig_start_time = 1. * sampling_period - - if value_columns is not None: - # create one analogsignal per value column requested - for v_id, value_column in enumerate(value_columns): - signal = data[ - selected_ids[0]:selected_ids[1], value_column] - - # create AnalogSignal objects and annotate them with - # the neuron ID - analogsignal_list.append(AnalogSignal( - signal * value_units[v_id], - sampling_period=sampling_period, - t_start=anasig_start_time, - id=i, - type=value_types[v_id])) - # check for correct length of analogsignal - assert (analogsignal_list[-1].t_stop - == anasig_start_time + len(signal) * sampling_period) + analogsignal_list = [] + for col in self.IOs: + + # loading raw data columns + data = col.get_columns( + column_ids=column_ids, + condition=condition, + condition_column=condition_column, + sorting_columns=sorting_column) + + sampling_period = self._check_input_sampling_period( + sampling_period, + time_column, + time_unit, + data) + + # extracting complete gid list for anasig generation + if (gid_list == []) and id_column is not None: + gid_list = np.unique(data[:, id_column]) + + # generate analogsignals for each neuron ID + for i in gid_list: + selected_ids = self._get_selected_ids( + i, id_column, time_column, t_start, t_stop, time_unit, + data) + + # extract starting time of analogsignal + if (time_column is not None) and data.size: + anasig_start_time = data[selected_ids[0], 1] * time_unit + else: + # set t_start equal to sampling_period because NEST starts + # recording only after 1 sampling_period + anasig_start_time = 1. * sampling_period + + if value_columns is not None: + # create one analogsignal per value column requested + for v_id, value_column in enumerate(value_columns): + signal = data[ + selected_ids[0]:selected_ids[1], value_column] + + # create AnalogSignal objects and annotate them with + # the neuron ID + analogsignal_list.append(AnalogSignal( + signal * value_units[v_id], + sampling_period=sampling_period, + t_start=anasig_start_time, + id=i, + type=value_types[v_id])) + # check for correct length of analogsignal + assert (analogsignal_list[-1].t_stop + == anasig_start_time + len(signal) * + sampling_period) return analogsignal_list def __read_spiketrains(self, gdf_id_list, time_unit, @@ -209,37 +222,46 @@ def __read_spiketrains(self, gdf_id_list, time_unit, self._get_conditions_and_sorting(id_column, time_column, gdf_id_list, t_start, t_stop) - data = self.IO.get_columns( - column_ids=column_ids, - condition=condition, - condition_column=condition_column, - sorting_columns=sorting_column) - - # create a list of SpikeTrains for all neuron IDs in gdf_id_list - # assign spike times to neuron IDs if id_column is given - if id_column is not None: - if (gdf_id_list == []) and id_column is not None: - gdf_id_list = np.unique(data[:, id_column]) - - spiketrain_list = [] - for nid in gdf_id_list: - selected_ids = self._get_selected_ids(nid, id_column, - time_column, t_start, - t_stop, time_unit, data) - times = data[selected_ids[0]:selected_ids[1], time_column] - spiketrain_list.append(SpikeTrain( - - times, units=time_unit, - t_start=t_start, t_stop=t_stop, - id=nid, **args)) - - # if id_column is not given, all spike times are collected in one - # spike train with id=None - else: - train = data[:, time_column] - spiketrain_list = [SpikeTrain(train, units=time_unit, - t_start=t_start, t_stop=t_stop, - id=None, **args)] + spiketrain_list = [] + for col in self.IOs: + + data = col.get_columns( + column_ids=column_ids, + condition=condition, + condition_column=condition_column, + sorting_columns=sorting_column) + + # create a list of SpikeTrains for all neuron IDs in gdf_id_list + # assign spike times to neuron IDs if id_column is given + if id_column is not None: + if (gdf_id_list == []) and id_column is not None: + current_file_ids = np.unique(data[:, id_column]) + else: + current_file_ids = gdf_id_list + + for nid in current_file_ids: + selected_ids = self._get_selected_ids(nid, id_column, + time_column, t_start, + t_stop, time_unit, + data) + times = data[selected_ids[0]:selected_ids[1], time_column] + spiketrain_list.append(SpikeTrain(times, units=time_unit, + t_start=t_start, + t_stop=t_stop, + id=nid, + source_file=col.filename, + **args)) + + # if id_column is not given, all spike times are collected in one + # spike train with id=None + else: + train = data[:, time_column] + spiketrain_list.append([SpikeTrain(train, units=time_unit, + t_start=t_start, + t_stop=t_stop, + id=None, + source_file=col.filename, + **args)]) return spiketrain_list def _check_input_times(self, t_start, t_stop, mandatory=True): @@ -423,11 +445,11 @@ def _get_selected_ids(self, gid, id_column, time_column, t_start, t_stop, Returns list of selected gids """ - gid_ids = np.array([0, data.shape[0]]) + gids = np.array([0, data.shape[0]]) if id_column is not None: - gid_ids = np.array([np.searchsorted(data[:, 0], gid, side='left'), - np.searchsorted(data[:, 0], gid, side='right')]) - gid_data = data[gid_ids[0]:gid_ids[1], :] + gids = np.array([np.searchsorted(data[:, 0], gid, side='left'), + np.searchsorted(data[:, 0], gid, side='right')]) + gid_data = data[gids[0]:gids[1], :] # select only requested time range id_shifts = np.array([0, 0]) @@ -441,7 +463,7 @@ def _get_selected_ids(self, gid, id_column, time_column, t_start, t_stop, time_unit).magnitude, side='left') - gid_data.shape[0]) - selected_ids = gid_ids + id_shifts + selected_ids = gids + id_shifts return selected_ids def read_block(self, gid_list=None, time_unit=pq.ms, t_start=None, @@ -456,7 +478,8 @@ def read_block(self, gid_list=None, time_unit=pq.ms, t_start=None, time_column_dat, value_columns_dat, id_column_gdf, time_column_gdf, value_types, value_units) - blk = Block(file_origin=seg.file_origin, file_datetime=seg.file_datetime) + blk = Block(file_origin=seg.file_origin, + file_datetime=seg.file_datetime) blk.segments.append(seg) return blk @@ -474,8 +497,8 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, gid_list : list, default: None A list of GDF IDs of which to return SpikeTrain(s). gid_list must be specified if the GDF file contains neuron IDs, the default None - then raises an error. Specify an empty list [] to retrieve the spike - trains of all neurons. + then raises an error. Specify an empty list [] to retrieve the + spike trains of all neurons. time_unit : Quantity (time), optional, default: quantities.ms The time unit of recorded time stamps in DAT as well as GDF files. t_start : Quantity (time), optional, default: 0 * pq.ms @@ -520,10 +543,9 @@ def read_segment(self, gid_list=None, time_unit=pq.ms, t_start=None, gid_list = [None] # create an empty Segment - seg = Segment(file_origin=",".join(self.filename)) - seg.file_datetime = datetime.fromtimestamp(os.stat(self.filename).st_mtime) - # todo: rather than take the first file for the timestamp, we should take the oldest - # in practice, there won't be much difference + seg = Segment(file_origin=",".join(self.filenames)) + seg.file_datetime = datetime.fromtimestamp( + os.stat(self.filenames[-1]).st_mtime) # Load analogsignals and attach to Segment if 'AnalogSignal' == self.target_object: @@ -679,7 +701,7 @@ def __init__(self, filename, additional_parameters={}): if '.' not in line: additional_parameters['dtype'] = np.int32 else: - additional_parameters['dtype'] = np.float + additional_parameters['dtype'] = np.float32 self.data = np.loadtxt(self.filename, skiprows=header_size, **additional_parameters) From 6254d8cf458a3bfc9c5b9f98babe13aff9c30ec9 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Fri, 17 Feb 2023 18:05:42 +0100 Subject: [PATCH 20/40] fix overwritten id issue --- neo/io/nestio.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 5a9fb838e..bd75a5a18 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -153,10 +153,12 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, # extracting complete gid list for anasig generation if (gid_list == []) and id_column is not None: - gid_list = np.unique(data[:, id_column]) + current_gid_list = np.unique(data[:, id_column]) + else: + current_gid_list = gid_list # generate analogsignals for each neuron ID - for i in gid_list: + for i in current_gid_list: selected_ids = self._get_selected_ids( i, id_column, time_column, t_start, t_stop, time_unit, data) @@ -182,6 +184,7 @@ def __read_analogsignals(self, gid_list, time_unit, t_start=None, sampling_period=sampling_period, t_start=anasig_start_time, id=i, + source_file=col.filename, type=value_types[v_id])) # check for correct length of analogsignal assert (analogsignal_list[-1].t_stop From af55cb7612e453768af38f97db6738e8c0978f82 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:46:38 +0100 Subject: [PATCH 21/40] Simon's changes Co-Authored-By: Simon Essink --- neo/io/nestio.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index bd75a5a18..58196e354 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -684,6 +684,10 @@ def __init__(self, filename, additional_parameters={}): self.filename = filename + # Were the next few lines thought as a speed improvement? + # Only works if the first line of the file consists of + # values and not a header as is with the new NEST versions. + # read the first line to check the data type (int or float) of the data f = open(self.filename) line = f.readline() From 7ecf970fe66ba07f6174727c1fb56202e853df9c Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:50:53 +0100 Subject: [PATCH 22/40] Ignore file header if there is one Co-Authored-By: Robin Gutzen --- neo/io/nestio.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 58196e354..bd75a5a18 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -684,10 +684,6 @@ def __init__(self, filename, additional_parameters={}): self.filename = filename - # Were the next few lines thought as a speed improvement? - # Only works if the first line of the file consists of - # values and not a header as is with the new NEST versions. - # read the first line to check the data type (int or float) of the data f = open(self.filename) line = f.readline() From 61ea22372cafdc44dd31f6193b923b3afa798d7e Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:36 +0100 Subject: [PATCH 23/40] remove support for lists --- neo/io/nestio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index bd75a5a18..9acd0d4d7 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -37,7 +37,7 @@ class NestIO(BaseIO): >>> files = ['membrane_voltages-1261-0.dat', 'spikes-1258-0.gdf'] - >>> r = NestIO(filenames=files) + >>> r = NestIO(filename=files) >>> seg = r.read_segment(gid_list=[], t_start=400 * pq.ms, t_stop=600 * pq.ms, id_column_gdf=0, time_column_gdf=1, @@ -60,7 +60,7 @@ class NestIO(BaseIO): supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filenames=None, target_object='SpikeTrain', + def __init__(self, filename=None, target_object='SpikeTrain', additional_parameters={}): """ Parameters From 44fdbae9d2e436ddae79b6608b3fb0ce3cf15465 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:50 +0100 Subject: [PATCH 24/40] pass the correct kwargs in the tests --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 51a8c7e81..d1f5cfde2 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename) + self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') def test_no_arguments(self): """ From 96dd4c3d6f9bcafe87463e0e524922ced0e2ea84 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 18:58:14 +0100 Subject: [PATCH 25/40] minor fixes --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index d1f5cfde2..51a8c7e81 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') + self.testIO = ColumnIO(filename=filename) def test_no_arguments(self): """ From dc7281f83630f003410972e23574c3d0d92c3784 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:46:38 +0100 Subject: [PATCH 26/40] Simon's changes Co-Authored-By: Simon Essink --- neo/io/nestio.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 9acd0d4d7..c706e4f4c 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -684,6 +684,10 @@ def __init__(self, filename, additional_parameters={}): self.filename = filename + # Were the next few lines thought as a speed improvement? + # Only works if the first line of the file consists of + # values and not a header as is with the new NEST versions. + # read the first line to check the data type (int or float) of the data f = open(self.filename) line = f.readline() From bec1cd848a935c24418fb67220feb7369733d806 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 12:50:53 +0100 Subject: [PATCH 27/40] Ignore file header if there is one Co-Authored-By: Robin Gutzen --- neo/io/nestio.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index c706e4f4c..9acd0d4d7 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -684,10 +684,6 @@ def __init__(self, filename, additional_parameters={}): self.filename = filename - # Were the next few lines thought as a speed improvement? - # Only works if the first line of the file consists of - # values and not a header as is with the new NEST versions. - # read the first line to check the data type (int or float) of the data f = open(self.filename) line = f.readline() From 8daf50c57cf81452a343d5fd0a41a2934b20a3c6 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 15:42:50 +0100 Subject: [PATCH 28/40] pass the correct kwargs in the tests --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 51a8c7e81..d1f5cfde2 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename) + self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') def test_no_arguments(self): """ From e42df5fb42ec0b58946404465fac330135cfee6a Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Wed, 9 Mar 2022 18:58:14 +0100 Subject: [PATCH 29/40] minor fixes --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index d1f5cfde2..51a8c7e81 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -600,7 +600,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename, target_object='AnalogSignal') + self.testIO = ColumnIO(filename=filename) def test_no_arguments(self): """ From 53586cd80f43b9d69b0b19d2548c43c95995551e Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Fri, 17 Feb 2023 17:29:52 +0100 Subject: [PATCH 30/40] Update NestIO to work with multiple files of the same type at once --- neo/io/nestio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 9acd0d4d7..bd75a5a18 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -37,7 +37,7 @@ class NestIO(BaseIO): >>> files = ['membrane_voltages-1261-0.dat', 'spikes-1258-0.gdf'] - >>> r = NestIO(filename=files) + >>> r = NestIO(filenames=files) >>> seg = r.read_segment(gid_list=[], t_start=400 * pq.ms, t_stop=600 * pq.ms, id_column_gdf=0, time_column_gdf=1, @@ -60,7 +60,7 @@ class NestIO(BaseIO): supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filename=None, target_object='SpikeTrain', + def __init__(self, filenames=None, target_object='SpikeTrain', additional_parameters={}): """ Parameters From 060d558e804fc979f05f1a17a59b0fc6e3d938f7 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Thu, 3 Aug 2023 12:36:16 +0200 Subject: [PATCH 31/40] fix kwarg name --- neo/test/iotest/test_nestio.py | 76 ++++++++++++++++------------------ 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 51a8c7e81..3a98b8224 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -1,14 +1,10 @@ """ Tests of neo.io.exampleio """ - import warnings - import unittest - import quantities as pq import numpy as np - from neo.io.nestio import ColumnIO from neo.io.nestio import NestIO from neo.test.iotest.common_io_test import BaseTestIO @@ -28,7 +24,7 @@ def test_read_analogsignal(self): - with GIDs, with time as integer """ filename = self.get_local_path('nest/0gid-1time-2gex-3Vm-1261-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, sampling_period=pq.ms, lazy=False, id_column=0, time_column=1, @@ -39,7 +35,7 @@ def test_read_analogsignal(self): value_types='V_m') filename = self.get_local_path('nest/0gid-1time_in_steps-2Vm-1263-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -52,7 +48,7 @@ def test_read_analogsignal(self): value_types='V_m') filename = self.get_local_path('nest/0gid-1time-2Vm-1259-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -71,7 +67,7 @@ def test_id_column_none_multiple_neurons(self): units. """ filename = self.get_local_path('nest/0time-1255-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) with self.assertRaises(ValueError): r.read_spiketrain(t_stop=1000. * pq.ms, lazy=False, sampling_period=pq.ms, @@ -87,7 +83,7 @@ def test_values(self): """ filename = self.get_local_path('nest/0gid-1time-2gex-3Vm-1261-0.dat') id_to_test = 1 - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') seg = r.read_segment(gid_list=[id_to_test], t_stop=1000. * pq.ms, sampling_period=pq.ms, lazy=False, @@ -105,7 +101,7 @@ def test_read_segment(self): Tests if signals are correctly stored in a segment. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') id_list_to_test = range(1, 10) seg = r.read_segment(gid_list=id_list_to_test, @@ -130,7 +126,7 @@ def test_read_block(self): Tests if signals are correctly stored in a block. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') id_list_to_test = range(1, 10) blk = r.read_block(gid_list=id_list_to_test, @@ -152,7 +148,7 @@ def test_wrong_input(self): - User specifies t_start < 1.*sampling_period """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') with self.assertRaises(ValueError): r.read_segment(t_stop=1000. * pq.ms, lazy=False, id_column_dat=0, time_column_dat=1) @@ -175,7 +171,7 @@ def test_t_start_t_stop(self): Test for correct t_start and t_stop values of AnalogSignalArrays. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') t_start_targ = 450. * pq.ms t_stop_targ = 480. * pq.ms @@ -194,7 +190,7 @@ def test_notimeid(self): Test for warning, when no time column id was provided. """ filename = self.get_local_path('nest/0gid-1time-2gex-1262-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') t_start_targ = 450. * pq.ms t_stop_targ = 460. * pq.ms @@ -222,7 +218,7 @@ def test_multiple_value_columns(self): Test for simultaneous loading of multiple columns from dat file. """ filename = self.get_local_path('nest/0gid-1time-2Vm-3Iex-4Iin-1264-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') sampling_period = pq.CompoundUnit('5*ms') seg = r.read_segment(gid_list=[1001], @@ -233,7 +229,7 @@ def test_multiple_value_columns(self): def test_single_gid(self): filename = self.get_local_path('nest/N1-0gid-1time-2Vm-1265-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') anasig = r.read_analogsignal(gid=1, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -243,7 +239,7 @@ def test_single_gid(self): def test_no_gid(self): filename = self.get_local_path('nest/N1-0time-1Vm-1266-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') anasig = r.read_analogsignal(gid=None, t_stop=1000. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), sampling_period=pq.ms, lazy=False, @@ -254,7 +250,7 @@ def test_no_gid(self): def test_no_gid_no_time(self): filename = self.get_local_path('nest/N1-0Vm-1267-0.dat') - r = NestIO(filename=filename, target_object='AnalogSignal') + r = NestIO(filenames=filename, target_object='AnalogSignal') anasig = r.read_analogsignal(gid=None, sampling_period=pq.ms, lazy=False, id_column=None, time_column=None, @@ -276,14 +272,14 @@ def test_read_spiketrain(self): - with GIDs, with times as integers in time steps """ filename = self.get_local_path('nest/0time-1255-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) r.read_spiketrain(t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column=None, time_column=0) r.read_segment(t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=None, time_column_gdf=0) filename = self.get_local_path('nest/0time_in_steps-1257-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) r.read_spiketrain(t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), lazy=False, id_column=None, time_column=0) @@ -292,14 +288,14 @@ def test_read_spiketrain(self): id_column_gdf=None, time_column_gdf=0) filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=0, time_column_gdf=1) r.read_segment(gid_list=[1], t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column_gdf=0, time_column_gdf=1) filename = self.get_local_path('nest/0gid-1time_in_steps-1258-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), lazy=False, id_column=0, time_column=1) @@ -313,7 +309,7 @@ def test_read_integer(self): in time steps in the file. """ filename = self.get_local_path('nest/0time_in_steps-1257-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) st = r.read_spiketrain(gdf_id=None, t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), @@ -328,7 +324,7 @@ def test_read_integer(self): filename = self.get_local_path('nest/0gid-1time_in_steps-1258-0.gdf') r = NestIO( - filename=filename) + filenames=filename) st = r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, time_unit=pq.CompoundUnit('0.1*ms'), @@ -347,7 +343,7 @@ def test_read_float(self): are stored as floats in the file. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) st = r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, id_column=0, time_column=1) @@ -364,7 +360,7 @@ def test_values(self): """ id_to_test = 1 filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) seg = r.read_segment(gid_list=[id_to_test], t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -381,7 +377,7 @@ def test_read_segment(self): Tests if spiketrains are correctly stored in a segment. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) id_list_to_test = range(1, 10) seg = r.read_segment(gid_list=id_list_to_test, t_start=400. * pq.ms, @@ -400,7 +396,7 @@ def test_read_segment_accepts_range(self): Tests if spiketrains can be retrieved by specifying a range of GDF IDs. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) seg = r.read_segment(gid_list=(10, 39), t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -413,7 +409,7 @@ def test_read_segment_range_is_reasonable(self): the first one of the range. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) seg = r.read_segment(gid_list=(10, 10), t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -429,7 +425,7 @@ def test_read_spiketrain_annotates(self): Tests if correct annotation is added when reading a spike train. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) ID = 7 st = r.read_spiketrain(gdf_id=ID, t_start=400. * pq.ms, t_stop=500. * pq.ms) @@ -440,7 +436,7 @@ def test_read_segment_annotates(self): Tests if correct annotation is added when reading a segment. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) IDs = (5, 11) sts = r.read_segment(gid_list=(5, 11), t_start=400. * pq.ms, t_stop=500. * pq.ms) @@ -452,7 +448,7 @@ def test_adding_custom_annotation(self): Tests if custom annotation is correctly added. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) st = r.read_spiketrain(gdf_id=0, t_start=400. * pq.ms, t_stop=500. * pq.ms, layer='L23', population='I') @@ -468,7 +464,7 @@ def test_wrong_input(self): - User does not make any specifications. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) with self.assertRaises(ValueError): r.read_segment(t_start=400. * pq.ms, t_stop=500. * pq.ms, lazy=False, @@ -481,7 +477,7 @@ def test_t_start_t_stop(self): Tests if the t_start and t_stop arguments are correctly processed. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) t_stop_targ = 490. * pq.ms t_start_targ = 410. * pq.ms @@ -502,7 +498,7 @@ def test_t_start_undefined_raises_error(self): Tests if undefined t_start, i.e., t_start=None raises error. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) with self.assertRaises(ValueError): r.read_spiketrain(gdf_id=1, t_stop=500. * pq.ms, lazy=False, id_column=0, time_column=1) @@ -515,7 +511,7 @@ def test_t_stop_undefined_raises_error(self): Tests if undefined t_stop, i.e., t_stop=None raises error. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) with self.assertRaises(ValueError): r.read_spiketrain(gdf_id=1, t_start=400. * pq.ms, lazy=False, id_column=0, time_column=1) @@ -529,7 +525,7 @@ def test_gdf_id_illdefined_raises_error(self): empty list) raises error. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) with self.assertRaises(ValueError): r.read_spiketrain(gdf_id=[], t_start=400. * pq.ms, t_stop=500. * pq.ms) @@ -545,7 +541,7 @@ def test_read_segment_can_return_empty_spiketrains(self): returned. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) seg = r.read_segment(gid_list=[], t_start=400.4 * pq.ms, t_stop=400.5 * pq.ms) for st in seg.spiketrains: @@ -557,7 +553,7 @@ def test_read_spiketrain_can_return_empty_spiketrain(self): time range. """ filename = self.get_local_path('nest/0gid-1time-1256-0.gdf') - r = NestIO(filename=filename) + r = NestIO(filenames=filename) st = r.read_spiketrain(gdf_id=0, t_start=400. * pq.ms, t_stop=410. * pq.ms) self.assertEqual(st.size, 0) @@ -600,7 +596,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filename=filename) + self.testIO = ColumnIO(filenames=filename) def test_no_arguments(self): """ From 5a2b9db7cf0097a33d2032728334251a1f3879b1 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Thu, 3 Aug 2023 14:44:02 +0200 Subject: [PATCH 32/40] fix columnIO kwarg --- neo/test/iotest/test_nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/test/iotest/test_nestio.py b/neo/test/iotest/test_nestio.py index 3a98b8224..5af01b42f 100644 --- a/neo/test/iotest/test_nestio.py +++ b/neo/test/iotest/test_nestio.py @@ -596,7 +596,7 @@ class TestColumnIO(BaseTestIO, unittest.TestCase): def setUp(self): BaseTestIO.setUp(self) filename = self.get_local_path('nest/0gid-1time-2Vm-3gex-4gin-1260-0.dat') - self.testIO = ColumnIO(filenames=filename) + self.testIO = ColumnIO(filename=filename) def test_no_arguments(self): """ From 61fd2931ecd6a89a05fb0746b08454c040f97027 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Thu, 4 Apr 2024 15:28:55 +0200 Subject: [PATCH 33/40] rename additional_parameters to kwargs --- neo/io/nestio.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 5fcbe166d..55298a1da 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -21,7 +21,9 @@ from neo.io.baseio import BaseIO from neo.core import Block, Segment, SpikeTrain, AnalogSignal -value_type_dict = {"V": pq.mV, "I": pq.pA, "g": pq.CompoundUnit("10^-9*S"), "no type": pq.dimensionless} +value_type_dict = {"V": pq.mV, "I": pq.pA, + "g": pq.CompoundUnit("10^-9*S"), + "no type": pq.dimensionless} class NestIO(BaseIO): @@ -57,8 +59,7 @@ class NestIO(BaseIO): supported_target_objects = ['SpikeTrain', 'AnalogSignal'] mode = 'file' - def __init__(self, filenames=None, target_object='SpikeTrain', - additional_parameters={}): + def __init__(self, filenames=None, target_object='SpikeTrain', **kwargs): """ Parameters ---------- @@ -67,6 +68,9 @@ def __init__(self, filenames=None, target_object='SpikeTrain', target_object : string or list of strings, default='SpikeTrain' The type of neo object that should be read out from the input. Options are: 'SpikeTrain', 'AnalogSignal' + kwargs : dict like + keyword arguments that will be passed to `numpy.loadtxt` see + https://numpy.org/devdocs/reference/generated/numpy.loadtxt.html """ if target_object not in self.supported_target_objects: raise ValueError(f'{target_object} is not a valid object type. ' @@ -82,7 +86,7 @@ def __init__(self, filenames=None, target_object='SpikeTrain', self.IOs = [] for filename in filenames: - self.IOs.append(ColumnIO(filename, additional_parameters)) + self.IOs.append(ColumnIO(filename, **kwargs)) def __read_analogsignals( self, @@ -694,7 +698,7 @@ class ColumnIO: Class for reading an ASCII file containing multiple columns of data. """ - def __init__(self, filename, additional_parameters={}): + def __init__(self, filename, **kwargs): """ filename: string, path to ASCII file to read. """ @@ -719,12 +723,11 @@ def __init__(self, filename, additional_parameters={}): warnings.warn(f'Ignoring {str(header_size)} header lines.') if '.' not in line: - additional_parameters['dtype'] = np.int32 + kwargs['dtype'] = np.int32 else: - additional_parameters['dtype'] = np.float32 + kwargs['dtype'] = np.float32 - self.data = np.loadtxt(self.filename, skiprows=header_size, - **additional_parameters) + self.data = np.loadtxt(self.filename, skiprows=header_size, **kwargs) if len(self.data.shape) == 1: self.data = self.data[:, np.newaxis] From 342a94325c1cb6c3b2cc237119aa8299c381186c Mon Sep 17 00:00:00 2001 From: Aitor Morales-Gregorio <43403140+morales-gregorio@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:30:08 +0200 Subject: [PATCH 34/40] Update neo/io/nestio.py Co-authored-by: Michael Denker --- neo/io/nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 55298a1da..9cf21d901 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -64,7 +64,7 @@ def __init__(self, filenames=None, target_object='SpikeTrain', **kwargs): Parameters ---------- filenames: string or list of strings, default=None - The filename or list of filename to load. + The filename or list of filenames to load. target_object : string or list of strings, default='SpikeTrain' The type of neo object that should be read out from the input. Options are: 'SpikeTrain', 'AnalogSignal' From 8c5e62e55565b40c819e15d489ebf96928107469 Mon Sep 17 00:00:00 2001 From: Aitor Morales-Gregorio <43403140+morales-gregorio@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:30:19 +0200 Subject: [PATCH 35/40] Update neo/io/nestio.py Co-authored-by: Michael Denker --- neo/io/nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 9cf21d901..bf784f8ec 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -76,7 +76,7 @@ def __init__(self, filenames=None, target_object='SpikeTrain', **kwargs): raise ValueError(f'{target_object} is not a valid object type. ' f'Valid values are {self.objects}.') - # Ensure right dimensionality + # Ensure filenames is always a list if isinstance(filenames, str): filenames = [filenames] From d1e97a0b6d7ae2e76de10918d0bc011df5911382 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Mon, 8 Apr 2024 20:15:27 +0200 Subject: [PATCH 36/40] turn loop to compact list comprehension --- neo/io/nestio.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 55298a1da..149aa3f01 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -84,9 +84,7 @@ def __init__(self, filenames=None, target_object='SpikeTrain', **kwargs): self.filenames = filenames self.target_object = target_object - self.IOs = [] - for filename in filenames: - self.IOs.append(ColumnIO(filename, **kwargs)) + self.IOs = [ColumnIO(filename, **kwargs) for filename in filenames] def __read_analogsignals( self, From 78bb2abc9cf736febcacb6d1712730802e417f23 Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Mon, 8 Apr 2024 20:17:18 +0200 Subject: [PATCH 37/40] fix hardcoded column indices --- neo/io/nestio.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 149aa3f01..8c8116a94 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -444,18 +444,23 @@ def _get_selected_ids(self, gid, id_column, time_column, t_start, t_stop, time_u Returns list of selected gids """ - gids = np.array([0, data.shape[0]]) + gids = np.array([0, data.shape[id_column]]) if id_column is not None: - gids = np.array([np.searchsorted(data[:, 0], gid, side='left'), - np.searchsorted(data[:, 0], gid, side='right')]) + gids = np.array([np.searchsorted(data[:, id_column], gid, side='left'), + np.searchsorted(data[:, id_column], gid, side='right')]) gid_data = data[gids[0]:gids[1], :] # select only requested time range id_shifts = np.array([0, 0]) if time_column is not None: - id_shifts[0] = np.searchsorted(gid_data[:, 1], t_start.rescale(time_unit).magnitude, side="left") + id_shifts[0] = np.searchsorted(gid_data[:, time_column], + t_start.rescale(time_unit).magnitude, + side="left") id_shifts[1] = ( - np.searchsorted(gid_data[:, 1], t_stop.rescale(time_unit).magnitude, side="left") - gid_data.shape[0] + np.searchsorted(gid_data[:, time_column], + t_stop.rescale(time_unit).magnitude, + side="left") + - gid_data.shape[0] ) selected_ids = gids + id_shifts From d65a104d77f33a4dcc46e1643d04a8f39808584e Mon Sep 17 00:00:00 2001 From: morales-gregorio Date: Mon, 8 Apr 2024 20:17:43 +0200 Subject: [PATCH 38/40] fix too long line --- neo/io/nestio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 8c8116a94..5e93fb83d 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -556,7 +556,8 @@ def read_segment( if isinstance(gid_list, tuple): if gid_list[0] > gid_list[1]: - raise ValueError("The second entry in gid_list must be " "greater or equal to the first entry.") + raise ValueError("The second entry in gid_list must be " + "greater or equal to the first entry.") gid_list = range(gid_list[0], gid_list[1] + 1) # __read_xxx() needs a list of IDs From 7530b843b947e59c3b2b1182f8fe72e21bb04f28 Mon Sep 17 00:00:00 2001 From: Aitor Morales-Gregorio <43403140+morales-gregorio@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:44:56 +0200 Subject: [PATCH 39/40] Update neo/io/nestio.py Co-authored-by: Hans Ekkehard Plesser --- neo/io/nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index 45bfa45c8..bb0dbb1df 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -159,7 +159,7 @@ def __read_analogsignals( data) # extracting complete gid list for anasig generation - if (gid_list == []) and id_column is not None: + if not gid_list and id_column is not None: current_gid_list = np.unique(data[:, id_column]) else: current_gid_list = gid_list From a96794b96c37dc7831dde3385bde868f24af9051 Mon Sep 17 00:00:00 2001 From: Aitor Morales-Gregorio <43403140+morales-gregorio@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:45:14 +0200 Subject: [PATCH 40/40] Update neo/io/nestio.py Co-authored-by: Hans Ekkehard Plesser --- neo/io/nestio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/io/nestio.py b/neo/io/nestio.py index bb0dbb1df..f13cd217e 100644 --- a/neo/io/nestio.py +++ b/neo/io/nestio.py @@ -127,7 +127,7 @@ def __read_analogsignals( if value_columns is not None: column_list += value_columns column_list_no_None = [c for c in column_list if c is not None] - if len(np.unique(column_list_no_None)) < len(column_list_no_None): + if len(set(column_list_no_None)) < len(column_list_no_None): raise ValueError( "One or more columns have been specified to contain " "the same data. Columns were specified to {column_list_no_None}."