Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import numbers
- from typing import NamedTuple, Tuple, Union, TypeVar, List
- import numpy as np
- class NullSlicingException(ValueError):
- def __init__(self, msg="", out_shape=None):
- super().__init__(msg)
- self.out_shape = out_shape
- class StartLenStride(NamedTuple):
- start: int
- len: int
- stride: int
- def slice_to_start_len_stride(slice_: slice, max_len: int) -> StartLenStride:
- """
- Convert a slice object, possibly with None or negative members, into positive integers for start, length, and stride.
- Raises NullSlicingException if there would be no results returned from this indexing.
- If stride is negative, start is still the lower corner and length is still positive: the negative stride
- must be done after the data read.
- :param slice_:
- :param max_len: maximum length of the dimension
- :return: tuple of positive integer start, length, +ve or -ve stride
- :raises: NullSlicingException
- """
- start, stop, stride = slice_.indices(max_len)
- shape = stop - start
- if shape == 0 or shape * stride < 0:
- raise NullSlicingException("Requested slice is empty")
- return StartLenStride(min(start, stop), abs(shape), stride)
- def int_to_start_len_stride(i: int, max_len: int) -> StartLenStride:
- """
- Convert an integer index, possibly negative, into positive integers for start, length, and stride.
- Raises NullSlicingException if there would be no results returned from this indexing
- :param i: integer index
- :param max_len: maximum length of the dimension
- :return: tuple of positive integer start, length, stride
- """
- if -max_len < i < 0:
- begin = i + max_len
- elif i >= max_len or i < -max_len:
- raise IndexError("index {} is out of bounds for axis with size".format(i, max_len))
- else:
- begin = i
- return StartLenStride(begin, 1, 1)
- class Roi(NamedTuple):
- """ROI described by positive integers.
- """
- start: Tuple[int, ...]
- read_shape: Tuple[int, ...]
- stride: Tuple[int, ...]
- out_shape: Tuple[int, ...]
- ConcreteSliceLike = Union[slice, int]
- SliceLike = Union[ConcreteSliceLike, type(Ellipsis), np.newaxis]
- SliceArgs = TypeVar("SliceArgs", SliceLike, Tuple[SliceLike])
- class Indexer:
- type_msg = (
- "Advanced selection inappropriate. "
- "Only numbers, slices (`:`), ellipsis (`...`), and np.newaxis (None) "
- "are valid indices (or tuples thereof); got {}"
- )
- non_indexes = (None, Ellipsis)
- def __init__(self, max_shape: Tuple[int, ...]):
- """Class for normalising some of numpy's indexing scheme.
- :param max_shape: shape of the array to be read
- """
- self.max_shape = max_shape
- self.ndim = len(max_shape)
- def _handle_newaxis_ellipses(self, index_tup: Tuple[SliceLike]) -> Tuple[List[ConcreteSliceLike], List[int]]:
- """
- :param index_tup:
- :return: list of ints and slices the same length as the dataset's shape,
- and list of indices at which to insert newaxes (must be in inserted in order)
- """
- concrete_indices = sum(idx not in self.non_indexes for idx in index_tup)
- index_lst = []
- newaxis_at = []
- has_ellipsis = False
- int_count = 0
- for item in index_tup:
- if isinstance(item, numbers.Number):
- int_count += 1
- if item is None:
- newaxis_at.append(len(index_lst) + len(newaxis_at) - int_count)
- elif item == Ellipsis:
- if has_ellipsis:
- raise IndexError("an index can only have a single ellipsis ('...')")
- has_ellipsis = True
- initial_len = len(index_lst)
- while len(index_lst) + (concrete_indices - initial_len) < self.ndim:
- index_lst.append(slice(None))
- else:
- index_lst.append(item)
- if len(index_lst) > self.ndim:
- raise IndexError("too many indices for array")
- while len(index_lst) < self.ndim:
- index_lst.append(slice(None))
- return index_lst, newaxis_at
- def __getitem__(self, args: SliceArgs):
- index_tup = np.index_exp[args]
- index_lst, newaxis_at = self._handle_newaxis_ellipses(index_tup)
- start_readshape_stride = []
- out_shape = []
- for item in index_lst:
- d = len(start_readshape_stride)
- max_len = self.max_shape[d]
- if isinstance(item, slice):
- try:
- bls = slice_to_start_len_stride(item, max_len)
- start_readshape_stride.append(bls)
- out_shape.append(bls[1])
- except NullSlicingException:
- start_readshape_stride.append((None, None, None))
- out_shape.append(0)
- elif isinstance(item, (int, np.integer)):
- start_readshape_stride.append(
- int_to_start_len_stride(int(item), max_len)
- )
- else:
- raise TypeError(self.type_msg.format(type(item)))
- for newax_idx in newaxis_at:
- out_shape.insert(newax_idx, 1)
- out_shape = tuple(out_shape)
- if 0 in out_shape:
- raise NullSlicingException("Slicing has a 0-length dimension", out_shape)
- start, read_shape, stride = zip(*start_readshape_stride)
- return Roi(start, read_shape, stride, out_shape)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement