From fc04ac7e2ece969e1206b0bea8d0f8e730ae1a9a Mon Sep 17 00:00:00 2001 From: Aitor Date: Wed, 26 Aug 2020 13:08:19 +0200 Subject: [PATCH 1/5] include analog signal merging utility function and the corresponding test, which passes in my machine --- neo/test/test_utils.py | 66 ++++++++++++++++++++++++++++++++-- neo/utils.py | 80 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 2 deletions(-) diff --git a/neo/test/test_utils.py b/neo/test/test_utils.py index 1623e5029..bbf973ef1 100644 --- a/neo/test/test_utils.py +++ b/neo/test/test_utils.py @@ -8,7 +8,7 @@ import quantities as pq from neo.rawio.examplerawio import ExampleRawIO from neo.io.proxyobjects import (AnalogSignalProxy, SpikeTrainProxy, - EventProxy, EpochProxy) + EventProxy, EpochProxy) from neo.core.dataobject import ArrayDict from neo.core import (Block, Segment, AnalogSignal, IrregularlySampledSignal, @@ -20,7 +20,10 @@ assert_same_attributes, assert_same_annotations) -from neo.utils import (get_events, get_epochs, add_epoch, match_events, cut_block_by_epochs) +from neo.utils import (get_events, get_epochs, add_epoch, match_events, + cut_block_by_epochs, merge_anasiglist) + +from copy import copy class BaseProxyTest(unittest.TestCase): @@ -465,6 +468,65 @@ def test__cut_block_by_epochs(self): epoch2.time_shift(- epoch.times[0]).time_slice(t_start=0 * pq.s, t_stop=epoch.durations[0])) + def test_merge_anasiglist(self): + baselist = [AnalogSignal(np.arange(55.0).reshape((11, 5)), + units="mV", + sampling_rate=1*pq.kHz)]*2 + + # Sanity of inputs + self.assertRaises(TypeError, merge_anasiglist, baselist[0]) + self.assertRaises(TypeError, merge_anasiglist, baselist, axis=1.0) + self.assertRaises(TypeError, merge_anasiglist, baselist, axis=9) + self.assertRaises(ValueError, merge_anasiglist, []) + self.assertRaises(ValueError, merge_anasiglist, [baselist[0]]) + + # Different units + wrongunits = AnalogSignal(np.arange(55.0).reshape((11, 5)), + units="uV", + sampling_rate=1*pq.kHz) + analist = baselist + [wrongunits] + self.assertRaises(ValueError, merge_anasiglist, analist) + + # Different sampling rate + wrongsampl = AnalogSignal(np.arange(55.0).reshape((11, 5)), + units="mV", + sampling_rate=100 * pq.kHz) + analist = baselist + [wrongsampl] + self.assertRaises(ValueError, merge_anasiglist, analist) + + # Different t_start + wrongstart = AnalogSignal(np.arange(55.0).reshape((11, 5)), + t_start=10*pq.s, + units="mV", + sampling_rate=1*pq.kHz) + analist = baselist + [wrongstart] + self.assertRaises(ValueError, merge_anasiglist, analist) + + # Different shape + wrongshape = AnalogSignal(np.arange(50.0).reshape((10, 5)), + units="mV", + sampling_rate=1*pq.kHz) + analist = baselist + [wrongshape] + self.assertRaises(ValueError, merge_anasiglist, analist) + + # Different shape + wrongshape = AnalogSignal(np.arange(50.0).reshape((5, 10)), + units="mV", + sampling_rate=1*pq.kHz) + analist = baselist + [wrongshape] + self.assertRaises(ValueError, merge_anasiglist, analist, axis=0) + + # Check that the generated analogsignals are the corresponding ones + for axis in [0, 1]: + ana = np.concatenate((np.arange(55.0).reshape((11, 5)), + np.arange(55.0).reshape((11, 5))), + axis=axis) + signal1 = AnalogSignal(ana, units="mV", sampling_rate=1*pq.kHz) + signal2 = merge_anasiglist(copy(baselist), axis=axis) + assert_arrays_equal(signal1.magnitude, signal2.magnitude) + assert_same_attributes(signal1, signal2) + assert_same_annotations(signal1, signal2) + class TestUtilsWithProxyObjects(BaseProxyTest): def test__get_events(self): diff --git a/neo/utils.py b/neo/utils.py index 84eac6193..50ba15c94 100644 --- a/neo/utils.py +++ b/neo/utils.py @@ -10,6 +10,86 @@ import quantities as pq +def merge_anasiglist(anasiglist, axis=1): + """ + Merges neo.AnalogSignal objects into a single object. + + Units, sampling_rate, t_start, t_stop and signals shape must be the same + for all signals. Otherwise a ValueError is raised. + + Parameters + ---------- + anasiglist: list of neo.AnalogSignal + list of analogsignals that will be merged + axis: int + axis along which to perform the merging + `axis = 1` corresponds to stacking the analogsignals + `axis = 0` corresponds to concatenating the analogsignals + Default: 1 + + Returns + ------- + merged_anasig: neo.AnalogSignal + merged output signal + """ + + # Sanity of inputs + if not isinstance(anasiglist, list): + raise TypeError('anasiglist must be a list') + if not isinstance(axis, int) or axis not in [0, 1]: + raise TypeError('axis must be 0 or 1') + if len(anasiglist) == 0: + raise ValueError('Empty list! nothing to be merged') + if len(anasiglist) == 1: + raise ValueError('Passed list of length 1, nothing to be merged') + + # Check units, sampling_rate, t_start, t_stop and signal shape + for anasig in anasiglist: + if not anasiglist[0].units == anasig.units: + raise ValueError('Units must be the same for all signals') + if not anasiglist[0].sampling_rate == anasig.sampling_rate: + raise ValueError('Sampling rate must be the same for all signals') + if not anasiglist[0].t_start == anasig.t_start: + raise ValueError('t_start must be the same for all signals') + if axis == 0: + if not anasiglist[0].magnitude.shape[1] == \ + anasig.magnitude.shape[1]: + raise ValueError('Analogsignals appear to contain different ' + 'number of channels!') + if axis == 1: + if not anasiglist[0].magnitude.shape[0] == \ + anasig.magnitude.shape[0]: + raise ValueError('Cannot merge signals of different length.') + + # Initialize the arrays + anasig0 = anasiglist.pop(0) + data_array = anasig0.magnitude + sr = anasig0.sampling_rate + t_start = anasig0.t_start + units = anasig0.units + + # Get the full array annotations + for anasig in anasiglist: + anasig0.array_annotations = anasig0._merge_array_annotations(anasig) + + array_annot = anasig0.array_annotations + del anasig0 + + while len(anasiglist) != 0: + anasig = anasiglist.pop(0) + data_array = np.concatenate((data_array, anasig.magnitude), + axis=axis) + del anasig + + # Create new analogsignal object to contain the analogsignals + merged_anasig = neo.AnalogSignal(data_array, + sampling_rate=sr, + t_start=t_start, + units=units, + array_annotations=array_annot) + return merged_anasig + + def get_events(container, **properties): """ This function returns a list of Event objects, corresponding to given From 6e2c239460707c436681c4d8e7985d3b07bfccf9 Mon Sep 17 00:00:00 2001 From: Aitor Date: Wed, 26 Aug 2020 13:12:59 +0200 Subject: [PATCH 2/5] fix PEP8 --- neo/test/test_utils.py | 12 ++++++------ neo/utils.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/neo/test/test_utils.py b/neo/test/test_utils.py index bbf973ef1..f7842ed68 100644 --- a/neo/test/test_utils.py +++ b/neo/test/test_utils.py @@ -471,7 +471,7 @@ def test__cut_block_by_epochs(self): def test_merge_anasiglist(self): baselist = [AnalogSignal(np.arange(55.0).reshape((11, 5)), units="mV", - sampling_rate=1*pq.kHz)]*2 + sampling_rate=1 * pq.kHz)] * 2 # Sanity of inputs self.assertRaises(TypeError, merge_anasiglist, baselist[0]) @@ -483,7 +483,7 @@ def test_merge_anasiglist(self): # Different units wrongunits = AnalogSignal(np.arange(55.0).reshape((11, 5)), units="uV", - sampling_rate=1*pq.kHz) + sampling_rate=1 * pq.kHz) analist = baselist + [wrongunits] self.assertRaises(ValueError, merge_anasiglist, analist) @@ -498,21 +498,21 @@ def test_merge_anasiglist(self): wrongstart = AnalogSignal(np.arange(55.0).reshape((11, 5)), t_start=10*pq.s, units="mV", - sampling_rate=1*pq.kHz) + sampling_rate=1 * pq.kHz) analist = baselist + [wrongstart] self.assertRaises(ValueError, merge_anasiglist, analist) # Different shape wrongshape = AnalogSignal(np.arange(50.0).reshape((10, 5)), units="mV", - sampling_rate=1*pq.kHz) + sampling_rate=1 * pq.kHz) analist = baselist + [wrongshape] self.assertRaises(ValueError, merge_anasiglist, analist) # Different shape wrongshape = AnalogSignal(np.arange(50.0).reshape((5, 10)), units="mV", - sampling_rate=1*pq.kHz) + sampling_rate=1 * pq.kHz) analist = baselist + [wrongshape] self.assertRaises(ValueError, merge_anasiglist, analist, axis=0) @@ -521,7 +521,7 @@ def test_merge_anasiglist(self): ana = np.concatenate((np.arange(55.0).reshape((11, 5)), np.arange(55.0).reshape((11, 5))), axis=axis) - signal1 = AnalogSignal(ana, units="mV", sampling_rate=1*pq.kHz) + signal1 = AnalogSignal(ana, units="mV", sampling_rate=1 * pq.kHz) signal2 = merge_anasiglist(copy(baselist), axis=axis) assert_arrays_equal(signal1.magnitude, signal2.magnitude) assert_same_attributes(signal1, signal2) diff --git a/neo/utils.py b/neo/utils.py index 50ba15c94..faa99d753 100644 --- a/neo/utils.py +++ b/neo/utils.py @@ -53,12 +53,12 @@ def merge_anasiglist(anasiglist, axis=1): raise ValueError('t_start must be the same for all signals') if axis == 0: if not anasiglist[0].magnitude.shape[1] == \ - anasig.magnitude.shape[1]: + anasig.magnitude.shape[1]: raise ValueError('Analogsignals appear to contain different ' 'number of channels!') if axis == 1: if not anasiglist[0].magnitude.shape[0] == \ - anasig.magnitude.shape[0]: + anasig.magnitude.shape[0]: raise ValueError('Cannot merge signals of different length.') # Initialize the arrays From c1520f3be26de28c70518b5f337ade18fc123fcb Mon Sep 17 00:00:00 2001 From: Aitor Date: Wed, 26 Aug 2020 13:14:12 +0200 Subject: [PATCH 3/5] more PEP8 fixes --- neo/test/test_utils.py | 2 +- neo/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/neo/test/test_utils.py b/neo/test/test_utils.py index f7842ed68..55c85d626 100644 --- a/neo/test/test_utils.py +++ b/neo/test/test_utils.py @@ -496,7 +496,7 @@ def test_merge_anasiglist(self): # Different t_start wrongstart = AnalogSignal(np.arange(55.0).reshape((11, 5)), - t_start=10*pq.s, + t_start=10 * pq.s, units="mV", sampling_rate=1 * pq.kHz) analist = baselist + [wrongstart] diff --git a/neo/utils.py b/neo/utils.py index faa99d753..c2bfa3e5a 100644 --- a/neo/utils.py +++ b/neo/utils.py @@ -53,12 +53,12 @@ def merge_anasiglist(anasiglist, axis=1): raise ValueError('t_start must be the same for all signals') if axis == 0: if not anasiglist[0].magnitude.shape[1] == \ - anasig.magnitude.shape[1]: + anasig.magnitude.shape[1]: raise ValueError('Analogsignals appear to contain different ' 'number of channels!') if axis == 1: if not anasiglist[0].magnitude.shape[0] == \ - anasig.magnitude.shape[0]: + anasig.magnitude.shape[0]: raise ValueError('Cannot merge signals of different length.') # Initialize the arrays From cea21156b8afc23577f7122e80da4cb838b8fd09 Mon Sep 17 00:00:00 2001 From: Aitor Date: Wed, 26 Aug 2020 13:15:02 +0200 Subject: [PATCH 4/5] is this the right indentation for PEP8? --- neo/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/utils.py b/neo/utils.py index c2bfa3e5a..fb861661d 100644 --- a/neo/utils.py +++ b/neo/utils.py @@ -53,12 +53,12 @@ def merge_anasiglist(anasiglist, axis=1): raise ValueError('t_start must be the same for all signals') if axis == 0: if not anasiglist[0].magnitude.shape[1] == \ - anasig.magnitude.shape[1]: + anasig.magnitude.shape[1]: raise ValueError('Analogsignals appear to contain different ' 'number of channels!') if axis == 1: if not anasiglist[0].magnitude.shape[0] == \ - anasig.magnitude.shape[0]: + anasig.magnitude.shape[0]: raise ValueError('Cannot merge signals of different length.') # Initialize the arrays From 7876e5ce9d9cae9509d3b81ea60bc1106471db51 Mon Sep 17 00:00:00 2001 From: Aitor Date: Wed, 26 Aug 2020 13:15:55 +0200 Subject: [PATCH 5/5] last chance with indent PEP8 --- neo/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/utils.py b/neo/utils.py index fb861661d..92edc0e20 100644 --- a/neo/utils.py +++ b/neo/utils.py @@ -53,12 +53,12 @@ def merge_anasiglist(anasiglist, axis=1): raise ValueError('t_start must be the same for all signals') if axis == 0: if not anasiglist[0].magnitude.shape[1] == \ - anasig.magnitude.shape[1]: + anasig.magnitude.shape[1]: raise ValueError('Analogsignals appear to contain different ' 'number of channels!') if axis == 1: if not anasiglist[0].magnitude.shape[0] == \ - anasig.magnitude.shape[0]: + anasig.magnitude.shape[0]: raise ValueError('Cannot merge signals of different length.') # Initialize the arrays