From e7f83ef1407e97b7cd591566e839437beaee4980 Mon Sep 17 00:00:00 2001 From: Anton Soloviev Date: Sat, 17 Oct 2026 19:10:02 -0700 Subject: [PATCH 1/2] Add Anton Soloviev to the contributor list --- doc/source/authors.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/authors.rst b/doc/source/authors.rst index 035fdfbe7..81fb794ab 100644 --- a/doc/source/authors.rst +++ b/doc/source/authors.rst @@ -106,6 +106,7 @@ and may not be the current affiliation of a contributor. * Aditya Singh (github) * Stanley C. * Axel Coiffard-Donaty +* Anton Soloviev 1. Centre de Recherche en Neuroscience de Lyon, CNRS UMR5292 - INSERM U1028 - Université Claude Bernard Lyon 1 2. Unité de Neuroscience, Information et Complexité, CNRS UPR 3293, Gif-sur-Yvette, France From 99465283a0023323d4d16287556ec8506b3a517a Mon Sep 17 00:00:00 2001 From: Anton Soloviev Date: Sat, 17 Oct 2026 19:10:02 -0700 Subject: [PATCH 2/2] Preserve sample timestamps when slicing AnalogSignal Normalize slice starts against the signal length and apply the stride to the sampling period in both bare and tuple indexing. Negative or clipped starts then refer to the samples actually selected, and a bare stride has the same timing as its tuple equivalent. --- neo/core/analogsignal.py | 12 ++++----- neo/test/coretest/test_analogsignal.py | 35 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/neo/core/analogsignal.py b/neo/core/analogsignal.py index 252224f16..aaaf32146 100644 --- a/neo/core/analogsignal.py +++ b/neo/core/analogsignal.py @@ -311,10 +311,9 @@ def __getitem__(self, i): obj = pq.Quantity(obj.magnitude, units=obj.units) else: if isinstance(j, slice): - if j.start: - obj.t_start = self.t_start + j.start * self.sampling_period - if j.step: - obj.sampling_period *= j.step + start, _, step = j.indices(len(self)) + obj.t_start = self.t_start + start * self.sampling_period + obj.sampling_period = self.sampling_period * step elif isinstance(j, np.ndarray): raise NotImplementedError("Arrays not yet supported") # in the general case, would need to return # IrregularlySampledSignal(Array) @@ -329,8 +328,9 @@ def __getitem__(self, i): obj.array_annotate(**deepcopy(self.array_annotations_at_index(k))) elif isinstance(i, slice): obj = super().__getitem__(i) - if i.start: - obj.t_start = self.t_start + i.start * self.sampling_period + start, _, step = i.indices(len(self)) + obj.t_start = self.t_start + start * self.sampling_period + obj.sampling_period = self.sampling_period * step obj.array_annotations = deepcopy(self.array_annotations) elif isinstance(i, np.ndarray): # Indexing of an AnalogSignal is only consistent if the resulting number of diff --git a/neo/test/coretest/test_analogsignal.py b/neo/test/coretest/test_analogsignal.py index b79c17f32..f0ece3ced 100644 --- a/neo/test/coretest/test_analogsignal.py +++ b/neo/test/coretest/test_analogsignal.py @@ -600,6 +600,41 @@ def test__slice_should_change_sampling_period(self): assert_array_equal(result2.magnitude, self.data1[::2, :1]) assert_array_equal(result3.magnitude, self.data1[1:7:2, :1]) + def test__slice_timestamps_match_selected_samples(self): + signal = AnalogSignal(np.arange(40).reshape(20, 2), units="uV", sampling_rate=4 * pq.Hz, t_start=2 * pq.s) + original_times = signal.times.copy() + original_period = signal.sampling_period.copy() + slices = ( + slice(None, None, 2), + slice(3, 17, 3), + slice(-5, None), + slice(-12, -2, 2), + slice(-100, None, 3), + slice(None, None, -1), + slice(17, 2, -3), + slice(100, None, -2), + ) + for time_slice in slices: + for channel_slice in (None, slice(None), 1): + with self.subTest(time_slice=time_slice, channel_slice=channel_slice): + if channel_slice is None: + result = signal[time_slice] + expected_data = signal.magnitude[time_slice] + else: + result = signal[time_slice, channel_slice] + expected_data = signal.magnitude[time_slice, channel_slice].reshape(result.shape) + assert_array_equal(result.magnitude, expected_data) + assert_array_equal(result.times, original_times[time_slice]) + self.assertEqual(result.sampling_period, original_period * (time_slice.step or 1)) + assert_array_equal(signal.times, original_times) + self.assertEqual(signal.sampling_period, original_period) + + def test__nested_slices_preserve_sample_times(self): + signal = AnalogSignal(np.arange(20), units="mV", sampling_rate=4 * pq.Hz, t_start=2 * pq.s) + result = signal[1::2][-3:] + assert_array_equal(result.magnitude, signal.magnitude[1::2][-3:]) + assert_array_equal(result.times, signal.times[1::2][-3:]) + def test__index_dim1_and_slice_dim0_should_return_single_channel_analogsignal(self): result = self.signal1[2:7, 0] self.assertIsInstance(result, AnalogSignal)