Skip to content

Commit 616bce4

Browse files
authored
Merge pull request #537 from jgrewe/invalid_event_slices
Invalid event slices
2 parents 7db7001 + eadd3a0 commit 616bce4

File tree

6 files changed

+85
-19
lines changed

6 files changed

+85
-19
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ name: NIXPy tests and linting
22

33
on:
44
# Run one build a month at 01:00
5-
schedule:
6-
- cron: '0 1 1 * *'
5+
# schedule:
6+
# - cron: '0 1 1 * *'
77
push:
88
branches:
99
- master

nixio/data_array.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ def get_slice(self, positions, extents=None, mode=DataSliceMode.Index):
316316
```
317317
318318
Note: The extents are *not* the end positions but the extent of the slice!
319+
In the case of regularly sampled dimensions, reaching beyond the start of end of the respective dimension will cause an exception. For irregularly sampled data no
320+
exception will be raised but the returned DataView might be invalid and empty.
319321
320322
:param positions: Specifies the start of the data slice. List of either indices or data positions depending on the DataSliceMode.
321323
:type positions: list length must match dimensionality of the data.
@@ -360,13 +362,29 @@ def get_slice(self, positions, extents=None, mode=DataSliceMode.Index):
360362
def _get_slice_bydim(self, positions, extents):
361363
dpos, dext = [], []
362364
for dim, pos, ext in zip(self.dimensions, positions, extents):
363-
if dim.dimension_type in (DimensionType.Sample,
364-
DimensionType.Range):
365-
dpos.append(dim.index_of(pos, mode=IndexMode.GreaterOrEqual))
366-
dext.append(dim.index_of(pos + ext) - dpos[-1])
365+
if dim.dimension_type == DimensionType.Sample:
366+
start_pos = dim.index_of(pos, mode=IndexMode.GreaterOrEqual)
367+
extent = dim.index_of(pos + ext) - start_pos
368+
elif dim.dimension_type == DimensionType.Range:
369+
try:
370+
start_pos = dim.index_of(pos, mode=IndexMode.GreaterOrEqual)
371+
extent = dim.index_of(pos + ext) - start_pos
372+
except IndexError:
373+
start_pos = -1
374+
extent = -1
375+
except Exception as e:
376+
raise e
367377
elif dim.dimension_type == DimensionType.Set:
368-
dpos.append(int(pos))
369-
dext.append(int(ext))
378+
start_pos = int(pos)
379+
extent = int(ext)
380+
else:
381+
raise IncompatibleDimensions(f"Unknown dimension type: {dim.dimension_type}",
382+
"data_array._get_slice_by_dim")
383+
384+
if extent < 0:
385+
return DataView(self, None)
386+
dpos.append(start_pos)
387+
dext.append(extent)
370388
slices = tuple(slice(p, p + e) for p, e in zip(dpos, dext))
371389
return DataView(self, slices)
372390

nixio/dimensions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ def range_indices(self, start_position, end_position, mode=SliceMode.Exclusive):
693693
:raises: ValueError if invalid mode is given
694694
:raises: Index Error if start position is greater than end position.
695695
"""
696-
if mode is not SliceMode.Exclusive and mode is not SliceMode.Inclusive:
696+
if mode not in (SliceMode.Exclusive, SliceMode.Inclusive):
697697
raise ValueError("Unknown SliceMode: {}".format(mode))
698698
if start_position > end_position:
699699
raise IndexError("Start position {} is greater than end position {}.".format(start_position, end_position))

nixio/tag.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright © 2014, German Neuroinformatics Node (G-Node)
2+
# Copyright © 2014 - 2022, German Neuroinformatics Node (G-Node)
33
#
44
# All rights reserved.
55
#
@@ -260,6 +260,8 @@ def position(self):
260260

261261
@position.setter
262262
def position(self, pos):
263+
if pos is not None and not hasattr(pos, "__getitem__"):
264+
pos = [pos]
263265
if pos is None or len(pos) == 0:
264266
if self._h5group.has_data("position"):
265267
del self._h5group["position"]
@@ -281,6 +283,8 @@ def extent(self):
281283

282284
@extent.setter
283285
def extent(self, ext):
286+
if ext is not None and not hasattr(ext, "__getitem__"):
287+
ext = [ext]
284288
if ext is None or len(ext) == 0:
285289
if self._h5group.has_data("extent"):
286290
del self._h5group["extent"]
@@ -313,6 +317,8 @@ def tagged_data(self, refidx, stop_rule=SliceMode.Exclusive):
313317
"do not match ", extent)
314318

315319
slices = self._calc_data_slices(ref, self.position, self.extent, stop_rule)
320+
if not all(slices):
321+
return DataView(ref, slices)
316322
if not self._slices_in_data(ref, slices):
317323
raise OutOfBounds("References data slice out of the extent of the "
318324
"DataArray!")

nixio/test/test_data_array.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import unittest
1414
import numpy as np
1515
import nixio as nix
16+
from nixio.data_array import DataSliceMode
1617
from nixio.exceptions import IncompatibleDimensions
1718
from .tmp import TempDir
1819

@@ -444,6 +445,21 @@ def test_get_slice(self):
444445
with self.assertRaises(IncompatibleDimensions):
445446
da3d.get_slice((0, 0, 0), (3, 9, 40, 1))
446447

448+
dslice = da2d.get_slice([20, 1], [10, 1], DataSliceMode.Data)
449+
self.assertFalse(dslice.valid)
450+
451+
time_vector = np.arange(0.0, 10., 0.001)
452+
indices = np.random.rand(len(time_vector))
453+
454+
event_data = time_vector[(indices < 0.1)]
455+
event_data = event_data[(event_data < 4) | (event_data > 7)]
456+
457+
event_da = self.block.create_data_array("event_data", "nix.events", data=event_data, unit="s")
458+
event_da.append_range_dimension_using_self()
459+
selection = event_da.get_slice([4.5], [1.0], nix.DataSliceMode.Data)
460+
self.assertFalse(selection.valid)
461+
np.testing.assert_almost_equal(np.array([]), selection[:])
462+
447463
def test_dim_one_based(self):
448464
self.array.append_set_dimension()
449465
self.array.append_range_dimension(range(10))

nixio/test/test_tag.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -579,26 +579,52 @@ def test_tagged_range_dim(self):
579579
# +0.1 should round up (ceil) the start position
580580
# +0.1 * 2 should round down (floor) the stop position and works the same for both inclusive and
581581
# exclusive
582-
tag.position = [pos+0.1]
583-
tag.extent = [ext+0.1]
584-
start = pos+1
585-
stop = pos+ext+1
582+
tag.position = [pos + 0.1]
583+
tag.extent = [ext + 0.1]
584+
start = pos + 1
585+
stop = pos + ext + 1
586586
np.testing.assert_array_almost_equal(tag.tagged_data(0), da[start:stop])
587587
np.testing.assert_array_almost_equal(tag.tagged_data(0, nix.SliceMode.Exclusive), da[start:stop])
588588
np.testing.assert_array_almost_equal(tag.tagged_data(0, nix.SliceMode.Inclusive), da[start:stop])
589589

590-
if pos+ext+2 < len(da):
590+
if pos + ext + 2 < len(da):
591591
# +0.9 should round up (ceil) the start position
592592
# +0.9 * 2 should round down (floor) the stop position and works the same for both inclusive and
593593
# exclusive
594-
tag.position = [pos+0.9]
595-
tag.extent = [ext+0.9]
596-
start = pos+1
597-
stop = pos+ext+2
594+
tag.position = [pos + 0.9]
595+
tag.extent = [ext + 0.9]
596+
start = pos + 1
597+
stop = pos + ext + 2
598598
np.testing.assert_array_almost_equal(tag.tagged_data(0), da[start:stop])
599599
np.testing.assert_array_almost_equal(tag.tagged_data(0, nix.SliceMode.Exclusive), da[start:stop])
600600
np.testing.assert_array_almost_equal(tag.tagged_data(0, nix.SliceMode.Inclusive), da[start:stop])
601601

602+
time_vector = np.arange(0.0, 10., 0.001)
603+
indices = np.random.rand(len(time_vector))
604+
event_data = time_vector[(indices < 0.1)]
605+
event_data = event_data[(event_data < 4) | (event_data > 7)]
606+
607+
event_da = self.block.create_data_array("event_data", "nix.events", data=event_data, unit="s")
608+
event_da.append_range_dimension_using_self()
609+
610+
tt = self.block.create_tag("no_event_segment", "nix.segment", 4.5)
611+
tt.extent = 1.0
612+
tt.references.append(event_da)
613+
sl = tt.tagged_data(0)
614+
self.assertFalse(sl.valid)
615+
616+
tt2 = self.block.create_tag("beyond data", "nix.segment", 12.0)
617+
tt2.extent = 3.0
618+
tt2.references.append(event_da)
619+
sl = tt2.tagged_data(0)
620+
self.assertFalse(sl.valid)
621+
622+
tt3 = self.block.create_tag("reachingbeyonddata", "nix.segment", 8.5)
623+
tt3.extent = [3.0]
624+
tt3.references.append(event_da)
625+
sl = tt3.tagged_data(0)
626+
self.assertTrue(sl.valid)
627+
602628
def test_tagged_sampled_dim(self):
603629
"""
604630
Simple test where the slice can be calculated directly from the position and extent and compared to the original

0 commit comments

Comments
 (0)