_methods.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. """
  2. Array methods which are called by both the C-code for the method
  3. and the Python code for the NumPy-namespace function
  4. """
  5. import warnings
  6. from numpy.core import multiarray as mu
  7. from numpy.core import umath as um
  8. from numpy.core._asarray import asanyarray
  9. from numpy.core import numerictypes as nt
  10. from numpy.core import _exceptions
  11. from numpy._globals import _NoValue
  12. from numpy.compat import pickle, os_fspath, contextlib_nullcontext
  13. # save those O(100) nanoseconds!
  14. umr_maximum = um.maximum.reduce
  15. umr_minimum = um.minimum.reduce
  16. umr_sum = um.add.reduce
  17. umr_prod = um.multiply.reduce
  18. umr_any = um.logical_or.reduce
  19. umr_all = um.logical_and.reduce
  20. # Complex types to -> (2,)float view for fast-path computation in _var()
  21. _complex_to_float = {
  22. nt.dtype(nt.csingle) : nt.dtype(nt.single),
  23. nt.dtype(nt.cdouble) : nt.dtype(nt.double),
  24. }
  25. # Special case for windows: ensure double takes precedence
  26. if nt.dtype(nt.longdouble) != nt.dtype(nt.double):
  27. _complex_to_float.update({
  28. nt.dtype(nt.clongdouble) : nt.dtype(nt.longdouble),
  29. })
  30. # avoid keyword arguments to speed up parsing, saves about 15%-20% for very
  31. # small reductions
  32. def _amax(a, axis=None, out=None, keepdims=False,
  33. initial=_NoValue, where=True):
  34. return umr_maximum(a, axis, None, out, keepdims, initial, where)
  35. def _amin(a, axis=None, out=None, keepdims=False,
  36. initial=_NoValue, where=True):
  37. return umr_minimum(a, axis, None, out, keepdims, initial, where)
  38. def _sum(a, axis=None, dtype=None, out=None, keepdims=False,
  39. initial=_NoValue, where=True):
  40. return umr_sum(a, axis, dtype, out, keepdims, initial, where)
  41. def _prod(a, axis=None, dtype=None, out=None, keepdims=False,
  42. initial=_NoValue, where=True):
  43. return umr_prod(a, axis, dtype, out, keepdims, initial, where)
  44. def _any(a, axis=None, dtype=None, out=None, keepdims=False):
  45. return umr_any(a, axis, dtype, out, keepdims)
  46. def _all(a, axis=None, dtype=None, out=None, keepdims=False):
  47. return umr_all(a, axis, dtype, out, keepdims)
  48. def _count_reduce_items(arr, axis):
  49. if axis is None:
  50. axis = tuple(range(arr.ndim))
  51. if not isinstance(axis, tuple):
  52. axis = (axis,)
  53. items = 1
  54. for ax in axis:
  55. items *= arr.shape[mu.normalize_axis_index(ax, arr.ndim)]
  56. return items
  57. # Numpy 1.17.0, 2019-02-24
  58. # Various clip behavior deprecations, marked with _clip_dep as a prefix.
  59. def _clip_dep_is_scalar_nan(a):
  60. # guarded to protect circular imports
  61. from numpy.core.fromnumeric import ndim
  62. if ndim(a) != 0:
  63. return False
  64. try:
  65. return um.isnan(a)
  66. except TypeError:
  67. return False
  68. def _clip_dep_is_byte_swapped(a):
  69. if isinstance(a, mu.ndarray):
  70. return not a.dtype.isnative
  71. return False
  72. def _clip_dep_invoke_with_casting(ufunc, *args, out=None, casting=None, **kwargs):
  73. # normal path
  74. if casting is not None:
  75. return ufunc(*args, out=out, casting=casting, **kwargs)
  76. # try to deal with broken casting rules
  77. try:
  78. return ufunc(*args, out=out, **kwargs)
  79. except _exceptions._UFuncOutputCastingError as e:
  80. # Numpy 1.17.0, 2019-02-24
  81. warnings.warn(
  82. "Converting the output of clip from {!r} to {!r} is deprecated. "
  83. "Pass `casting=\"unsafe\"` explicitly to silence this warning, or "
  84. "correct the type of the variables.".format(e.from_, e.to),
  85. DeprecationWarning,
  86. stacklevel=2
  87. )
  88. return ufunc(*args, out=out, casting="unsafe", **kwargs)
  89. def _clip(a, min=None, max=None, out=None, *, casting=None, **kwargs):
  90. if min is None and max is None:
  91. raise ValueError("One of max or min must be given")
  92. # Numpy 1.17.0, 2019-02-24
  93. # This deprecation probably incurs a substantial slowdown for small arrays,
  94. # it will be good to get rid of it.
  95. if not _clip_dep_is_byte_swapped(a) and not _clip_dep_is_byte_swapped(out):
  96. using_deprecated_nan = False
  97. if _clip_dep_is_scalar_nan(min):
  98. min = -float('inf')
  99. using_deprecated_nan = True
  100. if _clip_dep_is_scalar_nan(max):
  101. max = float('inf')
  102. using_deprecated_nan = True
  103. if using_deprecated_nan:
  104. warnings.warn(
  105. "Passing `np.nan` to mean no clipping in np.clip has always "
  106. "been unreliable, and is now deprecated. "
  107. "In future, this will always return nan, like it already does "
  108. "when min or max are arrays that contain nan. "
  109. "To skip a bound, pass either None or an np.inf of an "
  110. "appropriate sign.",
  111. DeprecationWarning,
  112. stacklevel=2
  113. )
  114. if min is None:
  115. return _clip_dep_invoke_with_casting(
  116. um.minimum, a, max, out=out, casting=casting, **kwargs)
  117. elif max is None:
  118. return _clip_dep_invoke_with_casting(
  119. um.maximum, a, min, out=out, casting=casting, **kwargs)
  120. else:
  121. return _clip_dep_invoke_with_casting(
  122. um.clip, a, min, max, out=out, casting=casting, **kwargs)
  123. def _mean(a, axis=None, dtype=None, out=None, keepdims=False):
  124. arr = asanyarray(a)
  125. is_float16_result = False
  126. rcount = _count_reduce_items(arr, axis)
  127. # Make this warning show up first
  128. if rcount == 0:
  129. warnings.warn("Mean of empty slice.", RuntimeWarning, stacklevel=2)
  130. # Cast bool, unsigned int, and int to float64 by default
  131. if dtype is None:
  132. if issubclass(arr.dtype.type, (nt.integer, nt.bool_)):
  133. dtype = mu.dtype('f8')
  134. elif issubclass(arr.dtype.type, nt.float16):
  135. dtype = mu.dtype('f4')
  136. is_float16_result = True
  137. ret = umr_sum(arr, axis, dtype, out, keepdims)
  138. if isinstance(ret, mu.ndarray):
  139. ret = um.true_divide(
  140. ret, rcount, out=ret, casting='unsafe', subok=False)
  141. if is_float16_result and out is None:
  142. ret = arr.dtype.type(ret)
  143. elif hasattr(ret, 'dtype'):
  144. if is_float16_result:
  145. ret = arr.dtype.type(ret / rcount)
  146. else:
  147. ret = ret.dtype.type(ret / rcount)
  148. else:
  149. ret = ret / rcount
  150. return ret
  151. def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False):
  152. arr = asanyarray(a)
  153. rcount = _count_reduce_items(arr, axis)
  154. # Make this warning show up on top.
  155. if ddof >= rcount:
  156. warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning,
  157. stacklevel=2)
  158. # Cast bool, unsigned int, and int to float64 by default
  159. if dtype is None and issubclass(arr.dtype.type, (nt.integer, nt.bool_)):
  160. dtype = mu.dtype('f8')
  161. # Compute the mean.
  162. # Note that if dtype is not of inexact type then arraymean will
  163. # not be either.
  164. arrmean = umr_sum(arr, axis, dtype, keepdims=True)
  165. if isinstance(arrmean, mu.ndarray):
  166. arrmean = um.true_divide(
  167. arrmean, rcount, out=arrmean, casting='unsafe', subok=False)
  168. else:
  169. arrmean = arrmean.dtype.type(arrmean / rcount)
  170. # Compute sum of squared deviations from mean
  171. # Note that x may not be inexact and that we need it to be an array,
  172. # not a scalar.
  173. x = asanyarray(arr - arrmean)
  174. if issubclass(arr.dtype.type, (nt.floating, nt.integer)):
  175. x = um.multiply(x, x, out=x)
  176. # Fast-paths for built-in complex types
  177. elif x.dtype in _complex_to_float:
  178. xv = x.view(dtype=(_complex_to_float[x.dtype], (2,)))
  179. um.multiply(xv, xv, out=xv)
  180. x = um.add(xv[..., 0], xv[..., 1], out=x.real).real
  181. # Most general case; includes handling object arrays containing imaginary
  182. # numbers and complex types with non-native byteorder
  183. else:
  184. x = um.multiply(x, um.conjugate(x), out=x).real
  185. ret = umr_sum(x, axis, dtype, out, keepdims)
  186. # Compute degrees of freedom and make sure it is not negative.
  187. rcount = max([rcount - ddof, 0])
  188. # divide by degrees of freedom
  189. if isinstance(ret, mu.ndarray):
  190. ret = um.true_divide(
  191. ret, rcount, out=ret, casting='unsafe', subok=False)
  192. elif hasattr(ret, 'dtype'):
  193. ret = ret.dtype.type(ret / rcount)
  194. else:
  195. ret = ret / rcount
  196. return ret
  197. def _std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False):
  198. ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,
  199. keepdims=keepdims)
  200. if isinstance(ret, mu.ndarray):
  201. ret = um.sqrt(ret, out=ret)
  202. elif hasattr(ret, 'dtype'):
  203. ret = ret.dtype.type(um.sqrt(ret))
  204. else:
  205. ret = um.sqrt(ret)
  206. return ret
  207. def _ptp(a, axis=None, out=None, keepdims=False):
  208. return um.subtract(
  209. umr_maximum(a, axis, None, out, keepdims),
  210. umr_minimum(a, axis, None, None, keepdims),
  211. out
  212. )
  213. def _dump(self, file, protocol=2):
  214. if hasattr(file, 'write'):
  215. ctx = contextlib_nullcontext(file)
  216. else:
  217. ctx = open(os_fspath(file), "wb")
  218. with ctx as f:
  219. pickle.dump(self, f, protocol=protocol)
  220. def _dumps(self, protocol=2):
  221. return pickle.dumps(self, protocol=protocol)