@@ -37,6 +37,17 @@ class IndexMode(Enum):
37
37
GEQ = "geq"
38
38
39
39
40
+ class SliceMode (Enum ):
41
+ Exclusive = "exclusive"
42
+ Inclusive = "inclusive"
43
+
44
+ def to_index_mode (self ):
45
+ if self == self .Exclusive :
46
+ return IndexMode .Less
47
+ if self == self .Inclusive :
48
+ return IndexMode .LessOrEqual
49
+
50
+
40
51
class DimensionContainer (Container ):
41
52
"""
42
53
DimensionContainer extends Container to support returning different types
@@ -375,15 +386,14 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
375
386
if scaled_position < 0 :
376
387
if mode == IndexMode .GreaterOrEqual :
377
388
return 0
378
- # position is OOB (left side) but can't round up
389
+ # position is OOB (left side) but can't round up because LessOrEqual or Less
379
390
raise IndexError ("Position {} is out of bounds for SampledDimension with offset {} and mode {}" .format (
380
391
position , offset , mode .name
381
392
))
382
393
383
394
if np .isclose (position , 0 ) and mode == IndexMode .Less :
384
- raise IndexError ("Position {} is out of bounds for SetDimension with mode {}" .format (position , mode .name ))
385
-
386
- index = int (np .floor (scaled_position ))
395
+ raise IndexError ("Position {} is out of bounds for SampledDimension with mode {}" .format (position , mode .name ))
396
+ index = int (np .round (scaled_position ))
387
397
if np .isclose (scaled_position , index ):
388
398
# exact position
389
399
if mode in (IndexMode .GreaterOrEqual , IndexMode .LessOrEqual ):
@@ -393,13 +403,50 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
393
403
# exact position and Less mode
394
404
return index - 1
395
405
raise ValueError ("Unknown IndexMode: {}" .format (mode ))
406
+ if index < scaled_position :
407
+ if mode in (IndexMode .LessOrEqual , IndexMode .Less ):
408
+ return index
409
+ elif mode == IndexMode .GreaterOrEqual :
410
+ return index + 1
411
+ else :
412
+ raise ValueError ("Unknown IndexMode: {}" .format (mode ))
413
+ else :
414
+ if mode in (IndexMode .LessOrEqual , IndexMode .Less ):
415
+ return index - 1
416
+ elif mode == IndexMode .GreaterOrEqual :
417
+ return index
418
+ else :
419
+ raise ValueError ("Unknown IndexMode: {}" .format (mode ))
396
420
397
- if mode == IndexMode .GreaterOrEqual : # and inexact position
398
- return index + 1
399
- if mode in (IndexMode .LessOrEqual , IndexMode .Less ): # and inexact position
400
- return index
421
+ def range_indices (self , start_position , end_position , mode = SliceMode .Exclusive ):
422
+ """
423
+ Returns the start and end indices in this dimension that are matching to the given start and end position.
401
424
402
- raise ValueError ("Unknown IndexMode: {}" .format (mode ))
425
+ :param start_position: the start position of the range.
426
+ :type start_position: float
427
+ :param end_position: the end position of the range.
428
+ :type end_position: float
429
+ :param mode: The nixio.SliceMode. Defaults to nixio.SliceMode.Exclusive, i.e. the end position is not part of the range.
430
+ :type mode: nixio.SliceMode
431
+
432
+ :returns: The respective start and end indices. None, if the range is empty!
433
+ :rtype: tuple of int
434
+
435
+ :raises: ValueError if invalid mode is given
436
+ :raises: Index Error if start position is greater than end position.
437
+ """
438
+ if mode is not SliceMode .Exclusive and mode is not SliceMode .Inclusive :
439
+ raise ValueError ("Unknown SliceMode: {}" .format (mode ))
440
+
441
+ end_mode = IndexMode .Less if mode == SliceMode .Exclusive else IndexMode .LessOrEqual
442
+ try :
443
+ start_index = self .index_of (start_position , mode = IndexMode .GreaterOrEqual )
444
+ end_index = self .index_of (end_position , mode = end_mode )
445
+ except IndexError :
446
+ return None
447
+ if start_index > end_index :
448
+ return None
449
+ return (start_index , end_index )
403
450
404
451
def axis (self , count , start = None , start_position = None ):
405
452
"""
@@ -516,7 +563,7 @@ def is_alias(self):
516
563
elif self .has_link and self .dimension_link ._data_object_type == "DataArray" :
517
564
return True
518
565
return False
519
-
566
+
520
567
@property
521
568
def _redirgrp (self ):
522
569
"""
@@ -528,7 +575,7 @@ def _redirgrp(self):
528
575
gname = self ._h5group .get_by_pos (0 ).name
529
576
return self ._h5group .open_group (gname )
530
577
return self ._h5group
531
-
578
+
532
579
@property
533
580
def ticks (self ):
534
581
if self .is_alias and not self .has_link :
@@ -585,7 +632,7 @@ def unit(self, unit):
585
632
else :
586
633
self ._h5group .set_attr ("unit" , unit )
587
634
588
- def index_of (self , position , mode = IndexMode .LessOrEqual ):
635
+ def index_of (self , position , mode = IndexMode .LessOrEqual , ticks = None ):
589
636
"""
590
637
Returns the index of a certain position in the dimension.
591
638
Raises IndexError if the position is out of bounds (depending on mode).
@@ -597,11 +644,14 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
597
644
If the mode is Less, the previous index of the matching tick is always returned.
598
645
If the mode is GreaterOrEqual and the position does not match a tick exactly, the next index is
599
646
returned.
647
+ :param ticks: Optional, the ticks stored in this dimension. If not passed as argument, they are (re)read from file.
648
+ :type ticks: iterable
600
649
601
650
:returns: The matching index
602
651
:rtype: int
603
652
"""
604
- ticks = self .ticks
653
+ if ticks is None :
654
+ ticks = self .ticks
605
655
if position < ticks [0 ]:
606
656
if mode == IndexMode .GreaterOrEqual :
607
657
return 0
@@ -626,6 +676,38 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
626
676
627
677
raise ValueError ("Unknown IndexMode: {}" .format (mode ))
628
678
679
+ def range_indices (self , start_position , end_position , mode = SliceMode .Exclusive ):
680
+ """
681
+ Returns the start and end indices in this dimension that are matching to the given start and end position.
682
+
683
+ :param start_position: the start position of the range.
684
+ :type start_position: float
685
+ :param end_position: the end position of the range.
686
+ :type end_position: float
687
+ :param mode: The nixio.SliceMode. Defaults to nixio.SliceMode.Exclusive, i.e. the end position is not part of the range.
688
+ :type mode: nixio.SliceMode
689
+
690
+ :returns: The respective start and end indices. None, if range is empty
691
+ :rtype: tuple of int
692
+
693
+ :raises: ValueError if invalid mode is given
694
+ :raises: Index Error if start position is greater than end position.
695
+ """
696
+ if mode is not SliceMode .Exclusive and mode is not SliceMode .Inclusive :
697
+ raise ValueError ("Unknown SliceMode: {}" .format (mode ))
698
+ if start_position > end_position :
699
+ raise IndexError ("Start position {} is greater than end position {}." .format (start_position , end_position ))
700
+ ticks = self .ticks
701
+ end_mode = IndexMode .Less if mode == SliceMode .Exclusive else IndexMode .LessOrEqual
702
+ try :
703
+ start_index = self .index_of (start_position , mode = IndexMode .GreaterOrEqual , ticks = ticks )
704
+ end_index = self .index_of (end_position , mode = end_mode , ticks = ticks )
705
+ except IndexError :
706
+ return None
707
+ if start_index > end_index :
708
+ return None
709
+ return (start_index , end_index )
710
+
629
711
def tick_at (self , index ):
630
712
"""
631
713
Returns the tick at the given index. Will throw an Exception if the
@@ -699,7 +781,7 @@ def labels(self, labels):
699
781
labels = list (labels )
700
782
self ._h5group .write_data ("labels" , labels , dtype = dt )
701
783
702
- def index_of (self , position , mode = IndexMode .LessOrEqual ):
784
+ def index_of (self , position , mode = IndexMode .LessOrEqual , dim_labels = None ):
703
785
"""
704
786
Returns the index of a certain position in the dimension.
705
787
Raises IndexError if the position is out of bounds (depending on mode and number of labels).
@@ -711,6 +793,8 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
711
793
If the position is not an integer (or is not equal to the nearest integer), then the value is
712
794
rounded down (for LessOrEqual) or rounded up (for GreaterOrEqual).
713
795
If the mode is Less, the previous integer is always returned.
796
+ :param dim_labels: The labels of this dimension, if None (default) the labels will be read from file.
797
+ :type dim_labels: iterable
714
798
715
799
:returns: The matching index
716
800
:rtype: int
@@ -724,12 +808,13 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
724
808
if position == 0 and mode == IndexMode .Less :
725
809
raise IndexError ("Position {} is out of bounds for SetDimension with mode {}" .format (position , mode .name ))
726
810
727
- labels = self .labels
728
- if labels and len (labels ) and position > len (labels )- 1 :
811
+ if dim_labels is None :
812
+ dim_labels = self .labels
813
+ if dim_labels and len (dim_labels ) and position > len (dim_labels ) - 1 :
729
814
if mode in (IndexMode .Less , IndexMode .LessOrEqual ):
730
- return len (labels ) - 1
815
+ return len (dim_labels ) - 1
731
816
raise IndexError ("Position {} is out of bounds for SetDimension with length {} and mode {}" .format (
732
- position , len (labels ), mode .name
817
+ position , len (dim_labels ), mode .name
733
818
))
734
819
735
820
index = int (np .floor (position ))
@@ -749,3 +834,36 @@ def index_of(self, position, mode=IndexMode.LessOrEqual):
749
834
return index
750
835
751
836
raise ValueError ("Unknown IndexMode: {}" .format (mode ))
837
+
838
+ def range_indices (self , start_position , end_position , mode = SliceMode .Exclusive ):
839
+ """
840
+ Returns the start and end indices in this dimension that are matching to the given start and end position.
841
+
842
+ :param start_position: the start position of the range.
843
+ :type start_position: float
844
+ :param end_position: the end position of the range.
845
+ :type end_position: float
846
+ :param mode: The nixio.SliceMode. Defaults to nixio.SliceMode.Exclusive, i.e. the end position is not part of the range.
847
+ :type mode: nixio.SliceMode
848
+
849
+ :returns: The respective start and end indices. None, if the range is empty
850
+ :rtype: tuple of int
851
+
852
+ :raises: ValueError if invalid mode is given
853
+ :raises: Index Error if start position is greater than end position.
854
+ """
855
+ if mode is not SliceMode .Exclusive and mode is not SliceMode .Inclusive :
856
+ raise ValueError ("Unknown SliceMode: {}" .format (mode ))
857
+
858
+ dim_labels = self .labels
859
+ end_mode = IndexMode .Less if mode == SliceMode .Exclusive else IndexMode .LessOrEqual
860
+ if start_position > end_position :
861
+ raise IndexError ("Start position {} is greater than end position {}." .format (start_position , end_position ))
862
+ try :
863
+ start = self .index_of (start_position , mode = IndexMode .GreaterOrEqual , dim_labels = dim_labels )
864
+ end = self .index_of (end_position , mode = end_mode , dim_labels = dim_labels )
865
+ except IndexError :
866
+ return None
867
+ if start > end :
868
+ return None
869
+ return (start , end )
0 commit comments