Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ and may not be the current affiliation of a contributor.
* Sai Asish Yamani [51]
* Kevin Doran [52]
* Aditya Singh (github)
* Arthur031221 (github)

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
Expand Down
7 changes: 5 additions & 2 deletions neo/core/analogsignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,13 +506,16 @@ def time_slice(self, t_start, t_stop):
the nearest sampling bin. The time bin for t_stop will be chosen to
make the duration of the resultant signal as close as possible to
t_stop - t_start. This means that for a given duration, the size
of the slice will always be the same.
of the slice will always be the same. Either parameter can also be
None, in which case that end of the signal is not cut.
"""

# checking start time and transforming to start index
if t_start is None:
i = 0
t_start = 0 * pq.s
# index 0 is self.t_start, and the stop index below is an offset
# from t_start, so the signal's own start is the origin here
t_start = self.t_start
else:
i = self.time_index(t_start)

Expand Down
37 changes: 37 additions & 0 deletions neo/test/coretest/test_analogsignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,43 @@ def test__time_slice__no_explicit_time(self):
assert_array_equal(result.magnitude, targ.magnitude)
assert_same_sub_schema(result, targ)

def test__time_slice__no_explicit_start_measures_from_signal_start(self):
# t_start=None means "do not cut the start", so the stop index has to be
# counted from the signal's own start. Counting it from 0 s instead
# lands in several different ways: too many samples, too few, a negative
# index that slices from the end, and an index past the end that raises.
# The last row is a sub-sample offset, where the two origins round to
# different indices even though the offset is under half a period.
cases = [
(20, 1.0 * pq.Hz, 5.0 * pq.s, 7.0 * pq.s, 2),
(20, 1.0 * pq.Hz, -2.0 * pq.s, 1.0 * pq.s, 3),
(20, 1.0 * pq.Hz, -2.0 * pq.s, -1.0 * pq.s, 1),
(20, 1.0 * pq.Hz, 30.0 * pq.s, 33.0 * pq.s, 3),
(50, 1.0 * pq.kHz, 0.4 * pq.ms, 10.6 * pq.ms, 10),
]
for n, rate, t_start, t_stop, n_expected in cases:
data = np.arange(float(n)).reshape(n, 1) * pq.mV
signal = AnalogSignal(data, sampling_rate=rate, t_start=t_start)

result = signal.time_slice(None, t_stop)

self.assertIsInstance(result, AnalogSignal)
assert_neo_object_is_compliant(result)
self.assertEqual(len(result), n_expected, f"t_start={t_start}, t_stop={t_stop}")
self.assertEqual(result.t_start, t_start)
assert_array_equal(result.magnitude, signal.magnitude[:n_expected])
assert_arrays_almost_equal(result.times, signal.time_slice(t_start, t_stop).times, 1e-12 * pq.ms)

def test__time_slice__no_explicit_start_refuses_what_explicit_start_refuses(self):
# None has to behave as passing the signal's own t_start does, which
# includes refusing a t_stop past the end rather than returning a slice
# measured from zero. This call returns 1600 samples without the fix.
signal = AnalogSignal(
np.arange(2000.0).reshape(2000, 1) * pq.mV, sampling_rate=1.0 * pq.kHz, t_start=-0.5 * pq.s
)
self.assertRaises(ValueError, signal.time_slice, signal.t_start, 1.6 * pq.s)
self.assertRaises(ValueError, signal.time_slice, None, 1.6 * pq.s)

def test__time_slice_deepcopy_data(self):
result = self.signal1.time_slice(None, None)

Expand Down