Advertisement
Guest User

Untitled

a guest
Jun 24th, 2019
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.36 KB | None | 0 0
  1. import numbers
  2. from typing import NamedTuple, Tuple, Union, TypeVar, List
  3.  
  4. import numpy as np
  5.  
  6.  
  7. class NullSlicingException(ValueError):
  8. def __init__(self, msg="", out_shape=None):
  9. super().__init__(msg)
  10. self.out_shape = out_shape
  11.  
  12.  
  13. class StartLenStride(NamedTuple):
  14. start: int
  15. len: int
  16. stride: int
  17.  
  18.  
  19. def slice_to_start_len_stride(slice_: slice, max_len: int) -> StartLenStride:
  20. """
  21. Convert a slice object, possibly with None or negative members, into positive integers for start, length, and stride.
  22.  
  23. Raises NullSlicingException if there would be no results returned from this indexing.
  24.  
  25. If stride is negative, start is still the lower corner and length is still positive: the negative stride
  26. must be done after the data read.
  27.  
  28. :param slice_:
  29. :param max_len: maximum length of the dimension
  30. :return: tuple of positive integer start, length, +ve or -ve stride
  31. :raises: NullSlicingException
  32. """
  33. start, stop, stride = slice_.indices(max_len)
  34. shape = stop - start
  35.  
  36. if shape == 0 or shape * stride < 0:
  37. raise NullSlicingException("Requested slice is empty")
  38.  
  39. return StartLenStride(min(start, stop), abs(shape), stride)
  40.  
  41.  
  42. def int_to_start_len_stride(i: int, max_len: int) -> StartLenStride:
  43. """
  44. Convert an integer index, possibly negative, into positive integers for start, length, and stride.
  45.  
  46. Raises NullSlicingException if there would be no results returned from this indexing
  47.  
  48. :param i: integer index
  49. :param max_len: maximum length of the dimension
  50. :return: tuple of positive integer start, length, stride
  51. """
  52. if -max_len < i < 0:
  53. begin = i + max_len
  54. elif i >= max_len or i < -max_len:
  55. raise IndexError("index {} is out of bounds for axis with size".format(i, max_len))
  56. else:
  57. begin = i
  58.  
  59. return StartLenStride(begin, 1, 1)
  60.  
  61.  
  62. class Roi(NamedTuple):
  63. """ROI described by positive integers.
  64. """
  65. start: Tuple[int, ...]
  66. read_shape: Tuple[int, ...]
  67. stride: Tuple[int, ...]
  68. out_shape: Tuple[int, ...]
  69.  
  70.  
  71. ConcreteSliceLike = Union[slice, int]
  72. SliceLike = Union[ConcreteSliceLike, type(Ellipsis), np.newaxis]
  73. SliceArgs = TypeVar("SliceArgs", SliceLike, Tuple[SliceLike])
  74.  
  75.  
  76. class Indexer:
  77. type_msg = (
  78. "Advanced selection inappropriate. "
  79. "Only numbers, slices (`:`), ellipsis (`...`), and np.newaxis (None) "
  80. "are valid indices (or tuples thereof); got {}"
  81. )
  82. non_indexes = (None, Ellipsis)
  83.  
  84. def __init__(self, max_shape: Tuple[int, ...]):
  85. """Class for normalising some of numpy's indexing scheme.
  86.  
  87. :param max_shape: shape of the array to be read
  88. """
  89. self.max_shape = max_shape
  90. self.ndim = len(max_shape)
  91.  
  92. def _handle_newaxis_ellipses(self, index_tup: Tuple[SliceLike]) -> Tuple[List[ConcreteSliceLike], List[int]]:
  93. """
  94.  
  95. :param index_tup:
  96. :return: list of ints and slices the same length as the dataset's shape,
  97. and list of indices at which to insert newaxes (must be in inserted in order)
  98. """
  99. concrete_indices = sum(idx not in self.non_indexes for idx in index_tup)
  100. index_lst = []
  101. newaxis_at = []
  102. has_ellipsis = False
  103. int_count = 0
  104. for item in index_tup:
  105. if isinstance(item, numbers.Number):
  106. int_count += 1
  107.  
  108. if item is None:
  109. newaxis_at.append(len(index_lst) + len(newaxis_at) - int_count)
  110. elif item == Ellipsis:
  111. if has_ellipsis:
  112. raise IndexError("an index can only have a single ellipsis ('...')")
  113. has_ellipsis = True
  114. initial_len = len(index_lst)
  115. while len(index_lst) + (concrete_indices - initial_len) < self.ndim:
  116. index_lst.append(slice(None))
  117. else:
  118. index_lst.append(item)
  119.  
  120. if len(index_lst) > self.ndim:
  121. raise IndexError("too many indices for array")
  122. while len(index_lst) < self.ndim:
  123. index_lst.append(slice(None))
  124.  
  125. return index_lst, newaxis_at
  126.  
  127. def __getitem__(self, args: SliceArgs):
  128. index_tup = np.index_exp[args]
  129. index_lst, newaxis_at = self._handle_newaxis_ellipses(index_tup)
  130.  
  131. start_readshape_stride = []
  132. out_shape = []
  133.  
  134. for item in index_lst:
  135. d = len(start_readshape_stride)
  136. max_len = self.max_shape[d]
  137.  
  138. if isinstance(item, slice):
  139.  
  140. try:
  141. bls = slice_to_start_len_stride(item, max_len)
  142. start_readshape_stride.append(bls)
  143. out_shape.append(bls[1])
  144. except NullSlicingException:
  145. start_readshape_stride.append((None, None, None))
  146. out_shape.append(0)
  147.  
  148. elif isinstance(item, (int, np.integer)):
  149.  
  150. start_readshape_stride.append(
  151. int_to_start_len_stride(int(item), max_len)
  152. )
  153.  
  154. else:
  155. raise TypeError(self.type_msg.format(type(item)))
  156.  
  157. for newax_idx in newaxis_at:
  158. out_shape.insert(newax_idx, 1)
  159.  
  160. out_shape = tuple(out_shape)
  161. if 0 in out_shape:
  162. raise NullSlicingException("Slicing has a 0-length dimension", out_shape)
  163.  
  164. start, read_shape, stride = zip(*start_readshape_stride)
  165. return Roi(start, read_shape, stride, out_shape)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement