1import builtins
2import collections.abc
3import functools
4import re
5import warnings
6
7import numpy as np
8import numpy._core.numeric as _nx
9from numpy._core import overrides, transpose
10from numpy._core._multiarray_umath import _array_converter
11from numpy._core.fromnumeric import any, mean, nonzero, partition, ravel, sum
12from numpy._core.multiarray import (
13 _monotonicity,
14 _place,
15 bincount,
16 interp as compiled_interp,
17 interp_complex as compiled_interp_complex,
18 normalize_axis_index,
19)
20from numpy._core.numeric import (
21 absolute,
22 arange,
23 array,
24 asanyarray,
25 asarray,
26 concatenate,
27 dot,
28 empty,
29 integer,
30 intp,
31 isscalar,
32 ndarray,
33 ones,
34 take,
35 where,
36 zeros_like,
37)
38from numpy._core.numerictypes import typecodes
39from numpy._core.umath import (
40 add,
41 arctan2,
42 cos,
43 exp,
44 floor,
45 frompyfunc,
46 less_equal,
47 minimum,
48 mod,
49 not_equal,
50 pi,
51 sin,
52 sqrt,
53 subtract,
54)
55from numpy._utils import set_module
56
57# needed in this module for compatibility
58from numpy.lib._histograms_impl import histogram, histogramdd # noqa: F401
59from numpy.lib._twodim_base_impl import diag
60
61array_function_dispatch = functools.partial(
62 overrides.array_function_dispatch, module='numpy')
63
64
65__all__ = [
66 'select', 'piecewise', 'trim_zeros', 'copy', 'iterable', 'percentile',
67 'diff', 'gradient', 'angle', 'unwrap', 'sort_complex', 'flip',
68 'rot90', 'extract', 'place', 'vectorize', 'asarray_chkfinite', 'average',
69 'bincount', 'digitize', 'cov', 'corrcoef',
70 'median', 'sinc', 'hamming', 'hanning', 'bartlett',
71 'blackman', 'kaiser', 'trapezoid', 'i0',
72 'meshgrid', 'delete', 'insert', 'append', 'interp',
73 'quantile'
74 ]
75
76# _QuantileMethods is a dictionary listing all the supported methods to
77# compute quantile/percentile.
78#
79# Below virtual_index refers to the index of the element where the percentile
80# would be found in the sorted sample.
81# When the sample contains exactly the percentile wanted, the virtual_index is
82# an integer to the index of this element.
83# When the percentile wanted is in between two elements, the virtual_index
84# is made of a integer part (a.k.a 'i' or 'left') and a fractional part
85# (a.k.a 'g' or 'gamma')
86#
87# Each method in _QuantileMethods has two properties
88# get_virtual_index : Callable
89# The function used to compute the virtual_index.
90# fix_gamma : Callable
91# A function used for discrete methods to force the index to a specific value.
92_QuantileMethods = {
93 # --- HYNDMAN and FAN METHODS
94 # Discrete methods
95 'inverted_cdf': {
96 'get_virtual_index': lambda n, quantiles: _inverted_cdf(n, quantiles), # noqa: PLW0108
97 'fix_gamma': None, # should never be called
98 },
99 'averaged_inverted_cdf': {
100 'get_virtual_index': lambda n, quantiles: (n * quantiles) - 1,
101 'fix_gamma': lambda gamma, _: _get_gamma_mask(
102 shape=gamma.shape,
103 default_value=1.,
104 conditioned_value=0.5,
105 where=gamma == 0),
106 },
107 'closest_observation': {
108 'get_virtual_index': lambda n, quantiles: _closest_observation(n, quantiles), # noqa: PLW0108
109 'fix_gamma': None, # should never be called
110 },
111 # Continuous methods
112 'interpolated_inverted_cdf': {
113 'get_virtual_index': lambda n, quantiles:
114 _compute_virtual_index(n, quantiles, 0, 1),
115 'fix_gamma': lambda gamma, _: gamma,
116 },
117 'hazen': {
118 'get_virtual_index': lambda n, quantiles:
119 _compute_virtual_index(n, quantiles, 0.5, 0.5),
120 'fix_gamma': lambda gamma, _: gamma,
121 },
122 'weibull': {
123 'get_virtual_index': lambda n, quantiles:
124 _compute_virtual_index(n, quantiles, 0, 0),
125 'fix_gamma': lambda gamma, _: gamma,
126 },
127 # Default method.
128 # To avoid some rounding issues, `(n-1) * quantiles` is preferred to
129 # `_compute_virtual_index(n, quantiles, 1, 1)`.
130 # They are mathematically equivalent.
131 'linear': {
132 'get_virtual_index': lambda n, quantiles: (n - 1) * quantiles,
133 'fix_gamma': lambda gamma, _: gamma,
134 },
135 'median_unbiased': {
136 'get_virtual_index': lambda n, quantiles:
137 _compute_virtual_index(n, quantiles, 1 / 3.0, 1 / 3.0),
138 'fix_gamma': lambda gamma, _: gamma,
139 },
140 'normal_unbiased': {
141 'get_virtual_index': lambda n, quantiles:
142 _compute_virtual_index(n, quantiles, 3 / 8.0, 3 / 8.0),
143 'fix_gamma': lambda gamma, _: gamma,
144 },
145 # --- OTHER METHODS
146 'lower': {
147 'get_virtual_index': lambda n, quantiles: np.floor(
148 (n - 1) * quantiles).astype(np.intp),
149 'fix_gamma': None, # should never be called, index dtype is int
150 },
151 'higher': {
152 'get_virtual_index': lambda n, quantiles: np.ceil(
153 (n - 1) * quantiles).astype(np.intp),
154 'fix_gamma': None, # should never be called, index dtype is int
155 },
156 'midpoint': {
157 'get_virtual_index': lambda n, quantiles: 0.5 * (
158 np.floor((n - 1) * quantiles)
159 + np.ceil((n - 1) * quantiles)),
160 'fix_gamma': lambda gamma, index: _get_gamma_mask(
161 shape=gamma.shape,
162 default_value=0.5,
163 conditioned_value=0.,
164 where=index % 1 == 0),
165 },
166 'nearest': {
167 'get_virtual_index': lambda n, quantiles: np.around(
168 (n - 1) * quantiles).astype(np.intp),
169 'fix_gamma': None,
170 # should never be called, index dtype is int
171 }}
172
173
174def _rot90_dispatcher(m, k=None, axes=None):
175 return (m,)
176
177
178@array_function_dispatch(_rot90_dispatcher)
179def rot90(m, k=1, axes=(0, 1)):
180 """
181 Rotate an array by 90 degrees in the plane specified by axes.
182
183 Rotation direction is from the first towards the second axis.
184 This means for a 2D array with the default `k` and `axes`, the
185 rotation will be counterclockwise.
186
187 Parameters
188 ----------
189 m : array_like
190 Array of two or more dimensions.
191 k : integer
192 Number of times the array is rotated by 90 degrees.
193 axes : (2,) array_like
194 The array is rotated in the plane defined by the axes.
195 Axes must be different.
196
197 Returns
198 -------
199 y : ndarray
200 A rotated view of `m`.
201
202 See Also
203 --------
204 flip : Reverse the order of elements in an array along the given axis.
205 fliplr : Flip an array horizontally.
206 flipud : Flip an array vertically.
207
208 Notes
209 -----
210 ``rot90(m, k=1, axes=(1,0))`` is the reverse of
211 ``rot90(m, k=1, axes=(0,1))``
212
213 ``rot90(m, k=1, axes=(1,0))`` is equivalent to
214 ``rot90(m, k=-1, axes=(0,1))``
215
216 Examples
217 --------
218 >>> import numpy as np
219 >>> m = np.array([[1,2],[3,4]], int)
220 >>> m
221 array([[1, 2],
222 [3, 4]])
223 >>> np.rot90(m)
224 array([[2, 4],
225 [1, 3]])
226 >>> np.rot90(m, 2)
227 array([[4, 3],
228 [2, 1]])
229 >>> m = np.arange(8).reshape((2,2,2))
230 >>> np.rot90(m, 1, (1,2))
231 array([[[1, 3],
232 [0, 2]],
233 [[5, 7],
234 [4, 6]]])
235
236 """
237 axes = tuple(axes)
238 if len(axes) != 2:
239 raise ValueError("len(axes) must be 2.")
240
241 m = asanyarray(m)
242
243 if axes[0] == axes[1] or absolute(axes[0] - axes[1]) == m.ndim:
244 raise ValueError("Axes must be different.")
245
246 if (axes[0] >= m.ndim or axes[0] < -m.ndim
247 or axes[1] >= m.ndim or axes[1] < -m.ndim):
248 raise ValueError(f"Axes={axes} out of range for array of ndim={m.ndim}.")
249
250 k %= 4
251
252 if k == 0:
253 return m[:]
254 if k == 2:
255 return flip(flip(m, axes[0]), axes[1])
256
257 axes_list = arange(0, m.ndim)
258 (axes_list[axes[0]], axes_list[axes[1]]) = (axes_list[axes[1]],
259 axes_list[axes[0]])
260
261 if k == 1:
262 return transpose(flip(m, axes[1]), axes_list)
263 else:
264 # k == 3
265 return flip(transpose(m, axes_list), axes[1])
266
267
268def _flip_dispatcher(m, axis=None):
269 return (m,)
270
271
272@array_function_dispatch(_flip_dispatcher)
273def flip(m, axis=None):
274 """
275 Reverse the order of elements in an array along the given axis.
276
277 The shape of the array is preserved, but the elements are reordered.
278
279 Parameters
280 ----------
281 m : array_like
282 Input array.
283 axis : None or int or tuple of ints, optional
284 Axis or axes along which to flip over. The default,
285 axis=None, will flip over all of the axes of the input array.
286 If axis is negative it counts from the last to the first axis.
287
288 If axis is a tuple of ints, flipping is performed on all of the axes
289 specified in the tuple.
290
291 Returns
292 -------
293 out : array_like
294 A view of `m` with the entries of axis reversed. Since a view is
295 returned, this operation is done in constant time.
296
297 See Also
298 --------
299 flipud : Flip an array vertically (axis=0).
300 fliplr : Flip an array horizontally (axis=1).
301
302 Notes
303 -----
304 flip(m, 0) is equivalent to flipud(m).
305
306 flip(m, 1) is equivalent to fliplr(m).
307
308 flip(m, n) corresponds to ``m[...,::-1,...]`` with ``::-1`` at position n.
309
310 flip(m) corresponds to ``m[::-1,::-1,...,::-1]`` with ``::-1`` at all
311 positions.
312
313 flip(m, (0, 1)) corresponds to ``m[::-1,::-1,...]`` with ``::-1`` at
314 position 0 and position 1.
315
316 Examples
317 --------
318 >>> import numpy as np
319 >>> A = np.arange(8).reshape((2,2,2))
320 >>> A
321 array([[[0, 1],
322 [2, 3]],
323 [[4, 5],
324 [6, 7]]])
325 >>> np.flip(A, 0)
326 array([[[4, 5],
327 [6, 7]],
328 [[0, 1],
329 [2, 3]]])
330 >>> np.flip(A, 1)
331 array([[[2, 3],
332 [0, 1]],
333 [[6, 7],
334 [4, 5]]])
335 >>> np.flip(A)
336 array([[[7, 6],
337 [5, 4]],
338 [[3, 2],
339 [1, 0]]])
340 >>> np.flip(A, (0, 2))
341 array([[[5, 4],
342 [7, 6]],
343 [[1, 0],
344 [3, 2]]])
345 >>> rng = np.random.default_rng()
346 >>> A = rng.normal(size=(3,4,5))
347 >>> np.all(np.flip(A,2) == A[:,:,::-1,...])
348 True
349 """
350 if not hasattr(m, 'ndim'):
351 m = asarray(m)
352 if axis is None:
353 indexer = (np.s_[::-1],) * m.ndim
354 else:
355 axis = _nx.normalize_axis_tuple(axis, m.ndim)
356 indexer = [np.s_[:]] * m.ndim
357 for ax in axis:
358 indexer[ax] = np.s_[::-1]
359 indexer = tuple(indexer)
360 return m[indexer]
361
362
363@set_module('numpy')
364def iterable(y):
365 """
366 Check whether or not an object can be iterated over.
367
368 Parameters
369 ----------
370 y : object
371 Input object.
372
373 Returns
374 -------
375 b : bool
376 Return ``True`` if the object has an iterator method or is a
377 sequence and ``False`` otherwise.
378
379
380 Examples
381 --------
382 >>> import numpy as np
383 >>> np.iterable([1, 2, 3])
384 True
385 >>> np.iterable(2)
386 False
387
388 Notes
389 -----
390 In most cases, the results of ``np.iterable(obj)`` are consistent with
391 ``isinstance(obj, collections.abc.Iterable)``. One notable exception is
392 the treatment of 0-dimensional arrays::
393
394 >>> from collections.abc import Iterable
395 >>> a = np.array(1.0) # 0-dimensional numpy array
396 >>> isinstance(a, Iterable)
397 True
398 >>> np.iterable(a)
399 False
400
401 """
402 try:
403 iter(y)
404 except TypeError:
405 return False
406 return True
407
408
409def _weights_are_valid(weights, a, axis):
410 """Validate weights array.
411
412 We assume, weights is not None.
413 """
414 wgt = np.asanyarray(weights)
415
416 # Sanity checks
417 if a.shape != wgt.shape:
418 if axis is None:
419 raise TypeError(
420 "Axis must be specified when shapes of a and weights "
421 "differ.")
422 if wgt.shape != tuple(a.shape[ax] for ax in axis):
423 raise ValueError(
424 "Shape of weights must be consistent with "
425 "shape of a along specified axis.")
426
427 # setup wgt to broadcast along axis
428 wgt = wgt.transpose(np.argsort(axis))
429 wgt = wgt.reshape(tuple((s if ax in axis else 1)
430 for ax, s in enumerate(a.shape)))
431 return wgt
432
433
434def _average_dispatcher(a, axis=None, weights=None, returned=None, *,
435 keepdims=None):
436 return (a, weights)
437
438
439@array_function_dispatch(_average_dispatcher)
440def average(a, axis=None, weights=None, returned=False, *,
441 keepdims=np._NoValue):
442 """
443 Compute the weighted average along the specified axis.
444
445 Parameters
446 ----------
447 a : array_like
448 Array containing data to be averaged. If `a` is not an array, a
449 conversion is attempted.
450 axis : None or int or tuple of ints, optional
451 Axis or axes along which to average `a`. The default,
452 `axis=None`, will average over all of the elements of the input array.
453 If axis is negative it counts from the last to the first axis.
454 If axis is a tuple of ints, averaging is performed on all of the axes
455 specified in the tuple instead of a single axis or all the axes as
456 before.
457 weights : array_like, optional
458 An array of weights associated with the values in `a`. Each value in
459 `a` contributes to the average according to its associated weight.
460 The array of weights must be the same shape as `a` if no axis is
461 specified, otherwise the weights must have dimensions and shape
462 consistent with `a` along the specified axis.
463 If `weights=None`, then all data in `a` are assumed to have a
464 weight equal to one.
465 The calculation is::
466
467 avg = sum(a * weights) / sum(weights)
468
469 where the sum is over all included elements.
470 The only constraint on the values of `weights` is that `sum(weights)`
471 must not be 0.
472 returned : bool, optional
473 Default is `False`. If `True`, the tuple (`average`, `sum_of_weights`)
474 is returned, otherwise only the average is returned.
475 If `weights=None`, `sum_of_weights` is equivalent to the number of
476 elements over which the average is taken.
477 keepdims : bool, optional
478 If this is set to True, the axes which are reduced are left
479 in the result as dimensions with size one. With this option,
480 the result will broadcast correctly against the original `a`.
481 *Note:* `keepdims` will not work with instances of `numpy.matrix`
482 or other classes whose methods do not support `keepdims`.
483
484 .. versionadded:: 1.23.0
485
486 Returns
487 -------
488 retval, [sum_of_weights] : array_type or double
489 Return the average along the specified axis. When `returned` is `True`,
490 return a tuple with the average as the first element and the sum
491 of the weights as the second element. `sum_of_weights` is of the
492 same type as `retval`. The result dtype follows a general pattern.
493 If `weights` is None, the result dtype will be that of `a` , or ``float64``
494 if `a` is integral. Otherwise, if `weights` is not None and `a` is non-
495 integral, the result type will be the type of lowest precision capable of
496 representing values of both `a` and `weights`. If `a` happens to be
497 integral, the previous rules still applies but the result dtype will
498 at least be ``float64``.
499
500 Raises
501 ------
502 ZeroDivisionError
503 When all weights along axis are zero. See `numpy.ma.average` for a
504 version robust to this type of error.
505 TypeError
506 When `weights` does not have the same shape as `a`, and `axis=None`.
507 ValueError
508 When `weights` does not have dimensions and shape consistent with `a`
509 along specified `axis`.
510
511 See Also
512 --------
513 mean
514
515 ma.average : average for masked arrays -- useful if your data contains
516 "missing" values
517 numpy.result_type : Returns the type that results from applying the
518 numpy type promotion rules to the arguments.
519
520 Examples
521 --------
522 >>> import numpy as np
523 >>> data = np.arange(1, 5)
524 >>> data
525 array([1, 2, 3, 4])
526 >>> np.average(data)
527 2.5
528 >>> np.average(np.arange(1, 11), weights=np.arange(10, 0, -1))
529 4.0
530
531 >>> data = np.arange(6).reshape((3, 2))
532 >>> data
533 array([[0, 1],
534 [2, 3],
535 [4, 5]])
536 >>> np.average(data, axis=1, weights=[1./4, 3./4])
537 array([0.75, 2.75, 4.75])
538 >>> np.average(data, weights=[1./4, 3./4])
539 Traceback (most recent call last):
540 ...
541 TypeError: Axis must be specified when shapes of a and weights differ.
542
543 With ``keepdims=True``, the following result has shape (3, 1).
544
545 >>> np.average(data, axis=1, keepdims=True)
546 array([[0.5],
547 [2.5],
548 [4.5]])
549
550 >>> data = np.arange(8).reshape((2, 2, 2))
551 >>> data
552 array([[[0, 1],
553 [2, 3]],
554 [[4, 5],
555 [6, 7]]])
556 >>> np.average(data, axis=(0, 1), weights=[[1./4, 3./4], [1., 1./2]])
557 array([3.4, 4.4])
558 >>> np.average(data, axis=0, weights=[[1./4, 3./4], [1., 1./2]])
559 Traceback (most recent call last):
560 ...
561 ValueError: Shape of weights must be consistent
562 with shape of a along specified axis.
563 """
564 a = np.asanyarray(a)
565
566 if axis is not None:
567 axis = _nx.normalize_axis_tuple(axis, a.ndim, argname="axis")
568
569 if keepdims is np._NoValue:
570 # Don't pass on the keepdims argument if one wasn't given.
571 keepdims_kw = {}
572 else:
573 keepdims_kw = {'keepdims': keepdims}
574
575 if weights is None:
576 avg = a.mean(axis, **keepdims_kw)
577 avg_as_array = np.asanyarray(avg)
578 scl = avg_as_array.dtype.type(a.size / avg_as_array.size)
579 else:
580 wgt = _weights_are_valid(weights=weights, a=a, axis=axis)
581
582 if issubclass(a.dtype.type, (np.integer, np.bool)):
583 result_dtype = np.result_type(a.dtype, wgt.dtype, 'f8')
584 else:
585 result_dtype = np.result_type(a.dtype, wgt.dtype)
586
587 scl = wgt.sum(axis=axis, dtype=result_dtype, **keepdims_kw)
588 if np.any(scl == 0.0):
589 raise ZeroDivisionError(
590 "Weights sum to zero, can't be normalized")
591
592 avg = avg_as_array = np.multiply(a, wgt,
593 dtype=result_dtype).sum(axis, **keepdims_kw) / scl
594
595 if returned:
596 if scl.shape != avg_as_array.shape:
597 scl = np.broadcast_to(scl, avg_as_array.shape, subok=True).copy()
598 return avg, scl
599 else:
600 return avg
601
602
603@set_module('numpy')
604def asarray_chkfinite(a, dtype=None, order=None):
605 """Convert the input to an array, checking for NaNs or Infs.
606
607 Parameters
608 ----------
609 a : array_like
610 Input data, in any form that can be converted to an array. This
611 includes lists, lists of tuples, tuples, tuples of tuples, tuples
612 of lists and ndarrays. Success requires no NaNs or Infs.
613 dtype : data-type, optional
614 By default, the data-type is inferred from the input data.
615 order : {'C', 'F', 'A', 'K'}, optional
616 The memory layout of the output.
617 'C' gives a row-major layout (C-style),
618 'F' gives a column-major layout (Fortran-style).
619 'C' and 'F' will copy if needed to ensure the output format.
620 'A' (any) is equivalent to 'F' if input a is non-contiguous or
621 Fortran-contiguous, otherwise, it is equivalent to 'C'.
622 Unlike 'C' or 'F', 'A' does not ensure that the result is contiguous.
623 'K' (keep) preserves the input order for the output.
624 'C' is the default.
625
626 Returns
627 -------
628 out : ndarray
629 Array interpretation of `a`. No copy is performed if the input
630 is already an ndarray. If `a` is a subclass of ndarray, a base
631 class ndarray is returned.
632
633 Raises
634 ------
635 ValueError
636 Raises ValueError if `a` contains NaN (Not a Number) or Inf (Infinity).
637
638 See Also
639 --------
640 asarray : Create and array.
641 asanyarray : Similar function which passes through subclasses.
642 ascontiguousarray : Convert input to a contiguous array.
643 asfortranarray : Convert input to an ndarray with column-major
644 memory order.
645 fromiter : Create an array from an iterator.
646 fromfunction : Construct an array by executing a function on grid
647 positions.
648
649 Examples
650 --------
651 >>> import numpy as np
652
653 Convert a list into an array. If all elements are finite, then
654 ``asarray_chkfinite`` is identical to ``asarray``.
655
656 >>> a = [1, 2]
657 >>> np.asarray_chkfinite(a, dtype=float)
658 array([1., 2.])
659
660 Raises ValueError if array_like contains Nans or Infs.
661
662 >>> a = [1, 2, np.inf]
663 >>> try:
664 ... np.asarray_chkfinite(a)
665 ... except ValueError:
666 ... print('ValueError')
667 ...
668 ValueError
669
670 """
671 a = asarray(a, dtype=dtype, order=order)
672 if a.dtype.char in typecodes['AllFloat'] and not np.isfinite(a).all():
673 raise ValueError(
674 "array must not contain infs or NaNs")
675 return a
676
677
678def _piecewise_dispatcher(x, condlist, funclist, *args, **kw):
679 yield x
680 # support the undocumented behavior of allowing scalars
681 if np.iterable(condlist):
682 yield from condlist
683
684
685@array_function_dispatch(_piecewise_dispatcher)
686def piecewise(x, condlist, funclist, *args, **kw):
687 """
688 Evaluate a piecewise-defined function.
689
690 Given a set of conditions and corresponding functions, evaluate each
691 function on the input data wherever its condition is true.
692
693 Parameters
694 ----------
695 x : ndarray or scalar
696 The input domain.
697 condlist : list of bool arrays or bool scalars
698 Each boolean array corresponds to a function in `funclist`. Wherever
699 `condlist[i]` is True, `funclist[i](x)` is used as the output value.
700
701 Each boolean array in `condlist` selects a piece of `x`,
702 and should therefore be of the same shape as `x`.
703
704 The length of `condlist` must correspond to that of `funclist`.
705 If one extra function is given, i.e. if
706 ``len(funclist) == len(condlist) + 1``, then that extra function
707 is the default value, used wherever all conditions are false.
708 funclist : list of callables, f(x,*args,**kw), or scalars
709 Each function is evaluated over `x` wherever its corresponding
710 condition is True. It should take a 1d array as input and give a 1d
711 array or a scalar value as output. If, instead of a callable,
712 a scalar is provided then a constant function (``lambda x: scalar``) is
713 assumed.
714 args : tuple, optional
715 Any further arguments given to `piecewise` are passed to the functions
716 upon execution, i.e., if called ``piecewise(..., ..., 1, 'a')``, then
717 each function is called as ``f(x, 1, 'a')``.
718 kw : dict, optional
719 Keyword arguments used in calling `piecewise` are passed to the
720 functions upon execution, i.e., if called
721 ``piecewise(..., ..., alpha=1)``, then each function is called as
722 ``f(x, alpha=1)``.
723
724 Returns
725 -------
726 out : ndarray
727 The output is the same shape and type as x and is found by
728 calling the functions in `funclist` on the appropriate portions of `x`,
729 as defined by the boolean arrays in `condlist`. Portions not covered
730 by any condition have a default value of 0.
731
732
733 See Also
734 --------
735 choose, select, where
736
737 Notes
738 -----
739 This is similar to choose or select, except that functions are
740 evaluated on elements of `x` that satisfy the corresponding condition from
741 `condlist`.
742
743 The result is::
744
745 |--
746 |funclist[0](x[condlist[0]])
747 out = |funclist[1](x[condlist[1]])
748 |...
749 |funclist[n2](x[condlist[n2]])
750 |--
751
752 Examples
753 --------
754 >>> import numpy as np
755
756 Define the signum function, which is -1 for ``x < 0`` and +1 for ``x >= 0``.
757
758 >>> x = np.linspace(-2.5, 2.5, 6)
759 >>> np.piecewise(x, [x < 0, x >= 0], [-1, 1])
760 array([-1., -1., -1., 1., 1., 1.])
761
762 Define the absolute value, which is ``-x`` for ``x <0`` and ``x`` for
763 ``x >= 0``.
764
765 >>> np.piecewise(x, [x < 0, x >= 0], [lambda x: -x, lambda x: x])
766 array([2.5, 1.5, 0.5, 0.5, 1.5, 2.5])
767
768 Apply the same function to a scalar value.
769
770 >>> y = -2
771 >>> np.piecewise(y, [y < 0, y >= 0], [lambda x: -x, lambda x: x])
772 array(2)
773
774 """
775 x = asanyarray(x)
776 n2 = len(funclist)
777
778 # undocumented: single condition is promoted to a list of one condition
779 if isscalar(condlist) or (
780 not isinstance(condlist[0], (list, ndarray)) and x.ndim != 0):
781 condlist = [condlist]
782
783 condlist = asarray(condlist, dtype=bool)
784 n = len(condlist)
785
786 if n == n2 - 1: # compute the "otherwise" condition.
787 condelse = ~np.any(condlist, axis=0, keepdims=True)
788 condlist = np.concatenate([condlist, condelse], axis=0)
789 n += 1
790 elif n != n2:
791 raise ValueError(
792 f"with {n} condition(s), either {n} or {n + 1} functions are expected"
793 )
794
795 y = zeros_like(x)
796 for cond, func in zip(condlist, funclist):
797 if not isinstance(func, collections.abc.Callable):
798 y[cond] = func
799 else:
800 vals = x[cond]
801 if vals.size > 0:
802 y[cond] = func(vals, *args, **kw)
803
804 return y
805
806
807def _select_dispatcher(condlist, choicelist, default=None):
808 yield from condlist
809 yield from choicelist
810
811
812@array_function_dispatch(_select_dispatcher)
813def select(condlist, choicelist, default=0):
814 """
815 Return an array drawn from elements in choicelist, depending on conditions.
816
817 Parameters
818 ----------
819 condlist : list of bool ndarrays
820 The list of conditions which determine from which array in `choicelist`
821 the output elements are taken. When multiple conditions are satisfied,
822 the first one encountered in `condlist` is used.
823 choicelist : list of ndarrays
824 The list of arrays from which the output elements are taken. It has
825 to be of the same length as `condlist`.
826 default : array_like, optional
827 The element inserted in `output` when all conditions evaluate to False.
828
829 Returns
830 -------
831 output : ndarray
832 The output at position m is the m-th element of the array in
833 `choicelist` where the m-th element of the corresponding array in
834 `condlist` is True.
835
836 See Also
837 --------
838 where : Return elements from one of two arrays depending on condition.
839 take, choose, compress, diag, diagonal
840
841 Examples
842 --------
843 >>> import numpy as np
844
845 Beginning with an array of integers from 0 to 5 (inclusive),
846 elements less than ``3`` are negated, elements greater than ``3``
847 are squared, and elements not meeting either of these conditions
848 (exactly ``3``) are replaced with a `default` value of ``42``.
849
850 >>> x = np.arange(6)
851 >>> condlist = [x<3, x>3]
852 >>> choicelist = [-x, x**2]
853 >>> np.select(condlist, choicelist, 42)
854 array([ 0, -1, -2, 42, 16, 25])
855
856 When multiple conditions are satisfied, the first one encountered in
857 `condlist` is used.
858
859 >>> condlist = [x<=4, x>3]
860 >>> choicelist = [x, x**2]
861 >>> np.select(condlist, choicelist, 55)
862 array([ 0, 1, 2, 3, 4, 25])
863
864 """
865 # Check the size of condlist and choicelist are the same, or abort.
866 if len(condlist) != len(choicelist):
867 raise ValueError(
868 'list of cases must be same length as list of conditions')
869
870 # Now that the dtype is known, handle the deprecated select([], []) case
871 if len(condlist) == 0:
872 raise ValueError("select with an empty condition list is not possible")
873
874 # TODO: This preserves the Python int, float, complex manually to get the
875 # right `result_type` with NEP 50. Most likely we will grow a better
876 # way to spell this (and this can be replaced).
877 choicelist = [
878 choice if type(choice) in (int, float, complex) else np.asarray(choice)
879 for choice in choicelist]
880 choicelist.append(default if type(default) in (int, float, complex)
881 else np.asarray(default))
882
883 try:
884 dtype = np.result_type(*choicelist)
885 except TypeError as e:
886 msg = f'Choicelist and default value do not have a common dtype: {e}'
887 raise TypeError(msg) from None
888
889 # Convert conditions to arrays and broadcast conditions and choices
890 # as the shape is needed for the result. Doing it separately optimizes
891 # for example when all choices are scalars.
892 condlist = np.broadcast_arrays(*condlist)
893 choicelist = np.broadcast_arrays(*choicelist)
894
895 # If cond array is not an ndarray in boolean format or scalar bool, abort.
896 for i, cond in enumerate(condlist):
897 if cond.dtype.type is not np.bool:
898 raise TypeError(
899 f'invalid entry {i} in condlist: should be boolean ndarray')
900
901 if choicelist[0].ndim == 0:
902 # This may be common, so avoid the call.
903 result_shape = condlist[0].shape
904 else:
905 result_shape = np.broadcast_arrays(condlist[0], choicelist[0])[0].shape
906
907 result = np.full(result_shape, choicelist[-1], dtype)
908
909 # Use np.copyto to burn each choicelist array onto result, using the
910 # corresponding condlist as a boolean mask. This is done in reverse
911 # order since the first choice should take precedence.
912 choicelist = choicelist[-2::-1]
913 condlist = condlist[::-1]
914 for choice, cond in zip(choicelist, condlist):
915 np.copyto(result, choice, where=cond)
916
917 return result
918
919
920def _copy_dispatcher(a, order=None, subok=None):
921 return (a,)
922
923
924@array_function_dispatch(_copy_dispatcher)
925def copy(a, order='K', subok=False):
926 """
927 Return an array copy of the given object.
928
929 Parameters
930 ----------
931 a : array_like
932 Input data.
933 order : {'C', 'F', 'A', 'K'}, optional
934 Controls the memory layout of the copy. 'C' means C-order,
935 'F' means F-order, 'A' means 'F' if `a` is Fortran contiguous,
936 'C' otherwise. 'K' means match the layout of `a` as closely
937 as possible. (Note that this function and :meth:`ndarray.copy` are very
938 similar, but have different default values for their order=
939 arguments.)
940 subok : bool, optional
941 If True, then sub-classes will be passed-through, otherwise the
942 returned array will be forced to be a base-class array (defaults to False).
943
944 Returns
945 -------
946 arr : ndarray
947 Array interpretation of `a`.
948
949 See Also
950 --------
951 ndarray.copy : Preferred method for creating an array copy
952
953 Notes
954 -----
955 This is equivalent to:
956
957 >>> np.array(a, copy=True) #doctest: +SKIP
958
959 The copy made of the data is shallow, i.e., for arrays with object dtype,
960 the new array will point to the same objects.
961 See Examples from `ndarray.copy`.
962
963 Examples
964 --------
965 >>> import numpy as np
966
967 Create an array x, with a reference y and a copy z:
968
969 >>> x = np.array([1, 2, 3])
970 >>> y = x
971 >>> z = np.copy(x)
972
973 Note that, when we modify x, y changes, but not z:
974
975 >>> x[0] = 10
976 >>> x[0] == y[0]
977 True
978 >>> x[0] == z[0]
979 False
980
981 Note that, np.copy clears previously set WRITEABLE=False flag.
982
983 >>> a = np.array([1, 2, 3])
984 >>> a.flags["WRITEABLE"] = False
985 >>> b = np.copy(a)
986 >>> b.flags["WRITEABLE"]
987 True
988 >>> b[0] = 3
989 >>> b
990 array([3, 2, 3])
991 """
992 return array(a, order=order, subok=subok, copy=True)
993
994# Basic operations
995
996
997def _gradient_dispatcher(f, *varargs, axis=None, edge_order=None):
998 yield f
999 yield from varargs
1000
1001
1002@array_function_dispatch(_gradient_dispatcher)
1003def gradient(f, *varargs, axis=None, edge_order=1):
1004 """
1005 Return the gradient of an N-dimensional array.
1006
1007 The gradient is computed using second order accurate central differences
1008 in the interior points and either first or second order accurate one-sides
1009 (forward or backwards) differences at the boundaries.
1010 The returned gradient hence has the same shape as the input array.
1011
1012 Parameters
1013 ----------
1014 f : array_like
1015 An N-dimensional array containing samples of a scalar function.
1016 varargs : list of scalar or array, optional
1017 Spacing between f values. Default unitary spacing for all dimensions.
1018 Spacing can be specified using:
1019
1020 1. single scalar to specify a sample distance for all dimensions.
1021 2. N scalars to specify a constant sample distance for each dimension.
1022 i.e. `dx`, `dy`, `dz`, ...
1023 3. N arrays to specify the coordinates of the values along each
1024 dimension of F. The length of the array must match the size of
1025 the corresponding dimension
1026 4. Any combination of N scalars/arrays with the meaning of 2. and 3.
1027
1028 If `axis` is given, the number of varargs must equal the number of axes
1029 specified in the axis parameter.
1030 Default: 1. (see Examples below).
1031
1032 edge_order : {1, 2}, optional
1033 Gradient is calculated using N-th order accurate differences
1034 at the boundaries. Default: 1.
1035 axis : None or int or tuple of ints, optional
1036 Gradient is calculated only along the given axis or axes
1037 The default (axis = None) is to calculate the gradient for all the axes
1038 of the input array. axis may be negative, in which case it counts from
1039 the last to the first axis.
1040
1041 Returns
1042 -------
1043 gradient : ndarray or tuple of ndarray
1044 A tuple of ndarrays (or a single ndarray if there is only one
1045 dimension) corresponding to the derivatives of f with respect
1046 to each dimension. Each derivative has the same shape as f.
1047
1048 Examples
1049 --------
1050 >>> import numpy as np
1051 >>> f = np.array([1, 2, 4, 7, 11, 16])
1052 >>> np.gradient(f)
1053 array([1. , 1.5, 2.5, 3.5, 4.5, 5. ])
1054 >>> np.gradient(f, 2)
1055 array([0.5 , 0.75, 1.25, 1.75, 2.25, 2.5 ])
1056
1057 Spacing can be also specified with an array that represents the coordinates
1058 of the values F along the dimensions.
1059 For instance a uniform spacing:
1060
1061 >>> x = np.arange(f.size)
1062 >>> np.gradient(f, x)
1063 array([1. , 1.5, 2.5, 3.5, 4.5, 5. ])
1064
1065 Or a non uniform one:
1066
1067 >>> x = np.array([0., 1., 1.5, 3.5, 4., 6.])
1068 >>> np.gradient(f, x)
1069 array([1. , 3. , 3.5, 6.7, 6.9, 2.5])
1070
1071 For two dimensional arrays, the return will be two arrays ordered by
1072 axis. In this example the first array stands for the gradient in
1073 rows and the second one in columns direction:
1074
1075 >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]]))
1076 (array([[ 2., 2., -1.],
1077 [ 2., 2., -1.]]),
1078 array([[1. , 2.5, 4. ],
1079 [1. , 1. , 1. ]]))
1080
1081 In this example the spacing is also specified:
1082 uniform for axis=0 and non uniform for axis=1
1083
1084 >>> dx = 2.
1085 >>> y = [1., 1.5, 3.5]
1086 >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]]), dx, y)
1087 (array([[ 1. , 1. , -0.5],
1088 [ 1. , 1. , -0.5]]),
1089 array([[2. , 2. , 2. ],
1090 [2. , 1.7, 0.5]]))
1091
1092 It is possible to specify how boundaries are treated using `edge_order`
1093
1094 >>> x = np.array([0, 1, 2, 3, 4])
1095 >>> f = x**2
1096 >>> np.gradient(f, edge_order=1)
1097 array([1., 2., 4., 6., 7.])
1098 >>> np.gradient(f, edge_order=2)
1099 array([0., 2., 4., 6., 8.])
1100
1101 The `axis` keyword can be used to specify a subset of axes of which the
1102 gradient is calculated
1103
1104 >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]]), axis=0)
1105 array([[ 2., 2., -1.],
1106 [ 2., 2., -1.]])
1107
1108 The `varargs` argument defines the spacing between sample points in the
1109 input array. It can take two forms:
1110
1111 1. An array, specifying coordinates, which may be unevenly spaced:
1112
1113 >>> x = np.array([0., 2., 3., 6., 8.])
1114 >>> y = x ** 2
1115 >>> np.gradient(y, x, edge_order=2)
1116 array([ 0., 4., 6., 12., 16.])
1117
1118 2. A scalar, representing the fixed sample distance:
1119
1120 >>> dx = 2
1121 >>> x = np.array([0., 2., 4., 6., 8.])
1122 >>> y = x ** 2
1123 >>> np.gradient(y, dx, edge_order=2)
1124 array([ 0., 4., 8., 12., 16.])
1125
1126 It's possible to provide different data for spacing along each dimension.
1127 The number of arguments must match the number of dimensions in the input
1128 data.
1129
1130 >>> dx = 2
1131 >>> dy = 3
1132 >>> x = np.arange(0, 6, dx)
1133 >>> y = np.arange(0, 9, dy)
1134 >>> xs, ys = np.meshgrid(x, y)
1135 >>> zs = xs + 2 * ys
1136 >>> np.gradient(zs, dy, dx) # Passing two scalars
1137 (array([[2., 2., 2.],
1138 [2., 2., 2.],
1139 [2., 2., 2.]]),
1140 array([[1., 1., 1.],
1141 [1., 1., 1.],
1142 [1., 1., 1.]]))
1143
1144 Mixing scalars and arrays is also allowed:
1145
1146 >>> np.gradient(zs, y, dx) # Passing one array and one scalar
1147 (array([[2., 2., 2.],
1148 [2., 2., 2.],
1149 [2., 2., 2.]]),
1150 array([[1., 1., 1.],
1151 [1., 1., 1.],
1152 [1., 1., 1.]]))
1153
1154 Notes
1155 -----
1156 Assuming that :math:`f\\in C^{3}` (i.e., :math:`f` has at least 3 continuous
1157 derivatives) and let :math:`h_{*}` be a non-homogeneous stepsize, we
1158 minimize the "consistency error" :math:`\\eta_{i}` between the true gradient
1159 and its estimate from a linear combination of the neighboring grid-points:
1160
1161 .. math::
1162
1163 \\eta_{i} = f_{i}^{\\left(1\\right)} -
1164 \\left[ \\alpha f\\left(x_{i}\\right) +
1165 \\beta f\\left(x_{i} + h_{d}\\right) +
1166 \\gamma f\\left(x_{i}-h_{s}\\right)
1167 \\right]
1168
1169 By substituting :math:`f(x_{i} + h_{d})` and :math:`f(x_{i} - h_{s})`
1170 with their Taylor series expansion, this translates into solving
1171 the following the linear system:
1172
1173 .. math::
1174
1175 \\left\\{
1176 \\begin{array}{r}
1177 \\alpha+\\beta+\\gamma=0 \\\\
1178 \\beta h_{d}-\\gamma h_{s}=1 \\\\
1179 \\beta h_{d}^{2}+\\gamma h_{s}^{2}=0
1180 \\end{array}
1181 \\right.
1182
1183 The resulting approximation of :math:`f_{i}^{(1)}` is the following:
1184
1185 .. math::
1186
1187 \\hat f_{i}^{(1)} =
1188 \\frac{
1189 h_{s}^{2}f\\left(x_{i} + h_{d}\\right)
1190 + \\left(h_{d}^{2} - h_{s}^{2}\\right)f\\left(x_{i}\\right)
1191 - h_{d}^{2}f\\left(x_{i}-h_{s}\\right)}
1192 { h_{s}h_{d}\\left(h_{d} + h_{s}\\right)}
1193 + \\mathcal{O}\\left(\\frac{h_{d}h_{s}^{2}
1194 + h_{s}h_{d}^{2}}{h_{d}
1195 + h_{s}}\\right)
1196
1197 It is worth noting that if :math:`h_{s}=h_{d}`
1198 (i.e., data are evenly spaced)
1199 we find the standard second order approximation:
1200
1201 .. math::
1202
1203 \\hat f_{i}^{(1)}=
1204 \\frac{f\\left(x_{i+1}\\right) - f\\left(x_{i-1}\\right)}{2h}
1205 + \\mathcal{O}\\left(h^{2}\\right)
1206
1207 With a similar procedure the forward/backward approximations used for
1208 boundaries can be derived.
1209
1210 References
1211 ----------
1212 .. [1] Quarteroni A., Sacco R., Saleri F. (2007) Numerical Mathematics
1213 (Texts in Applied Mathematics). New York: Springer.
1214 .. [2] Durran D. R. (1999) Numerical Methods for Wave Equations
1215 in Geophysical Fluid Dynamics. New York: Springer.
1216 .. [3] Fornberg B. (1988) Generation of Finite Difference Formulas on
1217 Arbitrarily Spaced Grids,
1218 Mathematics of Computation 51, no. 184 : 699-706.
1219 `PDF <https://www.ams.org/journals/mcom/1988-51-184/
1220 S0025-5718-1988-0935077-0/S0025-5718-1988-0935077-0.pdf>`_.
1221 """
1222 f = np.asanyarray(f)
1223 N = f.ndim # number of dimensions
1224
1225 if axis is None:
1226 axes = tuple(range(N))
1227 else:
1228 axes = _nx.normalize_axis_tuple(axis, N)
1229
1230 len_axes = len(axes)
1231 n = len(varargs)
1232 if n == 0:
1233 # no spacing argument - use 1 in all axes
1234 dx = [1.0] * len_axes
1235 elif n == 1 and np.ndim(varargs[0]) == 0:
1236 # single scalar for all axes
1237 dx = varargs * len_axes
1238 elif n == len_axes:
1239 # scalar or 1d array for each axis
1240 dx = list(varargs)
1241 for i, distances in enumerate(dx):
1242 distances = np.asanyarray(distances)
1243 if distances.ndim == 0:
1244 continue
1245 elif distances.ndim != 1:
1246 raise ValueError("distances must be either scalars or 1d")
1247 if len(distances) != f.shape[axes[i]]:
1248 raise ValueError("when 1d, distances must match "
1249 "the length of the corresponding dimension")
1250 if np.issubdtype(distances.dtype, np.integer):
1251 # Convert numpy integer types to float64 to avoid modular
1252 # arithmetic in np.diff(distances).
1253 distances = distances.astype(np.float64)
1254 diffx = np.diff(distances)
1255 # if distances are constant reduce to the scalar case
1256 # since it brings a consistent speedup
1257 if (diffx == diffx[0]).all():
1258 diffx = diffx[0]
1259 dx[i] = diffx
1260 else:
1261 raise TypeError("invalid number of arguments")
1262
1263 if edge_order > 2:
1264 raise ValueError("'edge_order' greater than 2 not supported")
1265
1266 # use central differences on interior and one-sided differences on the
1267 # endpoints. This preserves second order-accuracy over the full domain.
1268
1269 outvals = []
1270
1271 # create slice objects --- initially all are [:, :, ..., :]
1272 slice1 = [slice(None)] * N
1273 slice2 = [slice(None)] * N
1274 slice3 = [slice(None)] * N
1275 slice4 = [slice(None)] * N
1276
1277 otype = f.dtype
1278 if otype.type is np.datetime64:
1279 # the timedelta dtype with the same unit information
1280 otype = np.dtype(otype.name.replace('datetime', 'timedelta'))
1281 # view as timedelta to allow addition
1282 f = f.view(otype)
1283 elif otype.type is np.timedelta64:
1284 pass
1285 elif np.issubdtype(otype, np.inexact):
1286 pass
1287 else:
1288 # All other types convert to floating point.
1289 # First check if f is a numpy integer type; if so, convert f to float64
1290 # to avoid modular arithmetic when computing the changes in f.
1291 if np.issubdtype(otype, np.integer):
1292 f = f.astype(np.float64)
1293 otype = np.float64
1294
1295 for axis, ax_dx in zip(axes, dx):
1296 if f.shape[axis] < edge_order + 1:
1297 raise ValueError(
1298 "Shape of array too small to calculate a numerical gradient, "
1299 "at least (edge_order + 1) elements are required.")
1300 # result allocation
1301 out = np.empty_like(f, dtype=otype)
1302
1303 # spacing for the current axis
1304 uniform_spacing = np.ndim(ax_dx) == 0
1305
1306 # Numerical differentiation: 2nd order interior
1307 slice1[axis] = slice(1, -1)
1308 slice2[axis] = slice(None, -2)
1309 slice3[axis] = slice(1, -1)
1310 slice4[axis] = slice(2, None)
1311
1312 if uniform_spacing:
1313 out[tuple(slice1)] = (f[tuple(slice4)] - f[tuple(slice2)]) / (2. * ax_dx)
1314 else:
1315 dx1 = ax_dx[0:-1]
1316 dx2 = ax_dx[1:]
1317 a = -(dx2) / (dx1 * (dx1 + dx2))
1318 b = (dx2 - dx1) / (dx1 * dx2)
1319 c = dx1 / (dx2 * (dx1 + dx2))
1320 # fix the shape for broadcasting
1321 shape = np.ones(N, dtype=int)
1322 shape[axis] = -1
1323
1324 a = a.reshape(shape)
1325 b = b.reshape(shape)
1326 c = c.reshape(shape)
1327 # 1D equivalent -- out[1:-1] = a * f[:-2] + b * f[1:-1] + c * f[2:]
1328 out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] \
1329 + c * f[tuple(slice4)]
1330
1331 # Numerical differentiation: 1st order edges
1332 if edge_order == 1:
1333 slice1[axis] = 0
1334 slice2[axis] = 1
1335 slice3[axis] = 0
1336 dx_0 = ax_dx if uniform_spacing else ax_dx[0]
1337 # 1D equivalent -- out[0] = (f[1] - f[0]) / (x[1] - x[0])
1338 out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_0
1339
1340 slice1[axis] = -1
1341 slice2[axis] = -1
1342 slice3[axis] = -2
1343 dx_n = ax_dx if uniform_spacing else ax_dx[-1]
1344 # 1D equivalent -- out[-1] = (f[-1] - f[-2]) / (x[-1] - x[-2])
1345 out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_n
1346
1347 # Numerical differentiation: 2nd order edges
1348 else:
1349 slice1[axis] = 0
1350 slice2[axis] = 0
1351 slice3[axis] = 1
1352 slice4[axis] = 2
1353 if uniform_spacing:
1354 a = -1.5 / ax_dx
1355 b = 2. / ax_dx
1356 c = -0.5 / ax_dx
1357 else:
1358 dx1 = ax_dx[0]
1359 dx2 = ax_dx[1]
1360 a = -(2. * dx1 + dx2) / (dx1 * (dx1 + dx2))
1361 b = (dx1 + dx2) / (dx1 * dx2)
1362 c = - dx1 / (dx2 * (dx1 + dx2))
1363 # 1D equivalent -- out[0] = a * f[0] + b * f[1] + c * f[2]
1364 out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] \
1365 + c * f[tuple(slice4)]
1366
1367 slice1[axis] = -1
1368 slice2[axis] = -3
1369 slice3[axis] = -2
1370 slice4[axis] = -1
1371 if uniform_spacing:
1372 a = 0.5 / ax_dx
1373 b = -2. / ax_dx
1374 c = 1.5 / ax_dx
1375 else:
1376 dx1 = ax_dx[-2]
1377 dx2 = ax_dx[-1]
1378 a = (dx2) / (dx1 * (dx1 + dx2))
1379 b = - (dx2 + dx1) / (dx1 * dx2)
1380 c = (2. * dx2 + dx1) / (dx2 * (dx1 + dx2))
1381 # 1D equivalent -- out[-1] = a * f[-3] + b * f[-2] + c * f[-1]
1382 out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] \
1383 + c * f[tuple(slice4)]
1384
1385 outvals.append(out)
1386
1387 # reset the slice object in this dimension to ":"
1388 slice1[axis] = slice(None)
1389 slice2[axis] = slice(None)
1390 slice3[axis] = slice(None)
1391 slice4[axis] = slice(None)
1392
1393 if len_axes == 1:
1394 return outvals[0]
1395 return tuple(outvals)
1396
1397
1398def _diff_dispatcher(a, n=None, axis=None, prepend=None, append=None):
1399 return (a, prepend, append)
1400
1401
1402@array_function_dispatch(_diff_dispatcher)
1403def diff(a, n=1, axis=-1, prepend=np._NoValue, append=np._NoValue):
1404 """
1405 Calculate the n-th discrete difference along the given axis.
1406
1407 The first difference is given by ``out[i] = a[i+1] - a[i]`` along
1408 the given axis, higher differences are calculated by using `diff`
1409 recursively.
1410
1411 Parameters
1412 ----------
1413 a : array_like
1414 Input array
1415 n : int, optional
1416 The number of times values are differenced. If zero, the input
1417 is returned as-is.
1418 axis : int, optional
1419 The axis along which the difference is taken, default is the
1420 last axis.
1421 prepend, append : array_like, optional
1422 Values to prepend or append to `a` along axis prior to
1423 performing the difference. Scalar values are expanded to
1424 arrays with length 1 in the direction of axis and the shape
1425 of the input array in along all other axes. Otherwise the
1426 dimension and shape must match `a` except along axis.
1427
1428 Returns
1429 -------
1430 diff : ndarray
1431 The n-th differences. The shape of the output is the same as `a`
1432 except along `axis` where the dimension is smaller by `n`. The
1433 type of the output is the same as the type of the difference
1434 between any two elements of `a`. This is the same as the type of
1435 `a` in most cases. A notable exception is `datetime64`, which
1436 results in a `timedelta64` output array.
1437
1438 See Also
1439 --------
1440 gradient, ediff1d, cumsum
1441
1442 Notes
1443 -----
1444 Type is preserved for boolean arrays, so the result will contain
1445 `False` when consecutive elements are the same and `True` when they
1446 differ.
1447
1448 For unsigned integer arrays, the results will also be unsigned. This
1449 should not be surprising, as the result is consistent with
1450 calculating the difference directly:
1451
1452 >>> u8_arr = np.array([1, 0], dtype=np.uint8)
1453 >>> np.diff(u8_arr)
1454 array([255], dtype=uint8)
1455 >>> u8_arr[1,...] - u8_arr[0,...]
1456 np.uint8(255)
1457
1458 If this is not desirable, then the array should be cast to a larger
1459 integer type first:
1460
1461 >>> i16_arr = u8_arr.astype(np.int16)
1462 >>> np.diff(i16_arr)
1463 array([-1], dtype=int16)
1464
1465 Examples
1466 --------
1467 >>> import numpy as np
1468 >>> x = np.array([1, 2, 4, 7, 0])
1469 >>> np.diff(x)
1470 array([ 1, 2, 3, -7])
1471 >>> np.diff(x, n=2)
1472 array([ 1, 1, -10])
1473
1474 >>> x = np.array([[1, 3, 6, 10], [0, 5, 6, 8]])
1475 >>> np.diff(x)
1476 array([[2, 3, 4],
1477 [5, 1, 2]])
1478 >>> np.diff(x, axis=0)
1479 array([[-1, 2, 0, -2]])
1480
1481 >>> x = np.arange('1066-10-13', '1066-10-16', dtype=np.datetime64)
1482 >>> np.diff(x)
1483 array([1, 1], dtype='timedelta64[D]')
1484
1485 """
1486 if n == 0:
1487 return a
1488 if n < 0:
1489 raise ValueError(
1490 "order must be non-negative but got " + repr(n))
1491
1492 a = asanyarray(a)
1493 nd = a.ndim
1494 if nd == 0:
1495 raise ValueError("diff requires input that is at least one dimensional")
1496 axis = normalize_axis_index(axis, nd)
1497
1498 combined = []
1499 if prepend is not np._NoValue:
1500 prepend = np.asanyarray(prepend)
1501 if prepend.ndim == 0:
1502 shape = list(a.shape)
1503 shape[axis] = 1
1504 prepend = np.broadcast_to(prepend, tuple(shape))
1505 combined.append(prepend)
1506
1507 combined.append(a)
1508
1509 if append is not np._NoValue:
1510 append = np.asanyarray(append)
1511 if append.ndim == 0:
1512 shape = list(a.shape)
1513 shape[axis] = 1
1514 append = np.broadcast_to(append, tuple(shape))
1515 combined.append(append)
1516
1517 if len(combined) > 1:
1518 a = np.concatenate(combined, axis)
1519
1520 slice1 = [slice(None)] * nd
1521 slice2 = [slice(None)] * nd
1522 slice1[axis] = slice(1, None)
1523 slice2[axis] = slice(None, -1)
1524 slice1 = tuple(slice1)
1525 slice2 = tuple(slice2)
1526
1527 op = not_equal if a.dtype == np.bool else subtract
1528 for _ in range(n):
1529 a = op(a[slice1], a[slice2])
1530
1531 return a
1532
1533
1534def _interp_dispatcher(x, xp, fp, left=None, right=None, period=None):
1535 return (x, xp, fp)
1536
1537
1538@array_function_dispatch(_interp_dispatcher)
1539def interp(x, xp, fp, left=None, right=None, period=None):
1540 """
1541 One-dimensional linear interpolation for monotonically increasing sample points.
1542
1543 Returns the one-dimensional piecewise linear interpolant to a function
1544 with given discrete data points (`xp`, `fp`), evaluated at `x`.
1545
1546 Parameters
1547 ----------
1548 x : array_like
1549 The x-coordinates at which to evaluate the interpolated values.
1550
1551 xp : 1-D sequence of floats
1552 The x-coordinates of the data points, must be increasing if argument
1553 `period` is not specified. Otherwise, `xp` is internally sorted after
1554 normalizing the periodic boundaries with ``xp = xp % period``.
1555
1556 fp : 1-D sequence of float or complex
1557 The y-coordinates of the data points, same length as `xp`.
1558
1559 left : optional float or complex corresponding to fp
1560 Value to return for `x < xp[0]`, default is `fp[0]`.
1561
1562 right : optional float or complex corresponding to fp
1563 Value to return for `x > xp[-1]`, default is `fp[-1]`.
1564
1565 period : None or float, optional
1566 A period for the x-coordinates. This parameter allows the proper
1567 interpolation of angular x-coordinates. Parameters `left` and `right`
1568 are ignored if `period` is specified.
1569
1570 Returns
1571 -------
1572 y : float or complex (corresponding to fp) or ndarray
1573 The interpolated values, same shape as `x`.
1574
1575 Raises
1576 ------
1577 ValueError
1578 If `xp` and `fp` have different length
1579 If `xp` or `fp` are not 1-D sequences
1580 If `period == 0`
1581
1582 See Also
1583 --------
1584 scipy.interpolate
1585
1586 Warnings
1587 --------
1588 The x-coordinate sequence is expected to be increasing, but this is not
1589 explicitly enforced. However, if the sequence `xp` is non-increasing,
1590 interpolation results are meaningless.
1591
1592 Note that, since NaN is unsortable, `xp` also cannot contain NaNs.
1593
1594 A simple check for `xp` being strictly increasing is::
1595
1596 np.all(np.diff(xp) > 0)
1597
1598 Examples
1599 --------
1600 >>> import numpy as np
1601 >>> xp = [1, 2, 3]
1602 >>> fp = [3, 2, 0]
1603 >>> np.interp(2.5, xp, fp)
1604 1.0
1605 >>> np.interp([0, 1, 1.5, 2.72, 3.14], xp, fp)
1606 array([3. , 3. , 2.5 , 0.56, 0. ])
1607 >>> UNDEF = -99.0
1608 >>> np.interp(3.14, xp, fp, right=UNDEF)
1609 -99.0
1610
1611 Plot an interpolant to the sine function:
1612
1613 >>> x = np.linspace(0, 2*np.pi, 10)
1614 >>> y = np.sin(x)
1615 >>> xvals = np.linspace(0, 2*np.pi, 50)
1616 >>> yinterp = np.interp(xvals, x, y)
1617 >>> import matplotlib.pyplot as plt
1618 >>> plt.plot(x, y, 'o')
1619 [<matplotlib.lines.Line2D object at 0x...>]
1620 >>> plt.plot(xvals, yinterp, '-x')
1621 [<matplotlib.lines.Line2D object at 0x...>]
1622 >>> plt.show()
1623
1624 Interpolation with periodic x-coordinates:
1625
1626 >>> x = [-180, -170, -185, 185, -10, -5, 0, 365]
1627 >>> xp = [190, -190, 350, -350]
1628 >>> fp = [5, 10, 3, 4]
1629 >>> np.interp(x, xp, fp, period=360)
1630 array([7.5 , 5. , 8.75, 6.25, 3. , 3.25, 3.5 , 3.75])
1631
1632 Complex interpolation:
1633
1634 >>> x = [1.5, 4.0]
1635 >>> xp = [2,3,5]
1636 >>> fp = [1.0j, 0, 2+3j]
1637 >>> np.interp(x, xp, fp)
1638 array([0.+1.j , 1.+1.5j])
1639
1640 """
1641
1642 fp = np.asarray(fp)
1643
1644 if np.iscomplexobj(fp):
1645 interp_func = compiled_interp_complex
1646 input_dtype = np.complex128
1647 else:
1648 interp_func = compiled_interp
1649 input_dtype = np.float64
1650
1651 if period is not None:
1652 if period == 0:
1653 raise ValueError("period must be a non-zero value")
1654 period = abs(period)
1655 left = None
1656 right = None
1657
1658 x = np.asarray(x, dtype=np.float64)
1659 xp = np.asarray(xp, dtype=np.float64)
1660 fp = np.asarray(fp, dtype=input_dtype)
1661
1662 if xp.ndim != 1 or fp.ndim != 1:
1663 raise ValueError("Data points must be 1-D sequences")
1664 if xp.shape[0] != fp.shape[0]:
1665 raise ValueError("fp and xp are not of the same length")
1666 # normalizing periodic boundaries
1667 x = x % period
1668 xp = xp % period
1669 asort_xp = np.argsort(xp)
1670 xp = xp[asort_xp]
1671 fp = fp[asort_xp]
1672 xp = np.concatenate((xp[-1:] - period, xp, xp[0:1] + period))
1673 fp = np.concatenate((fp[-1:], fp, fp[0:1]))
1674
1675 return interp_func(x, xp, fp, left, right)
1676
1677
1678def _angle_dispatcher(z, deg=None):
1679 return (z,)
1680
1681
1682@array_function_dispatch(_angle_dispatcher)
1683def angle(z, deg=False):
1684 """
1685 Return the angle of the complex argument.
1686
1687 Parameters
1688 ----------
1689 z : array_like
1690 A complex number or sequence of complex numbers.
1691 deg : bool, optional
1692 Return angle in degrees if True, radians if False (default).
1693
1694 Returns
1695 -------
1696 angle : ndarray or scalar
1697 The counterclockwise angle from the positive real axis on the complex
1698 plane in the range ``(-pi, pi]``, with dtype as numpy.float64.
1699
1700 See Also
1701 --------
1702 arctan2
1703 absolute
1704
1705 Notes
1706 -----
1707 This function passes the imaginary and real parts of the argument to
1708 `arctan2` to compute the result; consequently, it follows the convention
1709 of `arctan2` when the magnitude of the argument is zero. See example.
1710
1711 Examples
1712 --------
1713 >>> import numpy as np
1714 >>> np.angle([1.0, 1.0j, 1+1j]) # in radians
1715 array([ 0. , 1.57079633, 0.78539816]) # may vary
1716 >>> np.angle(1+1j, deg=True) # in degrees
1717 45.0
1718 >>> np.angle([0., -0., complex(0., -0.), complex(-0., -0.)]) # convention
1719 array([ 0. , 3.14159265, -0. , -3.14159265])
1720
1721 """
1722 z = asanyarray(z)
1723 if issubclass(z.dtype.type, _nx.complexfloating):
1724 zimag = z.imag
1725 zreal = z.real
1726 else:
1727 zimag = 0
1728 zreal = z
1729
1730 a = arctan2(zimag, zreal)
1731 if deg:
1732 a *= 180 / pi
1733 return a
1734
1735
1736def _unwrap_dispatcher(p, discont=None, axis=None, *, period=None):
1737 return (p,)
1738
1739
1740@array_function_dispatch(_unwrap_dispatcher)
1741def unwrap(p, discont=None, axis=-1, *, period=2 * pi):
1742 r"""
1743 Unwrap by taking the complement of large deltas with respect to the period.
1744
1745 This unwraps a signal `p` by changing elements which have an absolute
1746 difference from their predecessor of more than ``max(discont, period/2)``
1747 to their `period`-complementary values.
1748
1749 For the default case where `period` is :math:`2\pi` and `discont` is
1750 :math:`\pi`, this unwraps a radian phase `p` such that adjacent differences
1751 are never greater than :math:`\pi` by adding :math:`2k\pi` for some
1752 integer :math:`k`.
1753
1754 Parameters
1755 ----------
1756 p : array_like
1757 Input array.
1758 discont : float, optional
1759 Maximum discontinuity between values, default is ``period/2``.
1760 Values below ``period/2`` are treated as if they were ``period/2``.
1761 To have an effect different from the default, `discont` should be
1762 larger than ``period/2``.
1763 axis : int, optional
1764 Axis along which unwrap will operate, default is the last axis.
1765 period : float, optional
1766 Size of the range over which the input wraps. By default, it is
1767 ``2 pi``.
1768
1769 .. versionadded:: 1.21.0
1770
1771 Returns
1772 -------
1773 out : ndarray
1774 Output array.
1775
1776 See Also
1777 --------
1778 rad2deg, deg2rad
1779
1780 Notes
1781 -----
1782 If the discontinuity in `p` is smaller than ``period/2``,
1783 but larger than `discont`, no unwrapping is done because taking
1784 the complement would only make the discontinuity larger.
1785
1786 Examples
1787 --------
1788 >>> import numpy as np
1789
1790 >>> phase = np.linspace(0, np.pi, num=5)
1791 >>> phase[3:] += np.pi
1792 >>> phase
1793 array([ 0. , 0.78539816, 1.57079633, 5.49778714, 6.28318531]) # may vary
1794 >>> np.unwrap(phase)
1795 array([ 0. , 0.78539816, 1.57079633, -0.78539816, 0. ]) # may vary
1796 >>> np.unwrap([0, 1, 2, -1, 0], period=4)
1797 array([0, 1, 2, 3, 4])
1798 >>> np.unwrap([ 1, 2, 3, 4, 5, 6, 1, 2, 3], period=6)
1799 array([1, 2, 3, 4, 5, 6, 7, 8, 9])
1800 >>> np.unwrap([2, 3, 4, 5, 2, 3, 4, 5], period=4)
1801 array([2, 3, 4, 5, 6, 7, 8, 9])
1802 >>> phase_deg = np.mod(np.linspace(0 ,720, 19), 360) - 180
1803 >>> np.unwrap(phase_deg, period=360)
1804 array([-180., -140., -100., -60., -20., 20., 60., 100., 140.,
1805 180., 220., 260., 300., 340., 380., 420., 460., 500.,
1806 540.])
1807
1808 This example plots the unwrapping of the wrapped input signal `w`.
1809 First generate `w`, then apply `unwrap` to get `u`.
1810
1811 >>> t = np.linspace(0, 25, 801)
1812 >>> w = np.mod(1.5 * np.sin(1.1 * t + 0.26) * (1 - t / 6 + (t / 23) ** 3), 2.0) - 1
1813 >>> u = np.unwrap(w, period=2.0)
1814
1815 Plot `w` and `u`.
1816
1817 >>> import matplotlib.pyplot as plt
1818 >>> plt.plot(t, w, label='w (a signal wrapped to [-1, 1])')
1819 >>> plt.plot(t, u, linewidth=2.5, alpha=0.5, label='unwrap(w, period=2)')
1820 >>> plt.xlabel('t')
1821 >>> plt.grid(alpha=0.6)
1822 >>> plt.legend(framealpha=1, shadow=True)
1823 >>> plt.show()
1824 """
1825 p = asarray(p)
1826 nd = p.ndim
1827 dd = diff(p, axis=axis)
1828 if discont is None:
1829 discont = period / 2
1830 slice1 = [slice(None, None)] * nd # full slices
1831 slice1[axis] = slice(1, None)
1832 slice1 = tuple(slice1)
1833 dtype = np.result_type(dd, period)
1834 if _nx.issubdtype(dtype, _nx.integer):
1835 interval_high, rem = divmod(period, 2)
1836 boundary_ambiguous = rem == 0
1837 else:
1838 interval_high = period / 2
1839 boundary_ambiguous = True
1840 interval_low = -interval_high
1841 ddmod = mod(dd - interval_low, period) + interval_low
1842 if boundary_ambiguous:
1843 # for `mask = (abs(dd) == period/2)`, the above line made
1844 # `ddmod[mask] == -period/2`. correct these such that
1845 # `ddmod[mask] == sign(dd[mask])*period/2`.
1846 _nx.copyto(ddmod, interval_high,
1847 where=(ddmod == interval_low) & (dd > 0))
1848 ph_correct = ddmod - dd
1849 _nx.copyto(ph_correct, 0, where=abs(dd) < discont)
1850 up = array(p, copy=True, dtype=dtype)
1851 up[slice1] = p[slice1] + ph_correct.cumsum(axis)
1852 return up
1853
1854
1855def _sort_complex(a):
1856 return (a,)
1857
1858
1859@array_function_dispatch(_sort_complex)
1860def sort_complex(a):
1861 """
1862 Sort a complex array using the real part first, then the imaginary part.
1863
1864 Parameters
1865 ----------
1866 a : array_like
1867 Input array
1868
1869 Returns
1870 -------
1871 out : complex ndarray
1872 Always returns a sorted complex array.
1873
1874 Examples
1875 --------
1876 >>> import numpy as np
1877 >>> np.sort_complex([5, 3, 6, 2, 1])
1878 array([1.+0.j, 2.+0.j, 3.+0.j, 5.+0.j, 6.+0.j])
1879
1880 >>> np.sort_complex([1 + 2j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j])
1881 array([1.+2.j, 2.-1.j, 3.-3.j, 3.-2.j, 3.+5.j])
1882
1883 """
1884 b = array(a, copy=True)
1885 b.sort()
1886 if not issubclass(b.dtype.type, _nx.complexfloating):
1887 if b.dtype.char in 'bhBH':
1888 return b.astype('F')
1889 elif b.dtype.char == 'g':
1890 return b.astype('G')
1891 else:
1892 return b.astype('D')
1893 else:
1894 return b
1895
1896
1897def _arg_trim_zeros(filt):
1898 """Return indices of the first and last non-zero element.
1899
1900 Parameters
1901 ----------
1902 filt : array_like
1903 Input array.
1904
1905 Returns
1906 -------
1907 start, stop : ndarray
1908 Two arrays containing the indices of the first and last non-zero
1909 element in each dimension.
1910
1911 See also
1912 --------
1913 trim_zeros
1914
1915 Examples
1916 --------
1917 >>> import numpy as np
1918 >>> _arg_trim_zeros(np.array([0, 0, 1, 1, 0]))
1919 (array([2]), array([3]))
1920 """
1921 nonzero = (
1922 np.argwhere(filt)
1923 if filt.dtype != np.object_
1924 # Historically, `trim_zeros` treats `None` in an object array
1925 # as non-zero while argwhere doesn't, account for that
1926 else np.argwhere(filt != 0)
1927 )
1928 if nonzero.size == 0:
1929 start = stop = np.array([], dtype=np.intp)
1930 else:
1931 start = nonzero.min(axis=0)
1932 stop = nonzero.max(axis=0)
1933 return start, stop
1934
1935
1936def _trim_zeros(filt, trim=None, axis=None):
1937 return (filt,)
1938
1939
1940@array_function_dispatch(_trim_zeros)
1941def trim_zeros(filt, trim='fb', axis=None):
1942 """Remove values along a dimension which are zero along all other.
1943
1944 Parameters
1945 ----------
1946 filt : array_like
1947 Input array.
1948 trim : {"fb", "f", "b"}, optional
1949 A string with 'f' representing trim from front and 'b' to trim from
1950 back. By default, zeros are trimmed on both sides.
1951 Front and back refer to the edges of a dimension, with "front" referring
1952 to the side with the lowest index 0, and "back" referring to the highest
1953 index (or index -1).
1954 axis : int or sequence, optional
1955 If None, `filt` is cropped such that the smallest bounding box is
1956 returned that still contains all values which are not zero.
1957 If an axis is specified, `filt` will be sliced in that dimension only
1958 on the sides specified by `trim`. The remaining area will be the
1959 smallest that still contains all values wich are not zero.
1960
1961 .. versionadded:: 2.2.0
1962
1963 Returns
1964 -------
1965 trimmed : ndarray or sequence
1966 The result of trimming the input. The number of dimensions and the
1967 input data type are preserved.
1968
1969 Notes
1970 -----
1971 For all-zero arrays, the first axis is trimmed first.
1972
1973 Examples
1974 --------
1975 >>> import numpy as np
1976 >>> a = np.array((0, 0, 0, 1, 2, 3, 0, 2, 1, 0))
1977 >>> np.trim_zeros(a)
1978 array([1, 2, 3, 0, 2, 1])
1979
1980 >>> np.trim_zeros(a, trim='b')
1981 array([0, 0, 0, ..., 0, 2, 1])
1982
1983 Multiple dimensions are supported.
1984
1985 >>> b = np.array([[0, 0, 2, 3, 0, 0],
1986 ... [0, 1, 0, 3, 0, 0],
1987 ... [0, 0, 0, 0, 0, 0]])
1988 >>> np.trim_zeros(b)
1989 array([[0, 2, 3],
1990 [1, 0, 3]])
1991
1992 >>> np.trim_zeros(b, axis=-1)
1993 array([[0, 2, 3],
1994 [1, 0, 3],
1995 [0, 0, 0]])
1996
1997 The input data type is preserved, list/tuple in means list/tuple out.
1998
1999 >>> np.trim_zeros([0, 1, 2, 0])
2000 [1, 2]
2001
2002 """
2003 filt_ = np.asarray(filt)
2004
2005 trim = trim.lower()
2006 if trim not in {"fb", "bf", "f", "b"}:
2007 raise ValueError(f"unexpected character(s) in `trim`: {trim!r}")
2008 if axis is None:
2009 axis_tuple = tuple(range(filt_.ndim))
2010 else:
2011 axis_tuple = _nx.normalize_axis_tuple(axis, filt_.ndim, argname="axis")
2012
2013 if not axis_tuple:
2014 # No trimming requested -> return input unmodified.
2015 return filt
2016
2017 start, stop = _arg_trim_zeros(filt_)
2018 stop += 1 # Adjust for slicing
2019
2020 if start.size == 0:
2021 # filt is all-zero -> assign same values to start and stop so that
2022 # resulting slice will be empty
2023 start = stop = np.zeros(filt_.ndim, dtype=np.intp)
2024 else:
2025 if 'f' not in trim:
2026 start = (None,) * filt_.ndim
2027 if 'b' not in trim:
2028 stop = (None,) * filt_.ndim
2029
2030 sl = tuple(slice(start[ax], stop[ax]) if ax in axis_tuple else slice(None)
2031 for ax in range(filt_.ndim))
2032 if len(sl) == 1:
2033 # filt is 1D -> avoid multi-dimensional slicing to preserve
2034 # non-array input types
2035 return filt[sl[0]]
2036 return filt[sl]
2037
2038
2039def _extract_dispatcher(condition, arr):
2040 return (condition, arr)
2041
2042
2043@array_function_dispatch(_extract_dispatcher)
2044def extract(condition, arr):
2045 """
2046 Return the elements of an array that satisfy some condition.
2047
2048 This is equivalent to ``np.compress(ravel(condition), ravel(arr))``. If
2049 `condition` is boolean ``np.extract`` is equivalent to ``arr[condition]``.
2050
2051 Note that `place` does the exact opposite of `extract`.
2052
2053 Parameters
2054 ----------
2055 condition : array_like
2056 An array whose nonzero or True entries indicate the elements of `arr`
2057 to extract.
2058 arr : array_like
2059 Input array of the same size as `condition`.
2060
2061 Returns
2062 -------
2063 extract : ndarray
2064 Rank 1 array of values from `arr` where `condition` is True.
2065
2066 See Also
2067 --------
2068 take, put, copyto, compress, place
2069
2070 Examples
2071 --------
2072 >>> import numpy as np
2073 >>> arr = np.arange(12).reshape((3, 4))
2074 >>> arr
2075 array([[ 0, 1, 2, 3],
2076 [ 4, 5, 6, 7],
2077 [ 8, 9, 10, 11]])
2078 >>> condition = np.mod(arr, 3)==0
2079 >>> condition
2080 array([[ True, False, False, True],
2081 [False, False, True, False],
2082 [False, True, False, False]])
2083 >>> np.extract(condition, arr)
2084 array([0, 3, 6, 9])
2085
2086
2087 If `condition` is boolean:
2088
2089 >>> arr[condition]
2090 array([0, 3, 6, 9])
2091
2092 """
2093 return _nx.take(ravel(arr), nonzero(ravel(condition))[0])
2094
2095
2096def _place_dispatcher(arr, mask, vals):
2097 return (arr, mask, vals)
2098
2099
2100@array_function_dispatch(_place_dispatcher)
2101def place(arr, mask, vals):
2102 """
2103 Change elements of an array based on conditional and input values.
2104
2105 Similar to ``np.copyto(arr, vals, where=mask)``, the difference is that
2106 `place` uses the first N elements of `vals`, where N is the number of
2107 True values in `mask`, while `copyto` uses the elements where `mask`
2108 is True.
2109
2110 Note that `extract` does the exact opposite of `place`.
2111
2112 Parameters
2113 ----------
2114 arr : ndarray
2115 Array to put data into.
2116 mask : array_like
2117 Boolean mask array. Must have the same size as `a`.
2118 vals : 1-D sequence
2119 Values to put into `a`. Only the first N elements are used, where
2120 N is the number of True values in `mask`. If `vals` is smaller
2121 than N, it will be repeated, and if elements of `a` are to be masked,
2122 this sequence must be non-empty.
2123
2124 See Also
2125 --------
2126 copyto, put, take, extract
2127
2128 Examples
2129 --------
2130 >>> import numpy as np
2131 >>> arr = np.arange(6).reshape(2, 3)
2132 >>> np.place(arr, arr>2, [44, 55])
2133 >>> arr
2134 array([[ 0, 1, 2],
2135 [44, 55, 44]])
2136
2137 """
2138 return _place(arr, mask, vals)
2139
2140
2141# See https://docs.scipy.org/doc/numpy/reference/c-api.generalized-ufuncs.html
2142_DIMENSION_NAME = r'\w+'
2143_CORE_DIMENSION_LIST = f'(?:{_DIMENSION_NAME}(?:,{_DIMENSION_NAME})*)?'
2144_ARGUMENT = fr'\({_CORE_DIMENSION_LIST}\)'
2145_ARGUMENT_LIST = f'{_ARGUMENT}(?:,{_ARGUMENT})*'
2146_SIGNATURE = f'^{_ARGUMENT_LIST}->{_ARGUMENT_LIST}$'
2147
2148
2149def _parse_gufunc_signature(signature):
2150 """
2151 Parse string signatures for a generalized universal function.
2152
2153 Arguments
2154 ---------
2155 signature : string
2156 Generalized universal function signature, e.g., ``(m,n),(n,p)->(m,p)``
2157 for ``np.matmul``.
2158
2159 Returns
2160 -------
2161 Tuple of input and output core dimensions parsed from the signature, each
2162 of the form List[Tuple[str, ...]].
2163 """
2164 signature = re.sub(r'\s+', '', signature)
2165
2166 if not re.match(_SIGNATURE, signature):
2167 raise ValueError(
2168 f'not a valid gufunc signature: {signature}')
2169 return tuple([tuple(re.findall(_DIMENSION_NAME, arg))
2170 for arg in re.findall(_ARGUMENT, arg_list)]
2171 for arg_list in signature.split('->'))
2172
2173
2174def _update_dim_sizes(dim_sizes, arg, core_dims):
2175 """
2176 Incrementally check and update core dimension sizes for a single argument.
2177
2178 Arguments
2179 ---------
2180 dim_sizes : Dict[str, int]
2181 Sizes of existing core dimensions. Will be updated in-place.
2182 arg : ndarray
2183 Argument to examine.
2184 core_dims : Tuple[str, ...]
2185 Core dimensions for this argument.
2186 """
2187 if not core_dims:
2188 return
2189
2190 num_core_dims = len(core_dims)
2191 if arg.ndim < num_core_dims:
2192 raise ValueError(
2193 '%d-dimensional argument does not have enough '
2194 'dimensions for all core dimensions %r'
2195 % (arg.ndim, core_dims))
2196
2197 core_shape = arg.shape[-num_core_dims:]
2198 for dim, size in zip(core_dims, core_shape):
2199 if dim in dim_sizes:
2200 if size != dim_sizes[dim]:
2201 raise ValueError(
2202 'inconsistent size for core dimension %r: %r vs %r'
2203 % (dim, size, dim_sizes[dim]))
2204 else:
2205 dim_sizes[dim] = size
2206
2207
2208def _parse_input_dimensions(args, input_core_dims):
2209 """
2210 Parse broadcast and core dimensions for vectorize with a signature.
2211
2212 Arguments
2213 ---------
2214 args : Tuple[ndarray, ...]
2215 Tuple of input arguments to examine.
2216 input_core_dims : List[Tuple[str, ...]]
2217 List of core dimensions corresponding to each input.
2218
2219 Returns
2220 -------
2221 broadcast_shape : Tuple[int, ...]
2222 Common shape to broadcast all non-core dimensions to.
2223 dim_sizes : Dict[str, int]
2224 Common sizes for named core dimensions.
2225 """
2226 broadcast_args = []
2227 dim_sizes = {}
2228 for arg, core_dims in zip(args, input_core_dims):
2229 _update_dim_sizes(dim_sizes, arg, core_dims)
2230 ndim = arg.ndim - len(core_dims)
2231 dummy_array = np.lib.stride_tricks.as_strided(0, arg.shape[:ndim])
2232 broadcast_args.append(dummy_array)
2233 broadcast_shape = np.lib._stride_tricks_impl._broadcast_shape(
2234 *broadcast_args
2235 )
2236 return broadcast_shape, dim_sizes
2237
2238
2239def _calculate_shapes(broadcast_shape, dim_sizes, list_of_core_dims):
2240 """Helper for calculating broadcast shapes with core dimensions."""
2241 return [broadcast_shape + tuple(dim_sizes[dim] for dim in core_dims)
2242 for core_dims in list_of_core_dims]
2243
2244
2245def _create_arrays(broadcast_shape, dim_sizes, list_of_core_dims, dtypes,
2246 results=None):
2247 """Helper for creating output arrays in vectorize."""
2248 shapes = _calculate_shapes(broadcast_shape, dim_sizes, list_of_core_dims)
2249 if dtypes is None:
2250 dtypes = [None] * len(shapes)
2251 if results is None:
2252 arrays = tuple(np.empty(shape=shape, dtype=dtype)
2253 for shape, dtype in zip(shapes, dtypes))
2254 else:
2255 arrays = tuple(np.empty_like(result, shape=shape, dtype=dtype)
2256 for result, shape, dtype
2257 in zip(results, shapes, dtypes))
2258 return arrays
2259
2260
2261def _get_vectorize_dtype(dtype):
2262 if dtype.char in "SU":
2263 return dtype.char
2264 return dtype
2265
2266
2267@set_module('numpy')
2268class vectorize:
2269 """
2270 vectorize(pyfunc=np._NoValue, otypes=None, doc=None, excluded=None,
2271 cache=False, signature=None)
2272
2273 Returns an object that acts like pyfunc, but takes arrays as input.
2274
2275 Define a vectorized function which takes a nested sequence of objects or
2276 numpy arrays as inputs and returns a single numpy array or a tuple of numpy
2277 arrays. The vectorized function evaluates `pyfunc` over successive tuples
2278 of the input arrays like the python map function, except it uses the
2279 broadcasting rules of numpy.
2280
2281 The data type of the output of `vectorized` is determined by calling
2282 the function with the first element of the input. This can be avoided
2283 by specifying the `otypes` argument.
2284
2285 Parameters
2286 ----------
2287 pyfunc : callable, optional
2288 A python function or method.
2289 Can be omitted to produce a decorator with keyword arguments.
2290 otypes : str or list of dtypes, optional
2291 The output data type. It must be specified as either a string of
2292 typecode characters or a list of data type specifiers. There should
2293 be one data type specifier for each output.
2294 doc : str, optional
2295 The docstring for the function. If None, the docstring will be the
2296 ``pyfunc.__doc__``.
2297 excluded : set, optional
2298 Set of strings or integers representing the positional or keyword
2299 arguments for which the function will not be vectorized. These will be
2300 passed directly to `pyfunc` unmodified.
2301
2302 cache : bool, optional
2303 If neither `otypes` nor `signature` are provided, and `cache` is ``True``, then
2304 cache the number of outputs.
2305
2306 signature : string, optional
2307 Generalized universal function signature, e.g., ``(m,n),(n)->(m)`` for
2308 vectorized matrix-vector multiplication. If provided, ``pyfunc`` will
2309 be called with (and expected to return) arrays with shapes given by the
2310 size of corresponding core dimensions. By default, ``pyfunc`` is
2311 assumed to take scalars as input and output.
2312
2313 Returns
2314 -------
2315 out : callable
2316 A vectorized function if ``pyfunc`` was provided,
2317 a decorator otherwise.
2318
2319 See Also
2320 --------
2321 frompyfunc : Takes an arbitrary Python function and returns a ufunc
2322
2323 Notes
2324 -----
2325 The `vectorize` function is provided primarily for convenience, not for
2326 performance. The implementation is essentially a for loop.
2327
2328 If neither `otypes` nor `signature` are specified, then a call to the function with
2329 the first argument will be used to determine the number of outputs. The results of
2330 this call will be cached if `cache` is `True` to prevent calling the function
2331 twice. However, to implement the cache, the original function must be wrapped
2332 which will slow down subsequent calls, so only do this if your function is
2333 expensive.
2334
2335 The new keyword argument interface and `excluded` argument support
2336 further degrades performance.
2337
2338 References
2339 ----------
2340 .. [1] :doc:`/reference/c-api/generalized-ufuncs`
2341
2342 Examples
2343 --------
2344 >>> import numpy as np
2345 >>> def myfunc(a, b):
2346 ... "Return a-b if a>b, otherwise return a+b"
2347 ... if a > b:
2348 ... return a - b
2349 ... else:
2350 ... return a + b
2351
2352 >>> vfunc = np.vectorize(myfunc)
2353 >>> vfunc([1, 2, 3, 4], 2)
2354 array([3, 4, 1, 2])
2355
2356 The docstring is taken from the input function to `vectorize` unless it
2357 is specified:
2358
2359 >>> vfunc.__doc__
2360 'Return a-b if a>b, otherwise return a+b'
2361 >>> vfunc = np.vectorize(myfunc, doc='Vectorized `myfunc`')
2362 >>> vfunc.__doc__
2363 'Vectorized `myfunc`'
2364
2365 The output type is determined by evaluating the first element of the input,
2366 unless it is specified:
2367
2368 >>> out = vfunc([1, 2, 3, 4], 2)
2369 >>> type(out[0])
2370 <class 'numpy.int64'>
2371 >>> vfunc = np.vectorize(myfunc, otypes=[float])
2372 >>> out = vfunc([1, 2, 3, 4], 2)
2373 >>> type(out[0])
2374 <class 'numpy.float64'>
2375
2376 The `excluded` argument can be used to prevent vectorizing over certain
2377 arguments. This can be useful for array-like arguments of a fixed length
2378 such as the coefficients for a polynomial as in `polyval`:
2379
2380 >>> def mypolyval(p, x):
2381 ... _p = list(p)
2382 ... res = _p.pop(0)
2383 ... while _p:
2384 ... res = res*x + _p.pop(0)
2385 ... return res
2386
2387 Here, we exclude the zeroth argument from vectorization whether it is
2388 passed by position or keyword.
2389
2390 >>> vpolyval = np.vectorize(mypolyval, excluded={0, 'p'})
2391 >>> vpolyval([1, 2, 3], x=[0, 1])
2392 array([3, 6])
2393 >>> vpolyval(p=[1, 2, 3], x=[0, 1])
2394 array([3, 6])
2395
2396 The `signature` argument allows for vectorizing functions that act on
2397 non-scalar arrays of fixed length. For example, you can use it for a
2398 vectorized calculation of Pearson correlation coefficient and its p-value:
2399
2400 >>> import scipy.stats
2401 >>> pearsonr = np.vectorize(scipy.stats.pearsonr,
2402 ... signature='(n),(n)->(),()')
2403 >>> pearsonr([[0, 1, 2, 3]], [[1, 2, 3, 4], [4, 3, 2, 1]])
2404 (array([ 1., -1.]), array([ 0., 0.]))
2405
2406 Or for a vectorized convolution:
2407
2408 >>> convolve = np.vectorize(np.convolve, signature='(n),(m)->(k)')
2409 >>> convolve(np.eye(4), [1, 2, 1])
2410 array([[1., 2., 1., 0., 0., 0.],
2411 [0., 1., 2., 1., 0., 0.],
2412 [0., 0., 1., 2., 1., 0.],
2413 [0., 0., 0., 1., 2., 1.]])
2414
2415 Decorator syntax is supported. The decorator can be called as
2416 a function to provide keyword arguments:
2417
2418 >>> @np.vectorize
2419 ... def identity(x):
2420 ... return x
2421 ...
2422 >>> identity([0, 1, 2])
2423 array([0, 1, 2])
2424 >>> @np.vectorize(otypes=[float])
2425 ... def as_float(x):
2426 ... return x
2427 ...
2428 >>> as_float([0, 1, 2])
2429 array([0., 1., 2.])
2430 """
2431 def __init__(self, pyfunc=np._NoValue, otypes=None, doc=None,
2432 excluded=None, cache=False, signature=None):
2433
2434 if (pyfunc != np._NoValue) and (not callable(pyfunc)):
2435 # Splitting the error message to keep
2436 # the length below 79 characters.
2437 part1 = "When used as a decorator, "
2438 part2 = "only accepts keyword arguments."
2439 raise TypeError(part1 + part2)
2440
2441 self.pyfunc = pyfunc
2442 self.cache = cache
2443 self.signature = signature
2444 if pyfunc != np._NoValue and hasattr(pyfunc, '__name__'):
2445 self.__name__ = pyfunc.__name__
2446
2447 self._ufunc = {} # Caching to improve default performance
2448 self._doc = None
2449 self.__doc__ = doc
2450 if doc is None and hasattr(pyfunc, '__doc__'):
2451 self.__doc__ = pyfunc.__doc__
2452 else:
2453 self._doc = doc
2454
2455 if isinstance(otypes, str):
2456 for char in otypes:
2457 if char not in typecodes['All']:
2458 raise ValueError(f"Invalid otype specified: {char}")
2459 elif iterable(otypes):
2460 otypes = [_get_vectorize_dtype(_nx.dtype(x)) for x in otypes]
2461 elif otypes is not None:
2462 raise ValueError("Invalid otype specification")
2463 self.otypes = otypes
2464
2465 # Excluded variable support
2466 if excluded is None:
2467 excluded = set()
2468 self.excluded = set(excluded)
2469
2470 if signature is not None:
2471 self._in_and_out_core_dims = _parse_gufunc_signature(signature)
2472 else:
2473 self._in_and_out_core_dims = None
2474
2475 def _init_stage_2(self, pyfunc, *args, **kwargs):
2476 self.__name__ = pyfunc.__name__
2477 self.pyfunc = pyfunc
2478 if self._doc is None:
2479 self.__doc__ = pyfunc.__doc__
2480 else:
2481 self.__doc__ = self._doc
2482
2483 def _call_as_normal(self, *args, **kwargs):
2484 """
2485 Return arrays with the results of `pyfunc` broadcast (vectorized) over
2486 `args` and `kwargs` not in `excluded`.
2487 """
2488 excluded = self.excluded
2489 if not kwargs and not excluded:
2490 func = self.pyfunc
2491 vargs = args
2492 else:
2493 # The wrapper accepts only positional arguments: we use `names` and
2494 # `inds` to mutate `the_args` and `kwargs` to pass to the original
2495 # function.
2496 nargs = len(args)
2497
2498 names = [_n for _n in kwargs if _n not in excluded]
2499 inds = [_i for _i in range(nargs) if _i not in excluded]
2500 the_args = list(args)
2501
2502 def func(*vargs):
2503 for _n, _i in enumerate(inds):
2504 the_args[_i] = vargs[_n]
2505 kwargs.update(zip(names, vargs[len(inds):]))
2506 return self.pyfunc(*the_args, **kwargs)
2507
2508 vargs = [args[_i] for _i in inds]
2509 vargs.extend([kwargs[_n] for _n in names])
2510
2511 return self._vectorize_call(func=func, args=vargs)
2512
2513 def __call__(self, *args, **kwargs):
2514 if self.pyfunc is np._NoValue:
2515 self._init_stage_2(*args, **kwargs)
2516 return self
2517
2518 return self._call_as_normal(*args, **kwargs)
2519
2520 def _get_ufunc_and_otypes(self, func, args):
2521 """Return (ufunc, otypes)."""
2522 # frompyfunc will fail if args is empty
2523 if not args:
2524 raise ValueError('args can not be empty')
2525
2526 if self.otypes is not None:
2527 otypes = self.otypes
2528
2529 # self._ufunc is a dictionary whose keys are the number of
2530 # arguments (i.e. len(args)) and whose values are ufuncs created
2531 # by frompyfunc. len(args) can be different for different calls if
2532 # self.pyfunc has parameters with default values. We only use the
2533 # cache when func is self.pyfunc, which occurs when the call uses
2534 # only positional arguments and no arguments are excluded.
2535
2536 nin = len(args)
2537 nout = len(self.otypes)
2538 if func is not self.pyfunc or nin not in self._ufunc:
2539 ufunc = frompyfunc(func, nin, nout)
2540 else:
2541 ufunc = None # We'll get it from self._ufunc
2542 if func is self.pyfunc:
2543 ufunc = self._ufunc.setdefault(nin, ufunc)
2544 else:
2545 # Get number of outputs and output types by calling the function on
2546 # the first entries of args. We also cache the result to prevent
2547 # the subsequent call when the ufunc is evaluated.
2548 # Assumes that ufunc first evaluates the 0th elements in the input
2549 # arrays (the input values are not checked to ensure this)
2550 args = [asarray(a) for a in args]
2551 if builtins.any(arg.size == 0 for arg in args):
2552 raise ValueError('cannot call `vectorize` on size 0 inputs '
2553 'unless `otypes` is set')
2554
2555 inputs = [arg.flat[0] for arg in args]
2556 outputs = func(*inputs)
2557
2558 # Performance note: profiling indicates that -- for simple
2559 # functions at least -- this wrapping can almost double the
2560 # execution time.
2561 # Hence we make it optional.
2562 if self.cache:
2563 _cache = [outputs]
2564
2565 def _func(*vargs):
2566 if _cache:
2567 return _cache.pop()
2568 else:
2569 return func(*vargs)
2570 else:
2571 _func = func
2572
2573 if isinstance(outputs, tuple):
2574 nout = len(outputs)
2575 else:
2576 nout = 1
2577 outputs = (outputs,)
2578
2579 otypes = ''.join([asarray(outputs[_k]).dtype.char
2580 for _k in range(nout)])
2581
2582 # Performance note: profiling indicates that creating the ufunc is
2583 # not a significant cost compared with wrapping so it seems not
2584 # worth trying to cache this.
2585 ufunc = frompyfunc(_func, len(args), nout)
2586
2587 return ufunc, otypes
2588
2589 def _vectorize_call(self, func, args):
2590 """Vectorized call to `func` over positional `args`."""
2591 if self.signature is not None:
2592 res = self._vectorize_call_with_signature(func, args)
2593 elif not args:
2594 res = func()
2595 else:
2596 ufunc, otypes = self._get_ufunc_and_otypes(func=func, args=args)
2597 # gh-29196: `dtype=object` should eventually be removed
2598 args = [asanyarray(a, dtype=object) for a in args]
2599 outputs = ufunc(*args, out=...)
2600
2601 if ufunc.nout == 1:
2602 res = asanyarray(outputs, dtype=otypes[0])
2603 else:
2604 res = tuple(asanyarray(x, dtype=t)
2605 for x, t in zip(outputs, otypes))
2606 return res
2607
2608 def _vectorize_call_with_signature(self, func, args):
2609 """Vectorized call over positional arguments with a signature."""
2610 input_core_dims, output_core_dims = self._in_and_out_core_dims
2611
2612 if len(args) != len(input_core_dims):
2613 raise TypeError('wrong number of positional arguments: '
2614 'expected %r, got %r'
2615 % (len(input_core_dims), len(args)))
2616 args = tuple(asanyarray(arg) for arg in args)
2617
2618 broadcast_shape, dim_sizes = _parse_input_dimensions(
2619 args, input_core_dims)
2620 input_shapes = _calculate_shapes(broadcast_shape, dim_sizes,
2621 input_core_dims)
2622 args = [np.broadcast_to(arg, shape, subok=True)
2623 for arg, shape in zip(args, input_shapes)]
2624
2625 outputs = None
2626 otypes = self.otypes
2627 nout = len(output_core_dims)
2628
2629 for index in np.ndindex(*broadcast_shape):
2630 results = func(*(arg[index] for arg in args))
2631
2632 n_results = len(results) if isinstance(results, tuple) else 1
2633
2634 if nout != n_results:
2635 raise ValueError(
2636 'wrong number of outputs from pyfunc: expected %r, got %r'
2637 % (nout, n_results))
2638
2639 if nout == 1:
2640 results = (results,)
2641
2642 if outputs is None:
2643 for result, core_dims in zip(results, output_core_dims):
2644 _update_dim_sizes(dim_sizes, result, core_dims)
2645
2646 outputs = _create_arrays(broadcast_shape, dim_sizes,
2647 output_core_dims, otypes, results)
2648
2649 for output, result in zip(outputs, results):
2650 output[index] = result
2651
2652 if outputs is None:
2653 # did not call the function even once
2654 if otypes is None:
2655 raise ValueError('cannot call `vectorize` on size 0 inputs '
2656 'unless `otypes` is set')
2657 if builtins.any(dim not in dim_sizes
2658 for dims in output_core_dims
2659 for dim in dims):
2660 raise ValueError('cannot call `vectorize` with a signature '
2661 'including new output dimensions on size 0 '
2662 'inputs')
2663 outputs = _create_arrays(broadcast_shape, dim_sizes,
2664 output_core_dims, otypes)
2665
2666 return outputs[0] if nout == 1 else outputs
2667
2668
2669def _cov_dispatcher(m, y=None, rowvar=None, bias=None, ddof=None,
2670 fweights=None, aweights=None, *, dtype=None):
2671 return (m, y, fweights, aweights)
2672
2673
2674@array_function_dispatch(_cov_dispatcher)
2675def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None,
2676 aweights=None, *, dtype=None):
2677 """
2678 Estimate a covariance matrix, given data and weights.
2679
2680 Covariance indicates the level to which two variables vary together.
2681 If we examine N-dimensional samples, :math:`X = [x_1, x_2, ..., x_N]^T`,
2682 then the covariance matrix element :math:`C_{ij}` is the covariance of
2683 :math:`x_i` and :math:`x_j`. The element :math:`C_{ii}` is the variance
2684 of :math:`x_i`.
2685
2686 See the notes for an outline of the algorithm.
2687
2688 Parameters
2689 ----------
2690 m : array_like
2691 A 1-D or 2-D array containing multiple variables and observations.
2692 Each row of `m` represents a variable, and each column a single
2693 observation of all those variables. Also see `rowvar` below.
2694 y : array_like, optional
2695 An additional set of variables and observations. `y` has the same form
2696 as that of `m`.
2697 rowvar : bool, optional
2698 If `rowvar` is True (default), then each row represents a
2699 variable, with observations in the columns. Otherwise, the relationship
2700 is transposed: each column represents a variable, while the rows
2701 contain observations.
2702 bias : bool, optional
2703 Default normalization (False) is by ``(N - 1)``, where ``N`` is the
2704 number of observations given (unbiased estimate). If `bias` is True,
2705 then normalization is by ``N``. These values can be overridden by using
2706 the keyword ``ddof`` in numpy versions >= 1.5.
2707 ddof : int, optional
2708 If not ``None`` the default value implied by `bias` is overridden.
2709 Note that ``ddof=1`` will return the unbiased estimate, even if both
2710 `fweights` and `aweights` are specified, and ``ddof=0`` will return
2711 the simple average. See the notes for the details. The default value
2712 is ``None``.
2713 fweights : array_like, int, optional
2714 1-D array of integer frequency weights; the number of times each
2715 observation vector should be repeated.
2716 aweights : array_like, optional
2717 1-D array of observation vector weights. These relative weights are
2718 typically large for observations considered "important" and smaller for
2719 observations considered less "important". If ``ddof=0`` the array of
2720 weights can be used to assign probabilities to observation vectors.
2721 dtype : data-type, optional
2722 Data-type of the result. By default, the return data-type will have
2723 at least `numpy.float64` precision.
2724
2725 .. versionadded:: 1.20
2726
2727 Returns
2728 -------
2729 out : ndarray
2730 The covariance matrix of the variables.
2731
2732 See Also
2733 --------
2734 corrcoef : Normalized covariance matrix
2735
2736 Notes
2737 -----
2738 Assume that the observations are in the columns of the observation
2739 array `m` and let ``f = fweights`` and ``a = aweights`` for brevity. The
2740 steps to compute the weighted covariance are as follows::
2741
2742 >>> m = np.arange(10, dtype=np.float64)
2743 >>> f = np.arange(10) * 2
2744 >>> a = np.arange(10) ** 2.
2745 >>> ddof = 1
2746 >>> w = f * a
2747 >>> v1 = np.sum(w)
2748 >>> v2 = np.sum(w * a)
2749 >>> m -= np.sum(m * w, axis=None, keepdims=True) / v1
2750 >>> cov = np.dot(m * w, m.T) * v1 / (v1**2 - ddof * v2)
2751
2752 Note that when ``a == 1``, the normalization factor
2753 ``v1 / (v1**2 - ddof * v2)`` goes over to ``1 / (np.sum(f) - ddof)``
2754 as it should.
2755
2756 Examples
2757 --------
2758 >>> import numpy as np
2759
2760 Consider two variables, :math:`x_0` and :math:`x_1`, which
2761 correlate perfectly, but in opposite directions:
2762
2763 >>> x = np.array([[0, 2], [1, 1], [2, 0]]).T
2764 >>> x
2765 array([[0, 1, 2],
2766 [2, 1, 0]])
2767
2768 Note how :math:`x_0` increases while :math:`x_1` decreases. The covariance
2769 matrix shows this clearly:
2770
2771 >>> np.cov(x)
2772 array([[ 1., -1.],
2773 [-1., 1.]])
2774
2775 Note that element :math:`C_{0,1}`, which shows the correlation between
2776 :math:`x_0` and :math:`x_1`, is negative.
2777
2778 Further, note how `x` and `y` are combined:
2779
2780 >>> x = [-2.1, -1, 4.3]
2781 >>> y = [3, 1.1, 0.12]
2782 >>> X = np.stack((x, y), axis=0)
2783 >>> np.cov(X)
2784 array([[11.71 , -4.286 ], # may vary
2785 [-4.286 , 2.144133]])
2786 >>> np.cov(x, y)
2787 array([[11.71 , -4.286 ], # may vary
2788 [-4.286 , 2.144133]])
2789 >>> np.cov(x)
2790 array(11.71)
2791
2792 """
2793 # Check inputs
2794 if ddof is not None and ddof != int(ddof):
2795 raise ValueError(
2796 "ddof must be integer")
2797
2798 # Handles complex arrays too
2799 m = np.asarray(m)
2800 if m.ndim > 2:
2801 raise ValueError("m has more than 2 dimensions")
2802
2803 if y is not None:
2804 y = np.asarray(y)
2805 if y.ndim > 2:
2806 raise ValueError("y has more than 2 dimensions")
2807
2808 if dtype is None:
2809 if y is None:
2810 dtype = np.result_type(m, np.float64)
2811 else:
2812 dtype = np.result_type(m, y, np.float64)
2813
2814 X = array(m, ndmin=2, dtype=dtype)
2815 if not rowvar and m.ndim != 1:
2816 X = X.T
2817 if X.shape[0] == 0:
2818 return np.array([]).reshape(0, 0)
2819 if y is not None:
2820 y = array(y, copy=None, ndmin=2, dtype=dtype)
2821 if not rowvar and y.shape[0] != 1:
2822 y = y.T
2823 X = np.concatenate((X, y), axis=0)
2824
2825 if ddof is None:
2826 if bias == 0:
2827 ddof = 1
2828 else:
2829 ddof = 0
2830
2831 # Get the product of frequencies and weights
2832 w = None
2833 if fweights is not None:
2834 fweights = np.asarray(fweights, dtype=float)
2835 if not np.all(fweights == np.around(fweights)):
2836 raise TypeError(
2837 "fweights must be integer")
2838 if fweights.ndim > 1:
2839 raise RuntimeError(
2840 "cannot handle multidimensional fweights")
2841 if fweights.shape[0] != X.shape[1]:
2842 raise RuntimeError(
2843 "incompatible numbers of samples and fweights")
2844 if any(fweights < 0):
2845 raise ValueError(
2846 "fweights cannot be negative")
2847 w = fweights
2848 if aweights is not None:
2849 aweights = np.asarray(aweights, dtype=float)
2850 if aweights.ndim > 1:
2851 raise RuntimeError(
2852 "cannot handle multidimensional aweights")
2853 if aweights.shape[0] != X.shape[1]:
2854 raise RuntimeError(
2855 "incompatible numbers of samples and aweights")
2856 if any(aweights < 0):
2857 raise ValueError(
2858 "aweights cannot be negative")
2859 if w is None:
2860 w = aweights
2861 else:
2862 w *= aweights
2863
2864 avg, w_sum = average(X, axis=1, weights=w, returned=True)
2865 w_sum = w_sum[0]
2866
2867 # Determine the normalization
2868 if w is None:
2869 fact = X.shape[1] - ddof
2870 elif ddof == 0:
2871 fact = w_sum
2872 elif aweights is None:
2873 fact = w_sum - ddof
2874 else:
2875 fact = w_sum - ddof * sum(w * aweights) / w_sum
2876
2877 if fact <= 0:
2878 warnings.warn("Degrees of freedom <= 0 for slice",
2879 RuntimeWarning, stacklevel=2)
2880 fact = 0.0
2881
2882 X -= avg[:, None]
2883 if w is None:
2884 X_T = X.T
2885 else:
2886 X_T = (X * w).T
2887 c = dot(X, X_T.conj())
2888 c *= np.true_divide(1, fact)
2889 return c.squeeze()
2890
2891
2892def _corrcoef_dispatcher(x, y=None, rowvar=None, *,
2893 dtype=None):
2894 return (x, y)
2895
2896
2897@array_function_dispatch(_corrcoef_dispatcher)
2898def corrcoef(x, y=None, rowvar=True, *,
2899 dtype=None):
2900 """
2901 Return Pearson product-moment correlation coefficients.
2902
2903 Please refer to the documentation for `cov` for more detail. The
2904 relationship between the correlation coefficient matrix, `R`, and the
2905 covariance matrix, `C`, is
2906
2907 .. math:: R_{ij} = \\frac{ C_{ij} } { \\sqrt{ C_{ii} C_{jj} } }
2908
2909 The values of `R` are between -1 and 1, inclusive.
2910
2911 Parameters
2912 ----------
2913 x : array_like
2914 A 1-D or 2-D array containing multiple variables and observations.
2915 Each row of `x` represents a variable, and each column a single
2916 observation of all those variables. Also see `rowvar` below.
2917 y : array_like, optional
2918 An additional set of variables and observations. `y` has the same
2919 shape as `x`.
2920 rowvar : bool, optional
2921 If `rowvar` is True (default), then each row represents a
2922 variable, with observations in the columns. Otherwise, the relationship
2923 is transposed: each column represents a variable, while the rows
2924 contain observations.
2925
2926 dtype : data-type, optional
2927 Data-type of the result. By default, the return data-type will have
2928 at least `numpy.float64` precision.
2929
2930 .. versionadded:: 1.20
2931
2932 Returns
2933 -------
2934 R : ndarray
2935 The correlation coefficient matrix of the variables.
2936
2937 See Also
2938 --------
2939 cov : Covariance matrix
2940
2941 Notes
2942 -----
2943 Due to floating point rounding the resulting array may not be Hermitian,
2944 the diagonal elements may not be 1, and the elements may not satisfy the
2945 inequality abs(a) <= 1. The real and imaginary parts are clipped to the
2946 interval [-1, 1] in an attempt to improve on that situation but is not
2947 much help in the complex case.
2948
2949 Examples
2950 --------
2951 >>> import numpy as np
2952
2953 In this example we generate two random arrays, ``xarr`` and ``yarr``, and
2954 compute the row-wise and column-wise Pearson correlation coefficients,
2955 ``R``. Since ``rowvar`` is true by default, we first find the row-wise
2956 Pearson correlation coefficients between the variables of ``xarr``.
2957
2958 >>> import numpy as np
2959 >>> rng = np.random.default_rng(seed=42)
2960 >>> xarr = rng.random((3, 3))
2961 >>> xarr
2962 array([[0.77395605, 0.43887844, 0.85859792],
2963 [0.69736803, 0.09417735, 0.97562235],
2964 [0.7611397 , 0.78606431, 0.12811363]])
2965 >>> R1 = np.corrcoef(xarr)
2966 >>> R1
2967 array([[ 1. , 0.99256089, -0.68080986],
2968 [ 0.99256089, 1. , -0.76492172],
2969 [-0.68080986, -0.76492172, 1. ]])
2970
2971 If we add another set of variables and observations ``yarr``, we can
2972 compute the row-wise Pearson correlation coefficients between the
2973 variables in ``xarr`` and ``yarr``.
2974
2975 >>> yarr = rng.random((3, 3))
2976 >>> yarr
2977 array([[0.45038594, 0.37079802, 0.92676499],
2978 [0.64386512, 0.82276161, 0.4434142 ],
2979 [0.22723872, 0.55458479, 0.06381726]])
2980 >>> R2 = np.corrcoef(xarr, yarr)
2981 >>> R2
2982 array([[ 1. , 0.99256089, -0.68080986, 0.75008178, -0.934284 ,
2983 -0.99004057],
2984 [ 0.99256089, 1. , -0.76492172, 0.82502011, -0.97074098,
2985 -0.99981569],
2986 [-0.68080986, -0.76492172, 1. , -0.99507202, 0.89721355,
2987 0.77714685],
2988 [ 0.75008178, 0.82502011, -0.99507202, 1. , -0.93657855,
2989 -0.83571711],
2990 [-0.934284 , -0.97074098, 0.89721355, -0.93657855, 1. ,
2991 0.97517215],
2992 [-0.99004057, -0.99981569, 0.77714685, -0.83571711, 0.97517215,
2993 1. ]])
2994
2995 Finally if we use the option ``rowvar=False``, the columns are now
2996 being treated as the variables and we will find the column-wise Pearson
2997 correlation coefficients between variables in ``xarr`` and ``yarr``.
2998
2999 >>> R3 = np.corrcoef(xarr, yarr, rowvar=False)
3000 >>> R3
3001 array([[ 1. , 0.77598074, -0.47458546, -0.75078643, -0.9665554 ,
3002 0.22423734],
3003 [ 0.77598074, 1. , -0.92346708, -0.99923895, -0.58826587,
3004 -0.44069024],
3005 [-0.47458546, -0.92346708, 1. , 0.93773029, 0.23297648,
3006 0.75137473],
3007 [-0.75078643, -0.99923895, 0.93773029, 1. , 0.55627469,
3008 0.47536961],
3009 [-0.9665554 , -0.58826587, 0.23297648, 0.55627469, 1. ,
3010 -0.46666491],
3011 [ 0.22423734, -0.44069024, 0.75137473, 0.47536961, -0.46666491,
3012 1. ]])
3013
3014 """
3015 c = cov(x, y, rowvar, dtype=dtype)
3016 try:
3017 d = diag(c)
3018 except ValueError:
3019 # scalar covariance
3020 # nan if incorrect value (nan, inf, 0), 1 otherwise
3021 return c / c
3022 stddev = sqrt(d.real)
3023 c /= stddev[:, None]
3024 c /= stddev[None, :]
3025
3026 # Clip real and imaginary parts to [-1, 1]. This does not guarantee
3027 # abs(a[i,j]) <= 1 for complex arrays, but is the best we can do without
3028 # excessive work.
3029 np.clip(c.real, -1, 1, out=c.real)
3030 if np.iscomplexobj(c):
3031 np.clip(c.imag, -1, 1, out=c.imag)
3032
3033 return c
3034
3035
3036@set_module('numpy')
3037def blackman(M):
3038 """
3039 Return the Blackman window.
3040
3041 The Blackman window is a taper formed by using the first three
3042 terms of a summation of cosines. It was designed to have close to the
3043 minimal leakage possible. It is close to optimal, only slightly worse
3044 than a Kaiser window.
3045
3046 Parameters
3047 ----------
3048 M : int
3049 Number of points in the output window. If zero or less, an empty
3050 array is returned.
3051
3052 Returns
3053 -------
3054 out : ndarray
3055 The window, with the maximum value normalized to one (the value one
3056 appears only if the number of samples is odd).
3057
3058 See Also
3059 --------
3060 bartlett, hamming, hanning, kaiser
3061
3062 Notes
3063 -----
3064 The Blackman window is defined as
3065
3066 .. math:: w(n) = 0.42 - 0.5 \\cos(2\\pi n/M) + 0.08 \\cos(4\\pi n/M)
3067
3068 Most references to the Blackman window come from the signal processing
3069 literature, where it is used as one of many windowing functions for
3070 smoothing values. It is also known as an apodization (which means
3071 "removing the foot", i.e. smoothing discontinuities at the beginning
3072 and end of the sampled signal) or tapering function. It is known as a
3073 "near optimal" tapering function, almost as good (by some measures)
3074 as the kaiser window.
3075
3076 References
3077 ----------
3078 Blackman, R.B. and Tukey, J.W., (1958) The measurement of power spectra,
3079 Dover Publications, New York.
3080
3081 Oppenheim, A.V., and R.W. Schafer. Discrete-Time Signal Processing.
3082 Upper Saddle River, NJ: Prentice-Hall, 1999, pp. 468-471.
3083
3084 Examples
3085 --------
3086 >>> import numpy as np
3087 >>> import matplotlib.pyplot as plt
3088 >>> np.blackman(12)
3089 array([-1.38777878e-17, 3.26064346e-02, 1.59903635e-01, # may vary
3090 4.14397981e-01, 7.36045180e-01, 9.67046769e-01,
3091 9.67046769e-01, 7.36045180e-01, 4.14397981e-01,
3092 1.59903635e-01, 3.26064346e-02, -1.38777878e-17])
3093
3094 Plot the window and the frequency response.
3095
3096 .. plot::
3097 :include-source:
3098
3099 import matplotlib.pyplot as plt
3100 from numpy.fft import fft, fftshift
3101 window = np.blackman(51)
3102 plt.plot(window)
3103 plt.title("Blackman window")
3104 plt.ylabel("Amplitude")
3105 plt.xlabel("Sample")
3106 plt.show() # doctest: +SKIP
3107
3108 plt.figure()
3109 A = fft(window, 2048) / 25.5
3110 mag = np.abs(fftshift(A))
3111 freq = np.linspace(-0.5, 0.5, len(A))
3112 with np.errstate(divide='ignore', invalid='ignore'):
3113 response = 20 * np.log10(mag)
3114 response = np.clip(response, -100, 100)
3115 plt.plot(freq, response)
3116 plt.title("Frequency response of Blackman window")
3117 plt.ylabel("Magnitude [dB]")
3118 plt.xlabel("Normalized frequency [cycles per sample]")
3119 plt.axis('tight')
3120 plt.show()
3121
3122 """
3123 # Ensures at least float64 via 0.0. M should be an integer, but conversion
3124 # to double is safe for a range.
3125 values = np.array([0.0, M])
3126 M = values[1]
3127
3128 if M < 1:
3129 return array([], dtype=values.dtype)
3130 if M == 1:
3131 return ones(1, dtype=values.dtype)
3132 n = arange(1 - M, M, 2)
3133 return 0.42 + 0.5 * cos(pi * n / (M - 1)) + 0.08 * cos(2.0 * pi * n / (M - 1))
3134
3135
3136@set_module('numpy')
3137def bartlett(M):
3138 """
3139 Return the Bartlett window.
3140
3141 The Bartlett window is very similar to a triangular window, except
3142 that the end points are at zero. It is often used in signal
3143 processing for tapering a signal, without generating too much
3144 ripple in the frequency domain.
3145
3146 Parameters
3147 ----------
3148 M : int
3149 Number of points in the output window. If zero or less, an
3150 empty array is returned.
3151
3152 Returns
3153 -------
3154 out : array
3155 The triangular window, with the maximum value normalized to one
3156 (the value one appears only if the number of samples is odd), with
3157 the first and last samples equal to zero.
3158
3159 See Also
3160 --------
3161 blackman, hamming, hanning, kaiser
3162
3163 Notes
3164 -----
3165 The Bartlett window is defined as
3166
3167 .. math:: w(n) = \\frac{2}{M-1} \\left(
3168 \\frac{M-1}{2} - \\left|n - \\frac{M-1}{2}\\right|
3169 \\right)
3170
3171 Most references to the Bartlett window come from the signal processing
3172 literature, where it is used as one of many windowing functions for
3173 smoothing values. Note that convolution with this window produces linear
3174 interpolation. It is also known as an apodization (which means "removing
3175 the foot", i.e. smoothing discontinuities at the beginning and end of the
3176 sampled signal) or tapering function. The Fourier transform of the
3177 Bartlett window is the product of two sinc functions. Note the excellent
3178 discussion in Kanasewich [2]_.
3179
3180 References
3181 ----------
3182 .. [1] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
3183 Biometrika 37, 1-16, 1950.
3184 .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics",
3185 The University of Alberta Press, 1975, pp. 109-110.
3186 .. [3] A.V. Oppenheim and R.W. Schafer, "Discrete-Time Signal
3187 Processing", Prentice-Hall, 1999, pp. 468-471.
3188 .. [4] Wikipedia, "Window function",
3189 https://en.wikipedia.org/wiki/Window_function
3190 .. [5] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling,
3191 "Numerical Recipes", Cambridge University Press, 1986, page 429.
3192
3193 Examples
3194 --------
3195 >>> import numpy as np
3196 >>> import matplotlib.pyplot as plt
3197 >>> np.bartlett(12)
3198 array([ 0. , 0.18181818, 0.36363636, 0.54545455, 0.72727273, # may vary
3199 0.90909091, 0.90909091, 0.72727273, 0.54545455, 0.36363636,
3200 0.18181818, 0. ])
3201
3202 Plot the window and its frequency response (requires SciPy and matplotlib).
3203
3204 .. plot::
3205 :include-source:
3206
3207 import matplotlib.pyplot as plt
3208 from numpy.fft import fft, fftshift
3209 window = np.bartlett(51)
3210 plt.plot(window)
3211 plt.title("Bartlett window")
3212 plt.ylabel("Amplitude")
3213 plt.xlabel("Sample")
3214 plt.show()
3215 plt.figure()
3216 A = fft(window, 2048) / 25.5
3217 mag = np.abs(fftshift(A))
3218 freq = np.linspace(-0.5, 0.5, len(A))
3219 with np.errstate(divide='ignore', invalid='ignore'):
3220 response = 20 * np.log10(mag)
3221 response = np.clip(response, -100, 100)
3222 plt.plot(freq, response)
3223 plt.title("Frequency response of Bartlett window")
3224 plt.ylabel("Magnitude [dB]")
3225 plt.xlabel("Normalized frequency [cycles per sample]")
3226 plt.axis('tight')
3227 plt.show()
3228
3229 """
3230 # Ensures at least float64 via 0.0. M should be an integer, but conversion
3231 # to double is safe for a range.
3232 values = np.array([0.0, M])
3233 M = values[1]
3234
3235 if M < 1:
3236 return array([], dtype=values.dtype)
3237 if M == 1:
3238 return ones(1, dtype=values.dtype)
3239 n = arange(1 - M, M, 2)
3240 return where(less_equal(n, 0), 1 + n / (M - 1), 1 - n / (M - 1))
3241
3242
3243@set_module('numpy')
3244def hanning(M):
3245 """
3246 Return the Hanning window.
3247
3248 The Hanning window is a taper formed by using a weighted cosine.
3249
3250 Parameters
3251 ----------
3252 M : int
3253 Number of points in the output window. If zero or less, an
3254 empty array is returned.
3255
3256 Returns
3257 -------
3258 out : ndarray, shape(M,)
3259 The window, with the maximum value normalized to one (the value
3260 one appears only if `M` is odd).
3261
3262 See Also
3263 --------
3264 bartlett, blackman, hamming, kaiser
3265
3266 Notes
3267 -----
3268 The Hanning window is defined as
3269
3270 .. math:: w(n) = 0.5 - 0.5\\cos\\left(\\frac{2\\pi{n}}{M-1}\\right)
3271 \\qquad 0 \\leq n \\leq M-1
3272
3273 The Hanning was named for Julius von Hann, an Austrian meteorologist.
3274 It is also known as the Cosine Bell. Some authors prefer that it be
3275 called a Hann window, to help avoid confusion with the very similar
3276 Hamming window.
3277
3278 Most references to the Hanning window come from the signal processing
3279 literature, where it is used as one of many windowing functions for
3280 smoothing values. It is also known as an apodization (which means
3281 "removing the foot", i.e. smoothing discontinuities at the beginning
3282 and end of the sampled signal) or tapering function.
3283
3284 References
3285 ----------
3286 .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power
3287 spectra, Dover Publications, New York.
3288 .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics",
3289 The University of Alberta Press, 1975, pp. 106-108.
3290 .. [3] Wikipedia, "Window function",
3291 https://en.wikipedia.org/wiki/Window_function
3292 .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling,
3293 "Numerical Recipes", Cambridge University Press, 1986, page 425.
3294
3295 Examples
3296 --------
3297 >>> import numpy as np
3298 >>> np.hanning(12)
3299 array([0. , 0.07937323, 0.29229249, 0.57115742, 0.82743037,
3300 0.97974649, 0.97974649, 0.82743037, 0.57115742, 0.29229249,
3301 0.07937323, 0. ])
3302
3303 Plot the window and its frequency response.
3304
3305 .. plot::
3306 :include-source:
3307
3308 import matplotlib.pyplot as plt
3309 from numpy.fft import fft, fftshift
3310 window = np.hanning(51)
3311 plt.plot(window)
3312 plt.title("Hann window")
3313 plt.ylabel("Amplitude")
3314 plt.xlabel("Sample")
3315 plt.show()
3316
3317 plt.figure()
3318 A = fft(window, 2048) / 25.5
3319 mag = np.abs(fftshift(A))
3320 freq = np.linspace(-0.5, 0.5, len(A))
3321 with np.errstate(divide='ignore', invalid='ignore'):
3322 response = 20 * np.log10(mag)
3323 response = np.clip(response, -100, 100)
3324 plt.plot(freq, response)
3325 plt.title("Frequency response of the Hann window")
3326 plt.ylabel("Magnitude [dB]")
3327 plt.xlabel("Normalized frequency [cycles per sample]")
3328 plt.axis('tight')
3329 plt.show()
3330
3331 """
3332 # Ensures at least float64 via 0.0. M should be an integer, but conversion
3333 # to double is safe for a range.
3334 values = np.array([0.0, M])
3335 M = values[1]
3336
3337 if M < 1:
3338 return array([], dtype=values.dtype)
3339 if M == 1:
3340 return ones(1, dtype=values.dtype)
3341 n = arange(1 - M, M, 2)
3342 return 0.5 + 0.5 * cos(pi * n / (M - 1))
3343
3344
3345@set_module('numpy')
3346def hamming(M):
3347 """
3348 Return the Hamming window.
3349
3350 The Hamming window is a taper formed by using a weighted cosine.
3351
3352 Parameters
3353 ----------
3354 M : int
3355 Number of points in the output window. If zero or less, an
3356 empty array is returned.
3357
3358 Returns
3359 -------
3360 out : ndarray
3361 The window, with the maximum value normalized to one (the value
3362 one appears only if the number of samples is odd).
3363
3364 See Also
3365 --------
3366 bartlett, blackman, hanning, kaiser
3367
3368 Notes
3369 -----
3370 The Hamming window is defined as
3371
3372 .. math:: w(n) = 0.54 - 0.46\\cos\\left(\\frac{2\\pi{n}}{M-1}\\right)
3373 \\qquad 0 \\leq n \\leq M-1
3374
3375 The Hamming was named for R. W. Hamming, an associate of J. W. Tukey
3376 and is described in Blackman and Tukey. It was recommended for
3377 smoothing the truncated autocovariance function in the time domain.
3378 Most references to the Hamming window come from the signal processing
3379 literature, where it is used as one of many windowing functions for
3380 smoothing values. It is also known as an apodization (which means
3381 "removing the foot", i.e. smoothing discontinuities at the beginning
3382 and end of the sampled signal) or tapering function.
3383
3384 References
3385 ----------
3386 .. [1] Blackman, R.B. and Tukey, J.W., (1958) The measurement of power
3387 spectra, Dover Publications, New York.
3388 .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The
3389 University of Alberta Press, 1975, pp. 109-110.
3390 .. [3] Wikipedia, "Window function",
3391 https://en.wikipedia.org/wiki/Window_function
3392 .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling,
3393 "Numerical Recipes", Cambridge University Press, 1986, page 425.
3394
3395 Examples
3396 --------
3397 >>> import numpy as np
3398 >>> np.hamming(12)
3399 array([ 0.08 , 0.15302337, 0.34890909, 0.60546483, 0.84123594, # may vary
3400 0.98136677, 0.98136677, 0.84123594, 0.60546483, 0.34890909,
3401 0.15302337, 0.08 ])
3402
3403 Plot the window and the frequency response.
3404
3405 .. plot::
3406 :include-source:
3407
3408 import matplotlib.pyplot as plt
3409 from numpy.fft import fft, fftshift
3410 window = np.hamming(51)
3411 plt.plot(window)
3412 plt.title("Hamming window")
3413 plt.ylabel("Amplitude")
3414 plt.xlabel("Sample")
3415 plt.show()
3416
3417 plt.figure()
3418 A = fft(window, 2048) / 25.5
3419 mag = np.abs(fftshift(A))
3420 freq = np.linspace(-0.5, 0.5, len(A))
3421 response = 20 * np.log10(mag)
3422 response = np.clip(response, -100, 100)
3423 plt.plot(freq, response)
3424 plt.title("Frequency response of Hamming window")
3425 plt.ylabel("Magnitude [dB]")
3426 plt.xlabel("Normalized frequency [cycles per sample]")
3427 plt.axis('tight')
3428 plt.show()
3429
3430 """
3431 # Ensures at least float64 via 0.0. M should be an integer, but conversion
3432 # to double is safe for a range.
3433 values = np.array([0.0, M])
3434 M = values[1]
3435
3436 if M < 1:
3437 return array([], dtype=values.dtype)
3438 if M == 1:
3439 return ones(1, dtype=values.dtype)
3440 n = arange(1 - M, M, 2)
3441 return 0.54 + 0.46 * cos(pi * n / (M - 1))
3442
3443
3444## Code from cephes for i0
3445
3446_i0A = [
3447 -4.41534164647933937950E-18,
3448 3.33079451882223809783E-17,
3449 -2.43127984654795469359E-16,
3450 1.71539128555513303061E-15,
3451 -1.16853328779934516808E-14,
3452 7.67618549860493561688E-14,
3453 -4.85644678311192946090E-13,
3454 2.95505266312963983461E-12,
3455 -1.72682629144155570723E-11,
3456 9.67580903537323691224E-11,
3457 -5.18979560163526290666E-10,
3458 2.65982372468238665035E-9,
3459 -1.30002500998624804212E-8,
3460 6.04699502254191894932E-8,
3461 -2.67079385394061173391E-7,
3462 1.11738753912010371815E-6,
3463 -4.41673835845875056359E-6,
3464 1.64484480707288970893E-5,
3465 -5.75419501008210370398E-5,
3466 1.88502885095841655729E-4,
3467 -5.76375574538582365885E-4,
3468 1.63947561694133579842E-3,
3469 -4.32430999505057594430E-3,
3470 1.05464603945949983183E-2,
3471 -2.37374148058994688156E-2,
3472 4.93052842396707084878E-2,
3473 -9.49010970480476444210E-2,
3474 1.71620901522208775349E-1,
3475 -3.04682672343198398683E-1,
3476 6.76795274409476084995E-1
3477 ]
3478
3479_i0B = [
3480 -7.23318048787475395456E-18,
3481 -4.83050448594418207126E-18,
3482 4.46562142029675999901E-17,
3483 3.46122286769746109310E-17,
3484 -2.82762398051658348494E-16,
3485 -3.42548561967721913462E-16,
3486 1.77256013305652638360E-15,
3487 3.81168066935262242075E-15,
3488 -9.55484669882830764870E-15,
3489 -4.15056934728722208663E-14,
3490 1.54008621752140982691E-14,
3491 3.85277838274214270114E-13,
3492 7.18012445138366623367E-13,
3493 -1.79417853150680611778E-12,
3494 -1.32158118404477131188E-11,
3495 -3.14991652796324136454E-11,
3496 1.18891471078464383424E-11,
3497 4.94060238822496958910E-10,
3498 3.39623202570838634515E-9,
3499 2.26666899049817806459E-8,
3500 2.04891858946906374183E-7,
3501 2.89137052083475648297E-6,
3502 6.88975834691682398426E-5,
3503 3.36911647825569408990E-3,
3504 8.04490411014108831608E-1
3505 ]
3506
3507
3508def _chbevl(x, vals):
3509 b0 = vals[0]
3510 b1 = 0.0
3511
3512 for i in range(1, len(vals)):
3513 b2 = b1
3514 b1 = b0
3515 b0 = x * b1 - b2 + vals[i]
3516
3517 return 0.5 * (b0 - b2)
3518
3519
3520def _i0_1(x):
3521 return exp(x) * _chbevl(x / 2.0 - 2, _i0A)
3522
3523
3524def _i0_2(x):
3525 return exp(x) * _chbevl(32.0 / x - 2.0, _i0B) / sqrt(x)
3526
3527
3528def _i0_dispatcher(x):
3529 return (x,)
3530
3531
3532@array_function_dispatch(_i0_dispatcher)
3533def i0(x):
3534 """
3535 Modified Bessel function of the first kind, order 0.
3536
3537 Usually denoted :math:`I_0`.
3538
3539 Parameters
3540 ----------
3541 x : array_like of float
3542 Argument of the Bessel function.
3543
3544 Returns
3545 -------
3546 out : ndarray, shape = x.shape, dtype = float
3547 The modified Bessel function evaluated at each of the elements of `x`.
3548
3549 See Also
3550 --------
3551 scipy.special.i0, scipy.special.iv, scipy.special.ive
3552
3553 Notes
3554 -----
3555 The scipy implementation is recommended over this function: it is a
3556 proper ufunc written in C, and more than an order of magnitude faster.
3557
3558 We use the algorithm published by Clenshaw [1]_ and referenced by
3559 Abramowitz and Stegun [2]_, for which the function domain is
3560 partitioned into the two intervals [0,8] and (8,inf), and Chebyshev
3561 polynomial expansions are employed in each interval. Relative error on
3562 the domain [0,30] using IEEE arithmetic is documented [3]_ as having a
3563 peak of 5.8e-16 with an rms of 1.4e-16 (n = 30000).
3564
3565 References
3566 ----------
3567 .. [1] C. W. Clenshaw, "Chebyshev series for mathematical functions", in
3568 *National Physical Laboratory Mathematical Tables*, vol. 5, London:
3569 Her Majesty's Stationery Office, 1962.
3570 .. [2] M. Abramowitz and I. A. Stegun, *Handbook of Mathematical
3571 Functions*, 10th printing, New York: Dover, 1964, pp. 379.
3572 https://personal.math.ubc.ca/~cbm/aands/page_379.htm
3573 .. [3] https://metacpan.org/pod/distribution/Math-Cephes/lib/Math/Cephes.pod#i0:-Modified-Bessel-function-of-order-zero
3574
3575 Examples
3576 --------
3577 >>> import numpy as np
3578 >>> np.i0(0.)
3579 array(1.0)
3580 >>> np.i0([0, 1, 2, 3])
3581 array([1. , 1.26606588, 2.2795853 , 4.88079259])
3582
3583 """
3584 x = np.asanyarray(x)
3585 if x.dtype.kind == 'c':
3586 raise TypeError("i0 not supported for complex values")
3587 if x.dtype.kind != 'f':
3588 x = x.astype(float)
3589 x = np.abs(x)
3590 return piecewise(x, [x <= 8.0], [_i0_1, _i0_2])
3591
3592## End of cephes code for i0
3593
3594
3595@set_module('numpy')
3596def kaiser(M, beta):
3597 """
3598 Return the Kaiser window.
3599
3600 The Kaiser window is a taper formed by using a Bessel function.
3601
3602 Parameters
3603 ----------
3604 M : int
3605 Number of points in the output window. If zero or less, an
3606 empty array is returned.
3607 beta : float
3608 Shape parameter for window.
3609
3610 Returns
3611 -------
3612 out : array
3613 The window, with the maximum value normalized to one (the value
3614 one appears only if the number of samples is odd).
3615
3616 See Also
3617 --------
3618 bartlett, blackman, hamming, hanning
3619
3620 Notes
3621 -----
3622 The Kaiser window is defined as
3623
3624 .. math:: w(n) = I_0\\left( \\beta \\sqrt{1-\\frac{4n^2}{(M-1)^2}}
3625 \\right)/I_0(\\beta)
3626
3627 with
3628
3629 .. math:: \\quad -\\frac{M-1}{2} \\leq n \\leq \\frac{M-1}{2},
3630
3631 where :math:`I_0` is the modified zeroth-order Bessel function.
3632
3633 The Kaiser was named for Jim Kaiser, who discovered a simple
3634 approximation to the DPSS window based on Bessel functions. The Kaiser
3635 window is a very good approximation to the Digital Prolate Spheroidal
3636 Sequence, or Slepian window, which is the transform which maximizes the
3637 energy in the main lobe of the window relative to total energy.
3638
3639 The Kaiser can approximate many other windows by varying the beta
3640 parameter.
3641
3642 ==== =======================
3643 beta Window shape
3644 ==== =======================
3645 0 Rectangular
3646 5 Similar to a Hamming
3647 6 Similar to a Hanning
3648 8.6 Similar to a Blackman
3649 ==== =======================
3650
3651 A beta value of 14 is probably a good starting point. Note that as beta
3652 gets large, the window narrows, and so the number of samples needs to be
3653 large enough to sample the increasingly narrow spike, otherwise NaNs will
3654 get returned.
3655
3656 Most references to the Kaiser window come from the signal processing
3657 literature, where it is used as one of many windowing functions for
3658 smoothing values. It is also known as an apodization (which means
3659 "removing the foot", i.e. smoothing discontinuities at the beginning
3660 and end of the sampled signal) or tapering function.
3661
3662 References
3663 ----------
3664 .. [1] J. F. Kaiser, "Digital Filters" - Ch 7 in "Systems analysis by
3665 digital computer", Editors: F.F. Kuo and J.F. Kaiser, p 218-285.
3666 John Wiley and Sons, New York, (1966).
3667 .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The
3668 University of Alberta Press, 1975, pp. 177-178.
3669 .. [3] Wikipedia, "Window function",
3670 https://en.wikipedia.org/wiki/Window_function
3671
3672 Examples
3673 --------
3674 >>> import numpy as np
3675 >>> import matplotlib.pyplot as plt
3676 >>> np.kaiser(12, 14)
3677 array([7.72686684e-06, 3.46009194e-03, 4.65200189e-02, # may vary
3678 2.29737120e-01, 5.99885316e-01, 9.45674898e-01,
3679 9.45674898e-01, 5.99885316e-01, 2.29737120e-01,
3680 4.65200189e-02, 3.46009194e-03, 7.72686684e-06])
3681
3682
3683 Plot the window and the frequency response.
3684
3685 .. plot::
3686 :include-source:
3687
3688 import matplotlib.pyplot as plt
3689 from numpy.fft import fft, fftshift
3690 window = np.kaiser(51, 14)
3691 plt.plot(window)
3692 plt.title("Kaiser window")
3693 plt.ylabel("Amplitude")
3694 plt.xlabel("Sample")
3695 plt.show()
3696
3697 plt.figure()
3698 A = fft(window, 2048) / 25.5
3699 mag = np.abs(fftshift(A))
3700 freq = np.linspace(-0.5, 0.5, len(A))
3701 response = 20 * np.log10(mag)
3702 response = np.clip(response, -100, 100)
3703 plt.plot(freq, response)
3704 plt.title("Frequency response of Kaiser window")
3705 plt.ylabel("Magnitude [dB]")
3706 plt.xlabel("Normalized frequency [cycles per sample]")
3707 plt.axis('tight')
3708 plt.show()
3709
3710 """
3711 # Ensures at least float64 via 0.0. M should be an integer, but conversion
3712 # to double is safe for a range. (Simplified result_type with 0.0
3713 # strongly typed. result-type is not/less order sensitive, but that mainly
3714 # matters for integers anyway.)
3715 values = np.array([0.0, M, beta])
3716 M = values[1]
3717 beta = values[2]
3718
3719 if M == 1:
3720 return np.ones(1, dtype=values.dtype)
3721 n = arange(0, M)
3722 alpha = (M - 1) / 2.0
3723 return i0(beta * sqrt(1 - ((n - alpha) / alpha)**2.0)) / i0(beta)
3724
3725
3726def _sinc_dispatcher(x):
3727 return (x,)
3728
3729
3730@array_function_dispatch(_sinc_dispatcher)
3731def sinc(x):
3732 r"""
3733 Return the normalized sinc function.
3734
3735 The sinc function is equal to :math:`\sin(\pi x)/(\pi x)` for any argument
3736 :math:`x\ne 0`. ``sinc(0)`` takes the limit value 1, making ``sinc`` not
3737 only everywhere continuous but also infinitely differentiable.
3738
3739 .. note::
3740
3741 Note the normalization factor of ``pi`` used in the definition.
3742 This is the most commonly used definition in signal processing.
3743 Use ``sinc(x / np.pi)`` to obtain the unnormalized sinc function
3744 :math:`\sin(x)/x` that is more common in mathematics.
3745
3746 Parameters
3747 ----------
3748 x : ndarray
3749 Array (possibly multi-dimensional) of values for which to calculate
3750 ``sinc(x)``.
3751
3752 Returns
3753 -------
3754 out : ndarray
3755 ``sinc(x)``, which has the same shape as the input.
3756
3757 Notes
3758 -----
3759 The name sinc is short for "sine cardinal" or "sinus cardinalis".
3760
3761 The sinc function is used in various signal processing applications,
3762 including in anti-aliasing, in the construction of a Lanczos resampling
3763 filter, and in interpolation.
3764
3765 For bandlimited interpolation of discrete-time signals, the ideal
3766 interpolation kernel is proportional to the sinc function.
3767
3768 References
3769 ----------
3770 .. [1] Weisstein, Eric W. "Sinc Function." From MathWorld--A Wolfram Web
3771 Resource. https://mathworld.wolfram.com/SincFunction.html
3772 .. [2] Wikipedia, "Sinc function",
3773 https://en.wikipedia.org/wiki/Sinc_function
3774
3775 Examples
3776 --------
3777 >>> import numpy as np
3778 >>> import matplotlib.pyplot as plt
3779 >>> x = np.linspace(-4, 4, 41)
3780 >>> np.sinc(x)
3781 array([-3.89804309e-17, -4.92362781e-02, -8.40918587e-02, # may vary
3782 -8.90384387e-02, -5.84680802e-02, 3.89804309e-17,
3783 6.68206631e-02, 1.16434881e-01, 1.26137788e-01,
3784 8.50444803e-02, -3.89804309e-17, -1.03943254e-01,
3785 -1.89206682e-01, -2.16236208e-01, -1.55914881e-01,
3786 3.89804309e-17, 2.33872321e-01, 5.04551152e-01,
3787 7.56826729e-01, 9.35489284e-01, 1.00000000e+00,
3788 9.35489284e-01, 7.56826729e-01, 5.04551152e-01,
3789 2.33872321e-01, 3.89804309e-17, -1.55914881e-01,
3790 -2.16236208e-01, -1.89206682e-01, -1.03943254e-01,
3791 -3.89804309e-17, 8.50444803e-02, 1.26137788e-01,
3792 1.16434881e-01, 6.68206631e-02, 3.89804309e-17,
3793 -5.84680802e-02, -8.90384387e-02, -8.40918587e-02,
3794 -4.92362781e-02, -3.89804309e-17])
3795
3796 >>> plt.plot(x, np.sinc(x))
3797 [<matplotlib.lines.Line2D object at 0x...>]
3798 >>> plt.title("Sinc Function")
3799 Text(0.5, 1.0, 'Sinc Function')
3800 >>> plt.ylabel("Amplitude")
3801 Text(0, 0.5, 'Amplitude')
3802 >>> plt.xlabel("X")
3803 Text(0.5, 0, 'X')
3804 >>> plt.show()
3805
3806 """
3807 x = np.asanyarray(x)
3808 x = pi * x
3809 # Hope that 1e-20 is sufficient for objects...
3810 eps = np.finfo(x.dtype).eps if x.dtype.kind == "f" else 1e-20
3811 y = where(x, x, eps)
3812 return sin(y) / y
3813
3814
3815def _ureduce(a, func, keepdims=False, **kwargs):
3816 """
3817 Internal Function.
3818 Call `func` with `a` as first argument swapping the axes to use extended
3819 axis on functions that don't support it natively.
3820
3821 Returns result and a.shape with axis dims set to 1.
3822
3823 Parameters
3824 ----------
3825 a : array_like
3826 Input array or object that can be converted to an array.
3827 func : callable
3828 Reduction function capable of receiving a single axis argument.
3829 It is called with `a` as first argument followed by `kwargs`.
3830 kwargs : keyword arguments
3831 additional keyword arguments to pass to `func`.
3832
3833 Returns
3834 -------
3835 result : tuple
3836 Result of func(a, **kwargs) and a.shape with axis dims set to 1
3837 which can be used to reshape the result to the same shape a ufunc with
3838 keepdims=True would produce.
3839
3840 """
3841 a = np.asanyarray(a)
3842 axis = kwargs.get('axis')
3843 out = kwargs.get('out')
3844
3845 if keepdims is np._NoValue:
3846 keepdims = False
3847
3848 nd = a.ndim
3849 if axis is not None:
3850 axis = _nx.normalize_axis_tuple(axis, nd)
3851
3852 if keepdims and out is not None:
3853 index_out = tuple(
3854 0 if i in axis else slice(None) for i in range(nd))
3855 kwargs['out'] = out[(Ellipsis, ) + index_out]
3856
3857 if len(axis) == 1:
3858 kwargs['axis'] = axis[0]
3859 else:
3860 keep = sorted(set(range(nd)) - set(axis))
3861 nkeep = len(keep)
3862
3863 def reshape_arr(a):
3864 # move axis that should not be reduced to front
3865 a = np.moveaxis(a, keep, range(nkeep))
3866 # merge reduced axis
3867 return a.reshape(a.shape[:nkeep] + (-1,))
3868
3869 a = reshape_arr(a)
3870
3871 weights = kwargs.get("weights")
3872 if weights is not None:
3873 kwargs["weights"] = reshape_arr(weights)
3874
3875 kwargs['axis'] = -1
3876 elif keepdims and out is not None:
3877 index_out = (0, ) * nd
3878 kwargs['out'] = out[(Ellipsis, ) + index_out]
3879
3880 r = func(a, **kwargs)
3881
3882 if out is not None:
3883 return out
3884
3885 if keepdims:
3886 if axis is None:
3887 index_r = (np.newaxis, ) * nd
3888 else:
3889 index_r = tuple(
3890 np.newaxis if i in axis else slice(None)
3891 for i in range(nd))
3892 r = r[(Ellipsis, ) + index_r]
3893
3894 return r
3895
3896
3897def _median_dispatcher(
3898 a, axis=None, out=None, overwrite_input=None, keepdims=None):
3899 return (a, out)
3900
3901
3902@array_function_dispatch(_median_dispatcher)
3903def median(a, axis=None, out=None, overwrite_input=False, keepdims=False):
3904 """
3905 Compute the median along the specified axis.
3906
3907 Returns the median of the array elements.
3908
3909 Parameters
3910 ----------
3911 a : array_like
3912 Input array or object that can be converted to an array.
3913 axis : {int, sequence of int, None}, optional
3914 Axis or axes along which the medians are computed. The default,
3915 axis=None, will compute the median along a flattened version of
3916 the array. If a sequence of axes, the array is first flattened
3917 along the given axes, then the median is computed along the
3918 resulting flattened axis.
3919 out : ndarray, optional
3920 Alternative output array in which to place the result. It must
3921 have the same shape and buffer length as the expected output,
3922 but the type (of the output) will be cast if necessary.
3923 overwrite_input : bool, optional
3924 If True, then allow use of memory of input array `a` for
3925 calculations. The input array will be modified by the call to
3926 `median`. This will save memory when you do not need to preserve
3927 the contents of the input array. Treat the input as undefined,
3928 but it will probably be fully or partially sorted. Default is
3929 False. If `overwrite_input` is ``True`` and `a` is not already an
3930 `ndarray`, an error will be raised.
3931 keepdims : bool, optional
3932 If this is set to True, the axes which are reduced are left
3933 in the result as dimensions with size one. With this option,
3934 the result will broadcast correctly against the original `arr`.
3935
3936 Returns
3937 -------
3938 median : ndarray
3939 A new array holding the result. If the input contains integers
3940 or floats smaller than ``float64``, then the output data-type is
3941 ``np.float64``. Otherwise, the data-type of the output is the
3942 same as that of the input. If `out` is specified, that array is
3943 returned instead.
3944
3945 See Also
3946 --------
3947 mean, percentile
3948
3949 Notes
3950 -----
3951 Given a vector ``V`` of length ``N``, the median of ``V`` is the
3952 middle value of a sorted copy of ``V``, ``V_sorted`` - i
3953 e., ``V_sorted[(N-1)/2]``, when ``N`` is odd, and the average of the
3954 two middle values of ``V_sorted`` when ``N`` is even.
3955
3956 Examples
3957 --------
3958 >>> import numpy as np
3959 >>> a = np.array([[10, 7, 4], [3, 2, 1]])
3960 >>> a
3961 array([[10, 7, 4],
3962 [ 3, 2, 1]])
3963 >>> np.median(a)
3964 np.float64(3.5)
3965 >>> np.median(a, axis=0)
3966 array([6.5, 4.5, 2.5])
3967 >>> np.median(a, axis=1)
3968 array([7., 2.])
3969 >>> np.median(a, axis=(0, 1))
3970 np.float64(3.5)
3971 >>> m = np.median(a, axis=0)
3972 >>> out = np.zeros_like(m)
3973 >>> np.median(a, axis=0, out=m)
3974 array([6.5, 4.5, 2.5])
3975 >>> m
3976 array([6.5, 4.5, 2.5])
3977 >>> b = a.copy()
3978 >>> np.median(b, axis=1, overwrite_input=True)
3979 array([7., 2.])
3980 >>> assert not np.all(a==b)
3981 >>> b = a.copy()
3982 >>> np.median(b, axis=None, overwrite_input=True)
3983 np.float64(3.5)
3984 >>> assert not np.all(a==b)
3985
3986 """
3987 return _ureduce(a, func=_median, keepdims=keepdims, axis=axis, out=out,
3988 overwrite_input=overwrite_input)
3989
3990
3991def _median(a, axis=None, out=None, overwrite_input=False):
3992 # can't be reasonably be implemented in terms of percentile as we have to
3993 # call mean to not break astropy
3994 a = np.asanyarray(a)
3995
3996 # Set the partition indexes
3997 if axis is None:
3998 sz = a.size
3999 else:
4000 sz = a.shape[axis]
4001 if sz % 2 == 0:
4002 szh = sz // 2
4003 kth = [szh - 1, szh]
4004 else:
4005 kth = [(sz - 1) // 2]
4006
4007 # We have to check for NaNs (as of writing 'M' doesn't actually work).
4008 supports_nans = np.issubdtype(a.dtype, np.inexact) or a.dtype.kind in 'Mm'
4009 if supports_nans:
4010 kth.append(-1)
4011
4012 if overwrite_input:
4013 if axis is None:
4014 part = a.ravel()
4015 part.partition(kth)
4016 else:
4017 a.partition(kth, axis=axis)
4018 part = a
4019 else:
4020 part = partition(a, kth, axis=axis)
4021
4022 if part.shape == ():
4023 # make 0-D arrays work
4024 return part.item()
4025 if axis is None:
4026 axis = 0
4027
4028 indexer = [slice(None)] * part.ndim
4029 index = part.shape[axis] // 2
4030 if part.shape[axis] % 2 == 1:
4031 # index with slice to allow mean (below) to work
4032 indexer[axis] = slice(index, index + 1)
4033 else:
4034 indexer[axis] = slice(index - 1, index + 1)
4035 indexer = tuple(indexer)
4036
4037 # Use mean in both odd and even case to coerce data type,
4038 # using out array if needed.
4039 rout = mean(part[indexer], axis=axis, out=out)
4040 if supports_nans and sz > 0:
4041 # If nans are possible, warn and replace by nans like mean would.
4042 rout = np.lib._utils_impl._median_nancheck(part, rout, axis)
4043
4044 return rout
4045
4046
4047def _percentile_dispatcher(a, q, axis=None, out=None, overwrite_input=None,
4048 method=None, keepdims=None, *, weights=None):
4049 return (a, q, out, weights)
4050
4051
4052@array_function_dispatch(_percentile_dispatcher)
4053def percentile(a,
4054 q,
4055 axis=None,
4056 out=None,
4057 overwrite_input=False,
4058 method="linear",
4059 keepdims=False,
4060 *,
4061 weights=None):
4062 """
4063 Compute the q-th percentile of the data along the specified axis.
4064
4065 Returns the q-th percentile(s) of the array elements.
4066
4067 Parameters
4068 ----------
4069 a : array_like of real numbers
4070 Input array or object that can be converted to an array.
4071 q : array_like of float
4072 Percentage or sequence of percentages for the percentiles to compute.
4073 Values must be between 0 and 100 inclusive.
4074 axis : {int, tuple of int, None}, optional
4075 Axis or axes along which the percentiles are computed. The
4076 default is to compute the percentile(s) along a flattened
4077 version of the array.
4078 out : ndarray, optional
4079 Alternative output array in which to place the result. It must
4080 have the same shape and buffer length as the expected output,
4081 but the type (of the output) will be cast if necessary.
4082 overwrite_input : bool, optional
4083 If True, then allow the input array `a` to be modified by intermediate
4084 calculations, to save memory. In this case, the contents of the input
4085 `a` after this function completes is undefined.
4086 method : str, optional
4087 This parameter specifies the method to use for estimating the
4088 percentile. There are many different methods, some unique to NumPy.
4089 See the notes for explanation. The options sorted by their R type
4090 as summarized in the H&F paper [1]_ are:
4091
4092 1. 'inverted_cdf'
4093 2. 'averaged_inverted_cdf'
4094 3. 'closest_observation'
4095 4. 'interpolated_inverted_cdf'
4096 5. 'hazen'
4097 6. 'weibull'
4098 7. 'linear' (default)
4099 8. 'median_unbiased'
4100 9. 'normal_unbiased'
4101
4102 The first three methods are discontinuous. NumPy further defines the
4103 following discontinuous variations of the default 'linear' (7.) option:
4104
4105 * 'lower'
4106 * 'higher',
4107 * 'midpoint'
4108 * 'nearest'
4109
4110 .. versionchanged:: 1.22.0
4111 This argument was previously called "interpolation" and only
4112 offered the "linear" default and last four options.
4113
4114 keepdims : bool, optional
4115 If this is set to True, the axes which are reduced are left in
4116 the result as dimensions with size one. With this option, the
4117 result will broadcast correctly against the original array `a`.
4118
4119 weights : array_like, optional
4120 An array of weights associated with the values in `a`. Each value in
4121 `a` contributes to the percentile according to its associated weight.
4122 The weights array can either be 1-D (in which case its length must be
4123 the size of `a` along the given axis) or of the same shape as `a`.
4124 If `weights=None`, then all data in `a` are assumed to have a
4125 weight equal to one.
4126 Only `method="inverted_cdf"` supports weights.
4127 See the notes for more details.
4128
4129 .. versionadded:: 2.0.0
4130
4131 Returns
4132 -------
4133 percentile : scalar or ndarray
4134 If `q` is a single percentile and `axis=None`, then the result
4135 is a scalar. If multiple percentiles are given, first axis of
4136 the result corresponds to the percentiles. The other axes are
4137 the axes that remain after the reduction of `a`. If the input
4138 contains integers or floats smaller than ``float64``, the output
4139 data-type is ``float64``. Otherwise, the output data-type is the
4140 same as that of the input. If `out` is specified, that array is
4141 returned instead.
4142
4143 See Also
4144 --------
4145 mean
4146 median : equivalent to ``percentile(..., 50)``
4147 nanpercentile
4148 quantile : equivalent to percentile, except q in the range [0, 1].
4149
4150 Notes
4151 -----
4152 The behavior of `numpy.percentile` with percentage `q` is
4153 that of `numpy.quantile` with argument ``q/100``.
4154 For more information, please see `numpy.quantile`.
4155
4156 Examples
4157 --------
4158 >>> import numpy as np
4159 >>> a = np.array([[10, 7, 4], [3, 2, 1]])
4160 >>> a
4161 array([[10, 7, 4],
4162 [ 3, 2, 1]])
4163 >>> np.percentile(a, 50)
4164 3.5
4165 >>> np.percentile(a, 50, axis=0)
4166 array([6.5, 4.5, 2.5])
4167 >>> np.percentile(a, 50, axis=1)
4168 array([7., 2.])
4169 >>> np.percentile(a, 50, axis=1, keepdims=True)
4170 array([[7.],
4171 [2.]])
4172
4173 >>> m = np.percentile(a, 50, axis=0)
4174 >>> out = np.zeros_like(m)
4175 >>> np.percentile(a, 50, axis=0, out=out)
4176 array([6.5, 4.5, 2.5])
4177 >>> m
4178 array([6.5, 4.5, 2.5])
4179
4180 >>> b = a.copy()
4181 >>> np.percentile(b, 50, axis=1, overwrite_input=True)
4182 array([7., 2.])
4183 >>> assert not np.all(a == b)
4184
4185 The different methods can be visualized graphically:
4186
4187 .. plot::
4188
4189 import matplotlib.pyplot as plt
4190
4191 a = np.arange(4)
4192 p = np.linspace(0, 100, 6001)
4193 ax = plt.gca()
4194 lines = [
4195 ('linear', '-', 'C0'),
4196 ('inverted_cdf', ':', 'C1'),
4197 # Almost the same as `inverted_cdf`:
4198 ('averaged_inverted_cdf', '-.', 'C1'),
4199 ('closest_observation', ':', 'C2'),
4200 ('interpolated_inverted_cdf', '--', 'C1'),
4201 ('hazen', '--', 'C3'),
4202 ('weibull', '-.', 'C4'),
4203 ('median_unbiased', '--', 'C5'),
4204 ('normal_unbiased', '-.', 'C6'),
4205 ]
4206 for method, style, color in lines:
4207 ax.plot(
4208 p, np.percentile(a, p, method=method),
4209 label=method, linestyle=style, color=color)
4210 ax.set(
4211 title='Percentiles for different methods and data: ' + str(a),
4212 xlabel='Percentile',
4213 ylabel='Estimated percentile value',
4214 yticks=a)
4215 ax.legend(bbox_to_anchor=(1.03, 1))
4216 plt.tight_layout()
4217 plt.show()
4218
4219 References
4220 ----------
4221 .. [1] R. J. Hyndman and Y. Fan,
4222 "Sample quantiles in statistical packages,"
4223 The American Statistician, 50(4), pp. 361-365, 1996
4224
4225 """
4226 a = np.asanyarray(a)
4227 if a.dtype.kind == "c":
4228 raise TypeError("a must be an array of real numbers")
4229
4230 weak_q = type(q) in (int, float) # use weak promotion for final result type
4231 q = np.true_divide(q, 100, out=...)
4232 if not _quantile_is_valid(q):
4233 raise ValueError("Percentiles must be in the range [0, 100]")
4234
4235 if weights is not None:
4236 if method != "inverted_cdf":
4237 msg = ("Only method 'inverted_cdf' supports weights. "
4238 f"Got: {method}.")
4239 raise ValueError(msg)
4240 if axis is not None:
4241 axis = _nx.normalize_axis_tuple(axis, a.ndim, argname="axis")
4242 weights = _weights_are_valid(weights=weights, a=a, axis=axis)
4243 if np.any(weights < 0):
4244 raise ValueError("Weights must be non-negative.")
4245
4246 return _quantile_unchecked(
4247 a, q, axis, out, overwrite_input, method, keepdims, weights, weak_q)
4248
4249
4250def _quantile_dispatcher(a, q, axis=None, out=None, overwrite_input=None,
4251 method=None, keepdims=None, *, weights=None):
4252 return (a, q, out, weights)
4253
4254
4255@array_function_dispatch(_quantile_dispatcher)
4256def quantile(a,
4257 q,
4258 axis=None,
4259 out=None,
4260 overwrite_input=False,
4261 method="linear",
4262 keepdims=False,
4263 *,
4264 weights=None):
4265 """
4266 Compute the q-th quantile of the data along the specified axis.
4267
4268 Parameters
4269 ----------
4270 a : array_like of real numbers
4271 Input array or object that can be converted to an array.
4272 q : array_like of float
4273 Probability or sequence of probabilities of the quantiles to compute.
4274 Values must be between 0 and 1 inclusive.
4275 axis : {int, tuple of int, None}, optional
4276 Axis or axes along which the quantiles are computed. The default is
4277 to compute the quantile(s) along a flattened version of the array.
4278 out : ndarray, optional
4279 Alternative output array in which to place the result. It must have
4280 the same shape and buffer length as the expected output, but the
4281 type (of the output) will be cast if necessary.
4282 overwrite_input : bool, optional
4283 If True, then allow the input array `a` to be modified by
4284 intermediate calculations, to save memory. In this case, the
4285 contents of the input `a` after this function completes is
4286 undefined.
4287 method : str, optional
4288 This parameter specifies the method to use for estimating the
4289 quantile. There are many different methods, some unique to NumPy.
4290 The recommended options, numbered as they appear in [1]_, are:
4291
4292 1. 'inverted_cdf'
4293 2. 'averaged_inverted_cdf'
4294 3. 'closest_observation'
4295 4. 'interpolated_inverted_cdf'
4296 5. 'hazen'
4297 6. 'weibull'
4298 7. 'linear' (default)
4299 8. 'median_unbiased'
4300 9. 'normal_unbiased'
4301
4302 The first three methods are discontinuous. For backward compatibility
4303 with previous versions of NumPy, the following discontinuous variations
4304 of the default 'linear' (7.) option are available:
4305
4306 * 'lower'
4307 * 'higher',
4308 * 'midpoint'
4309 * 'nearest'
4310
4311 See Notes for details.
4312
4313 .. versionchanged:: 1.22.0
4314 This argument was previously called "interpolation" and only
4315 offered the "linear" default and last four options.
4316
4317 keepdims : bool, optional
4318 If this is set to True, the axes which are reduced are left in
4319 the result as dimensions with size one. With this option, the
4320 result will broadcast correctly against the original array `a`.
4321
4322 weights : array_like, optional
4323 An array of weights associated with the values in `a`. Each value in
4324 `a` contributes to the quantile according to its associated weight.
4325 The weights array can either be 1-D (in which case its length must be
4326 the size of `a` along the given axis) or of the same shape as `a`.
4327 If `weights=None`, then all data in `a` are assumed to have a
4328 weight equal to one.
4329 Only `method="inverted_cdf"` supports weights.
4330 See the notes for more details.
4331
4332 .. versionadded:: 2.0.0
4333
4334 Returns
4335 -------
4336 quantile : scalar or ndarray
4337 If `q` is a single probability and `axis=None`, then the result
4338 is a scalar. If multiple probability levels are given, first axis
4339 of the result corresponds to the quantiles. The other axes are
4340 the axes that remain after the reduction of `a`. If the input
4341 contains integers or floats smaller than ``float64``, the output
4342 data-type is ``float64``. Otherwise, the output data-type is the
4343 same as that of the input. If `out` is specified, that array is
4344 returned instead.
4345
4346 See Also
4347 --------
4348 mean
4349 percentile : equivalent to quantile, but with q in the range [0, 100].
4350 median : equivalent to ``quantile(..., 0.5)``
4351 nanquantile
4352
4353 Notes
4354 -----
4355 Given a sample `a` from an underlying distribution, `quantile` provides a
4356 nonparametric estimate of the inverse cumulative distribution function.
4357
4358 By default, this is done by interpolating between adjacent elements in
4359 ``y``, a sorted copy of `a`::
4360
4361 (1-g)*y[j] + g*y[j+1]
4362
4363 where the index ``j`` and coefficient ``g`` are the integral and
4364 fractional components of ``q * (n-1)``, and ``n`` is the number of
4365 elements in the sample.
4366
4367 This is a special case of Equation 1 of H&F [1]_. More generally,
4368
4369 - ``j = (q*n + m - 1) // 1``, and
4370 - ``g = (q*n + m - 1) % 1``,
4371
4372 where ``m`` may be defined according to several different conventions.
4373 The preferred convention may be selected using the ``method`` parameter:
4374
4375 =============================== =============== ===============
4376 ``method`` number in H&F ``m``
4377 =============================== =============== ===============
4378 ``interpolated_inverted_cdf`` 4 ``0``
4379 ``hazen`` 5 ``1/2``
4380 ``weibull`` 6 ``q``
4381 ``linear`` (default) 7 ``1 - q``
4382 ``median_unbiased`` 8 ``q/3 + 1/3``
4383 ``normal_unbiased`` 9 ``q/4 + 3/8``
4384 =============================== =============== ===============
4385
4386 Note that indices ``j`` and ``j + 1`` are clipped to the range ``0`` to
4387 ``n - 1`` when the results of the formula would be outside the allowed
4388 range of non-negative indices. The ``- 1`` in the formulas for ``j`` and
4389 ``g`` accounts for Python's 0-based indexing.
4390
4391 The table above includes only the estimators from H&F that are continuous
4392 functions of probability `q` (estimators 4-9). NumPy also provides the
4393 three discontinuous estimators from H&F (estimators 1-3), where ``j`` is
4394 defined as above, ``m`` is defined as follows, and ``g`` is a function
4395 of the real-valued ``index = q*n + m - 1`` and ``j``.
4396
4397 1. ``inverted_cdf``: ``m = 0`` and ``g = int(index - j > 0)``
4398 2. ``averaged_inverted_cdf``: ``m = 0`` and
4399 ``g = (1 + int(index - j > 0)) / 2``
4400 3. ``closest_observation``: ``m = -1/2`` and
4401 ``g = 1 - int((index == j) & (j%2 == 1))``
4402
4403 For backward compatibility with previous versions of NumPy, `quantile`
4404 provides four additional discontinuous estimators. Like
4405 ``method='linear'``, all have ``m = 1 - q`` so that ``j = q*(n-1) // 1``,
4406 but ``g`` is defined as follows.
4407
4408 - ``lower``: ``g = 0``
4409 - ``midpoint``: ``g = 0.5``
4410 - ``higher``: ``g = 1``
4411 - ``nearest``: ``g = (q*(n-1) % 1) > 0.5``
4412
4413 **Weighted quantiles:**
4414 More formally, the quantile at probability level :math:`q` of a cumulative
4415 distribution function :math:`F(y)=P(Y \\leq y)` with probability measure
4416 :math:`P` is defined as any number :math:`x` that fulfills the
4417 *coverage conditions*
4418
4419 .. math:: P(Y < x) \\leq q \\quad\\text{and}\\quad P(Y \\leq x) \\geq q
4420
4421 with random variable :math:`Y\\sim P`.
4422 Sample quantiles, the result of `quantile`, provide nonparametric
4423 estimation of the underlying population counterparts, represented by the
4424 unknown :math:`F`, given a data vector `a` of length ``n``.
4425
4426 Some of the estimators above arise when one considers :math:`F` as the
4427 empirical distribution function of the data, i.e.
4428 :math:`F(y) = \\frac{1}{n} \\sum_i 1_{a_i \\leq y}`.
4429 Then, different methods correspond to different choices of :math:`x` that
4430 fulfill the above coverage conditions. Methods that follow this approach
4431 are ``inverted_cdf`` and ``averaged_inverted_cdf``.
4432
4433 For weighted quantiles, the coverage conditions still hold. The
4434 empirical cumulative distribution is simply replaced by its weighted
4435 version, i.e.
4436 :math:`P(Y \\leq t) = \\frac{1}{\\sum_i w_i} \\sum_i w_i 1_{x_i \\leq t}`.
4437 Only ``method="inverted_cdf"`` supports weights.
4438
4439 Examples
4440 --------
4441 >>> import numpy as np
4442 >>> a = np.array([[10, 7, 4], [3, 2, 1]])
4443 >>> a
4444 array([[10, 7, 4],
4445 [ 3, 2, 1]])
4446 >>> np.quantile(a, 0.5)
4447 3.5
4448 >>> np.quantile(a, 0.5, axis=0)
4449 array([6.5, 4.5, 2.5])
4450 >>> np.quantile(a, 0.5, axis=1)
4451 array([7., 2.])
4452 >>> np.quantile(a, 0.5, axis=1, keepdims=True)
4453 array([[7.],
4454 [2.]])
4455 >>> m = np.quantile(a, 0.5, axis=0)
4456 >>> out = np.zeros_like(m)
4457 >>> np.quantile(a, 0.5, axis=0, out=out)
4458 array([6.5, 4.5, 2.5])
4459 >>> m
4460 array([6.5, 4.5, 2.5])
4461 >>> b = a.copy()
4462 >>> np.quantile(b, 0.5, axis=1, overwrite_input=True)
4463 array([7., 2.])
4464 >>> assert not np.all(a == b)
4465
4466 See also `numpy.percentile` for a visualization of most methods.
4467
4468 References
4469 ----------
4470 .. [1] R. J. Hyndman and Y. Fan,
4471 "Sample quantiles in statistical packages,"
4472 The American Statistician, 50(4), pp. 361-365, 1996
4473
4474 """
4475 a = np.asanyarray(a)
4476 if a.dtype.kind == "c":
4477 raise TypeError("a must be an array of real numbers")
4478
4479 weak_q = type(q) in (int, float) # use weak promotion for final result type
4480 q = np.asanyarray(q)
4481
4482 if not _quantile_is_valid(q):
4483 raise ValueError("Quantiles must be in the range [0, 1]")
4484
4485 if weights is not None:
4486 if method != "inverted_cdf":
4487 msg = ("Only method 'inverted_cdf' supports weights. "
4488 f"Got: {method}.")
4489 raise ValueError(msg)
4490 if axis is not None:
4491 axis = _nx.normalize_axis_tuple(axis, a.ndim, argname="axis")
4492 weights = _weights_are_valid(weights=weights, a=a, axis=axis)
4493 if np.any(weights < 0):
4494 raise ValueError("Weights must be non-negative.")
4495
4496 return _quantile_unchecked(
4497 a, q, axis, out, overwrite_input, method, keepdims, weights, weak_q)
4498
4499
4500def _quantile_unchecked(a,
4501 q,
4502 axis=None,
4503 out=None,
4504 overwrite_input=False,
4505 method="linear",
4506 keepdims=False,
4507 weights=None,
4508 weak_q=False):
4509 """Assumes that q is in [0, 1], and is an ndarray"""
4510 return _ureduce(a,
4511 func=_quantile_ureduce_func,
4512 q=q,
4513 weights=weights,
4514 keepdims=keepdims,
4515 axis=axis,
4516 out=out,
4517 overwrite_input=overwrite_input,
4518 method=method,
4519 weak_q=weak_q)
4520
4521
4522def _quantile_is_valid(q):
4523 # avoid expensive reductions, relevant for arrays with < O(1000) elements
4524 if q.ndim == 1 and q.size < 10:
4525 for i in range(q.size):
4526 if not (0.0 <= q[i] <= 1.0):
4527 return False
4528 elif not (q.min() >= 0 and q.max() <= 1):
4529 return False
4530 return True
4531
4532
4533def _compute_virtual_index(n, quantiles, alpha: float, beta: float):
4534 """
4535 Compute the floating point indexes of an array for the linear
4536 interpolation of quantiles.
4537 n : array_like
4538 The sample sizes.
4539 quantiles : array_like
4540 The quantiles values.
4541 alpha : float
4542 A constant used to correct the index computed.
4543 beta : float
4544 A constant used to correct the index computed.
4545
4546 alpha and beta values depend on the chosen method
4547 (see quantile documentation)
4548
4549 Reference:
4550 Hyndman&Fan paper "Sample Quantiles in Statistical Packages",
4551 DOI: 10.1080/00031305.1996.10473566
4552 """
4553 return n * quantiles + (
4554 alpha + quantiles * (1 - alpha - beta)
4555 ) - 1
4556
4557
4558def _get_gamma(virtual_indexes, previous_indexes, method):
4559 """
4560 Compute gamma (a.k.a 'm' or 'weight') for the linear interpolation
4561 of quantiles.
4562
4563 virtual_indexes : array_like
4564 The indexes where the percentile is supposed to be found in the sorted
4565 sample.
4566 previous_indexes : array_like
4567 The floor values of virtual_indexes.
4568 method : dict
4569 The interpolation method chosen, which may have a specific rule
4570 modifying gamma.
4571
4572 gamma is usually the fractional part of virtual_indexes but can be modified
4573 by the interpolation method.
4574 """
4575 gamma = np.asanyarray(virtual_indexes - previous_indexes)
4576 gamma = method["fix_gamma"](gamma, virtual_indexes)
4577 # Ensure both that we have an array, and that we keep the dtype
4578 # (which may have been matched to the input array).
4579 return np.asanyarray(gamma, dtype=virtual_indexes.dtype)
4580
4581
4582def _lerp(a, b, t, out=None):
4583 """
4584 Compute the linear interpolation weighted by gamma on each point of
4585 two same shape array.
4586
4587 a : array_like
4588 Left bound.
4589 b : array_like
4590 Right bound.
4591 t : array_like
4592 The interpolation weight.
4593 out : array_like
4594 Output array.
4595 """
4596 diff_b_a = b - a
4597 lerp_interpolation = add(a, diff_b_a * t, out=... if out is None else out)
4598 subtract(b, diff_b_a * (1 - t), out=lerp_interpolation, where=t >= 0.5,
4599 casting='unsafe', dtype=type(lerp_interpolation.dtype))
4600 if lerp_interpolation.ndim == 0 and out is None:
4601 lerp_interpolation = lerp_interpolation[()] # unpack 0d arrays
4602 return lerp_interpolation
4603
4604
4605def _get_gamma_mask(shape, default_value, conditioned_value, where):
4606 out = np.full(shape, default_value)
4607 np.copyto(out, conditioned_value, where=where, casting="unsafe")
4608 return out
4609
4610
4611def _discrete_interpolation_to_boundaries(index, gamma_condition_fun):
4612 previous = np.floor(index)
4613 next = previous + 1
4614 gamma = index - previous
4615 res = _get_gamma_mask(shape=index.shape,
4616 default_value=next,
4617 conditioned_value=previous,
4618 where=gamma_condition_fun(gamma, index)
4619 ).astype(np.intp)
4620 # Some methods can lead to out-of-bound integers, clip them:
4621 res[res < 0] = 0
4622 return res
4623
4624
4625def _closest_observation(n, quantiles):
4626 # "choose the nearest even order statistic at g=0" (H&F (1996) pp. 362).
4627 # Order is 1-based so for zero-based indexing round to nearest odd index.
4628 gamma_fun = lambda gamma, index: (gamma == 0) & (np.floor(index) % 2 == 1)
4629 return _discrete_interpolation_to_boundaries((n * quantiles) - 1 - 0.5,
4630 gamma_fun)
4631
4632
4633def _inverted_cdf(n, quantiles):
4634 gamma_fun = lambda gamma, _: (gamma == 0)
4635 return _discrete_interpolation_to_boundaries((n * quantiles) - 1,
4636 gamma_fun)
4637
4638
4639def _quantile_ureduce_func(
4640 a: np.ndarray,
4641 q: np.ndarray,
4642 weights: np.ndarray | None,
4643 axis: int | None = None,
4644 out: np.ndarray | None = None,
4645 overwrite_input: bool = False,
4646 method: str = "linear",
4647 weak_q: bool = False,
4648) -> np.ndarray:
4649 if q.ndim > 2:
4650 # The code below works fine for nd, but it might not have useful
4651 # semantics. For now, keep the supported dimensions the same as it was
4652 # before.
4653 raise ValueError("q must be a scalar or 1d")
4654 if overwrite_input:
4655 if axis is None:
4656 axis = 0
4657 arr = a.ravel()
4658 wgt = None if weights is None else weights.ravel()
4659 else:
4660 arr = a
4661 wgt = weights
4662 elif axis is None:
4663 axis = 0
4664 arr = a.flatten()
4665 wgt = None if weights is None else weights.flatten()
4666 else:
4667 arr = a.copy()
4668 wgt = weights
4669 result = _quantile(arr,
4670 quantiles=q,
4671 axis=axis,
4672 method=method,
4673 out=out,
4674 weights=wgt,
4675 weak_q=weak_q)
4676 return result
4677
4678
4679def _get_indexes(arr, virtual_indexes, valid_values_count):
4680 """
4681 Get the valid indexes of arr neighbouring virtual_indexes.
4682 Note
4683 This is a companion function to linear interpolation of
4684 Quantiles
4685
4686 Returns
4687 -------
4688 (previous_indexes, next_indexes): Tuple
4689 A Tuple of virtual_indexes neighbouring indexes
4690 """
4691 previous_indexes = floor(virtual_indexes, out=...)
4692 next_indexes = add(previous_indexes, 1, out=...)
4693 indexes_above_bounds = virtual_indexes >= valid_values_count - 1
4694 # When indexes is above max index, take the max value of the array
4695 if indexes_above_bounds.any():
4696 previous_indexes[indexes_above_bounds] = -1
4697 next_indexes[indexes_above_bounds] = -1
4698 # When indexes is below min index, take the min value of the array
4699 indexes_below_bounds = virtual_indexes < 0
4700 if indexes_below_bounds.any():
4701 previous_indexes[indexes_below_bounds] = 0
4702 next_indexes[indexes_below_bounds] = 0
4703 if np.issubdtype(arr.dtype, np.inexact):
4704 # After the sort, slices having NaNs will have for last element a NaN
4705 virtual_indexes_nans = np.isnan(virtual_indexes)
4706 if virtual_indexes_nans.any():
4707 previous_indexes[virtual_indexes_nans] = -1
4708 next_indexes[virtual_indexes_nans] = -1
4709 previous_indexes = previous_indexes.astype(np.intp)
4710 next_indexes = next_indexes.astype(np.intp)
4711 return previous_indexes, next_indexes
4712
4713
4714def _quantile(
4715 arr: "np.typing.ArrayLike",
4716 quantiles: np.ndarray,
4717 axis: int = -1,
4718 method: str = "linear",
4719 out: np.ndarray | None = None,
4720 weights: "np.typing.ArrayLike | None" = None,
4721 weak_q: bool = False,
4722) -> np.ndarray:
4723 """
4724 Private function that doesn't support extended axis or keepdims.
4725 These methods are extended to this function using _ureduce
4726 See nanpercentile for parameter usage
4727 It computes the quantiles of the array for the given axis.
4728 A linear interpolation is performed based on the `method`.
4729
4730 By default, the method is "linear" where alpha == beta == 1 which
4731 performs the 7th method of Hyndman&Fan.
4732 With "median_unbiased" we get alpha == beta == 1/3
4733 thus the 8th method of Hyndman&Fan.
4734 """
4735 # --- Setup
4736 arr = np.asanyarray(arr)
4737 values_count = arr.shape[axis]
4738 # The dimensions of `q` are prepended to the output shape, so we need the
4739 # axis being sampled from `arr` to be last.
4740 if axis != 0: # But moveaxis is slow, so only call it if necessary.
4741 arr = np.moveaxis(arr, axis, destination=0)
4742 supports_nans = (
4743 np.issubdtype(arr.dtype, np.inexact) or arr.dtype.kind in 'Mm'
4744 )
4745
4746 if weights is None:
4747 # --- Computation of indexes
4748 # Index where to find the value in the sorted array.
4749 # Virtual because it is a floating point value, not a valid index.
4750 # The nearest neighbours are used for interpolation
4751 try:
4752 method_props = _QuantileMethods[method]
4753 except KeyError:
4754 raise ValueError(
4755 f"{method!r} is not a valid method. Use one of: "
4756 f"{_QuantileMethods.keys()}") from None
4757 virtual_indexes = method_props["get_virtual_index"](values_count,
4758 quantiles)
4759 virtual_indexes = np.asanyarray(virtual_indexes)
4760
4761 if method_props["fix_gamma"] is None:
4762 supports_integers = True
4763 else:
4764 int_virtual_indices = np.issubdtype(virtual_indexes.dtype,
4765 np.integer)
4766 supports_integers = method == 'linear' and int_virtual_indices
4767
4768 if supports_integers:
4769 # No interpolation needed, take the points along axis
4770 if supports_nans:
4771 # may contain nan, which would sort to the end
4772 arr.partition(
4773 concatenate((virtual_indexes.ravel(), [-1])), axis=0,
4774 )
4775 slices_having_nans = np.isnan(arr[-1, ...])
4776 else:
4777 # cannot contain nan
4778 arr.partition(virtual_indexes.ravel(), axis=0)
4779 slices_having_nans = np.array(False, dtype=bool)
4780 result = take(arr, virtual_indexes, axis=0, out=out)
4781 else:
4782 previous_indexes, next_indexes = _get_indexes(arr,
4783 virtual_indexes,
4784 values_count)
4785 # --- Sorting
4786 arr.partition(
4787 np.unique(np.concatenate(([0, -1],
4788 previous_indexes.ravel(),
4789 next_indexes.ravel(),
4790 ))),
4791 axis=0)
4792 if supports_nans:
4793 slices_having_nans = np.isnan(arr[-1, ...])
4794 else:
4795 slices_having_nans = None
4796 # --- Get values from indexes
4797 previous = arr[previous_indexes]
4798 next = arr[next_indexes]
4799 # --- Linear interpolation
4800 gamma = _get_gamma(virtual_indexes, previous_indexes,
4801 method_props)
4802 if weak_q:
4803 gamma = float(gamma)
4804 else:
4805 result_shape = virtual_indexes.shape + (1,) * (arr.ndim - 1)
4806 gamma = gamma.reshape(result_shape)
4807 result = _lerp(previous,
4808 next,
4809 gamma,
4810 out=out)
4811 else:
4812 # Weighted case
4813 # This implements method="inverted_cdf", the only supported weighted
4814 # method, which needs to sort anyway.
4815 weights = np.asanyarray(weights)
4816 if axis != 0:
4817 weights = np.moveaxis(weights, axis, destination=0)
4818 index_array = np.argsort(arr, axis=0)
4819
4820 # arr = arr[index_array, ...] # but this adds trailing dimensions of
4821 # 1.
4822 arr = np.take_along_axis(arr, index_array, axis=0)
4823 if weights.shape == arr.shape:
4824 weights = np.take_along_axis(weights, index_array, axis=0)
4825 else:
4826 # weights is 1d
4827 weights = weights.reshape(-1)[index_array, ...]
4828
4829 if supports_nans:
4830 # may contain nan, which would sort to the end
4831 slices_having_nans = np.isnan(arr[-1, ...])
4832 else:
4833 # cannot contain nan
4834 slices_having_nans = np.array(False, dtype=bool)
4835
4836 # We use the weights to calculate the empirical cumulative
4837 # distribution function cdf
4838 cdf = weights.cumsum(axis=0, dtype=np.float64)
4839 cdf /= cdf[-1, ...] # normalization to 1
4840 if np.isnan(cdf[-1]).any():
4841 # Above calculations should normally warn for the zero/inf case.
4842 raise ValueError("Weights included NaN, inf or were all zero.")
4843 # Search index i such that
4844 # sum(weights[j], j=0..i-1) < quantile <= sum(weights[j], j=0..i)
4845 # is then equivalent to
4846 # cdf[i-1] < quantile <= cdf[i]
4847 # Unfortunately, searchsorted only accepts 1-d arrays as first
4848 # argument, so we will need to iterate over dimensions.
4849
4850 # Without the following cast, searchsorted can return surprising
4851 # results, e.g.
4852 # np.searchsorted(np.array([0.2, 0.4, 0.6, 0.8, 1.]),
4853 # np.array(0.4, dtype=np.float32), side="left")
4854 # returns 2 instead of 1 because 0.4 is not binary representable.
4855 if quantiles.dtype.kind == "f":
4856 cdf = cdf.astype(quantiles.dtype)
4857 # Weights must be non-negative, so we might have zero weights at the
4858 # beginning leading to some leading zeros in cdf. The call to
4859 # np.searchsorted for quantiles=0 will then pick the first element,
4860 # but should pick the first one larger than zero. We
4861 # therefore simply set 0 values in cdf to -1.
4862 if np.any(cdf[0, ...] == 0):
4863 cdf[cdf == 0] = -1
4864
4865 def find_cdf_1d(arr, cdf):
4866 indices = np.searchsorted(cdf, quantiles, side="left")
4867 # We might have reached the maximum with i = len(arr), e.g. for
4868 # quantiles = 1, and need to cut it to len(arr) - 1.
4869 indices = minimum(indices, values_count - 1)
4870 result = take(arr, indices, axis=0)
4871 return result
4872
4873 r_shape = arr.shape[1:]
4874 if quantiles.ndim > 0:
4875 r_shape = quantiles.shape + r_shape
4876 if out is None:
4877 result = np.empty_like(arr, shape=r_shape)
4878 else:
4879 if out.shape != r_shape:
4880 msg = (f"Wrong shape of argument 'out', shape={r_shape} is "
4881 f"required; got shape={out.shape}.")
4882 raise ValueError(msg)
4883 result = out
4884
4885 # See apply_along_axis, which we do for axis=0. Note that Ni = (,)
4886 # always, so we remove it here.
4887 Nk = arr.shape[1:]
4888 for kk in np.ndindex(Nk):
4889 result[(...,) + kk] = find_cdf_1d(
4890 arr[np.s_[:, ] + kk], cdf[np.s_[:, ] + kk]
4891 )
4892
4893 # Make result the same as in unweighted inverted_cdf.
4894 if result.shape == () and result.dtype == np.dtype("O"):
4895 result = result.item()
4896
4897 if np.any(slices_having_nans):
4898 if result.ndim == 0 and out is None:
4899 # can't write to a scalar, but indexing will be correct
4900 result = arr[-1]
4901 else:
4902 np.copyto(result, arr[-1, ...], where=slices_having_nans)
4903 return result
4904
4905
4906def _trapezoid_dispatcher(y, x=None, dx=None, axis=None):
4907 return (y, x)
4908
4909
4910@array_function_dispatch(_trapezoid_dispatcher)
4911def trapezoid(y, x=None, dx=1.0, axis=-1):
4912 r"""
4913 Integrate along the given axis using the composite trapezoidal rule.
4914
4915 If `x` is provided, the integration happens in sequence along its
4916 elements - they are not sorted.
4917
4918 Integrate `y` (`x`) along each 1d slice on the given axis, compute
4919 :math:`\int y(x) dx`.
4920 When `x` is specified, this integrates along the parametric curve,
4921 computing :math:`\int_t y(t) dt =
4922 \int_t y(t) \left.\frac{dx}{dt}\right|_{x=x(t)} dt`.
4923
4924 .. versionadded:: 2.0.0
4925
4926 Parameters
4927 ----------
4928 y : array_like
4929 Input array to integrate.
4930 x : array_like, optional
4931 The sample points corresponding to the `y` values. If `x` is None,
4932 the sample points are assumed to be evenly spaced `dx` apart. The
4933 default is None.
4934 dx : scalar, optional
4935 The spacing between sample points when `x` is None. The default is 1.
4936 axis : int, optional
4937 The axis along which to integrate.
4938
4939 Returns
4940 -------
4941 trapezoid : float or ndarray
4942 Definite integral of `y` = n-dimensional array as approximated along
4943 a single axis by the trapezoidal rule. If `y` is a 1-dimensional array,
4944 then the result is a float. If `n` is greater than 1, then the result
4945 is an `n`-1 dimensional array.
4946
4947 See Also
4948 --------
4949 sum, cumsum
4950
4951 Notes
4952 -----
4953 Image [2]_ illustrates trapezoidal rule -- y-axis locations of points
4954 will be taken from `y` array, by default x-axis distances between
4955 points will be 1.0, alternatively they can be provided with `x` array
4956 or with `dx` scalar. Return value will be equal to combined area under
4957 the red lines.
4958
4959
4960 References
4961 ----------
4962 .. [1] Wikipedia page: https://en.wikipedia.org/wiki/Trapezoidal_rule
4963
4964 .. [2] Illustration image:
4965 https://en.wikipedia.org/wiki/File:Composite_trapezoidal_rule_illustration.png
4966
4967 Examples
4968 --------
4969 >>> import numpy as np
4970
4971 Use the trapezoidal rule on evenly spaced points:
4972
4973 >>> np.trapezoid([1, 2, 3])
4974 4.0
4975
4976 The spacing between sample points can be selected by either the
4977 ``x`` or ``dx`` arguments:
4978
4979 >>> np.trapezoid([1, 2, 3], x=[4, 6, 8])
4980 8.0
4981 >>> np.trapezoid([1, 2, 3], dx=2)
4982 8.0
4983
4984 Using a decreasing ``x`` corresponds to integrating in reverse:
4985
4986 >>> np.trapezoid([1, 2, 3], x=[8, 6, 4])
4987 -8.0
4988
4989 More generally ``x`` is used to integrate along a parametric curve. We can
4990 estimate the integral :math:`\int_0^1 x^2 = 1/3` using:
4991
4992 >>> x = np.linspace(0, 1, num=50)
4993 >>> y = x**2
4994 >>> np.trapezoid(y, x)
4995 0.33340274885464394
4996
4997 Or estimate the area of a circle, noting we repeat the sample which closes
4998 the curve:
4999
5000 >>> theta = np.linspace(0, 2 * np.pi, num=1000, endpoint=True)
5001 >>> np.trapezoid(np.cos(theta), x=np.sin(theta))
5002 3.141571941375841
5003
5004 ``np.trapezoid`` can be applied along a specified axis to do multiple
5005 computations in one call:
5006
5007 >>> a = np.arange(6).reshape(2, 3)
5008 >>> a
5009 array([[0, 1, 2],
5010 [3, 4, 5]])
5011 >>> np.trapezoid(a, axis=0)
5012 array([1.5, 2.5, 3.5])
5013 >>> np.trapezoid(a, axis=1)
5014 array([2., 8.])
5015 """
5016
5017 y = asanyarray(y)
5018 if x is None:
5019 d = dx
5020 else:
5021 x = asanyarray(x)
5022 if x.ndim == 1:
5023 d = diff(x)
5024 # reshape to correct shape
5025 shape = [1] * y.ndim
5026 shape[axis] = d.shape[0]
5027 d = d.reshape(shape)
5028 else:
5029 d = diff(x, axis=axis)
5030 nd = y.ndim
5031 slice1 = [slice(None)] * nd
5032 slice2 = [slice(None)] * nd
5033 slice1[axis] = slice(1, None)
5034 slice2[axis] = slice(None, -1)
5035 try:
5036 ret = (d * (y[tuple(slice1)] + y[tuple(slice2)]) / 2.0).sum(axis)
5037 except ValueError:
5038 # Operations didn't work, cast to ndarray
5039 d = np.asarray(d)
5040 y = np.asarray(y)
5041 ret = add.reduce(d * (y[tuple(slice1)] + y[tuple(slice2)]) / 2.0, axis)
5042 return ret
5043
5044
5045def _meshgrid_dispatcher(*xi, copy=None, sparse=None, indexing=None):
5046 return xi
5047
5048
5049# Based on scitools meshgrid
5050@array_function_dispatch(_meshgrid_dispatcher)
5051def meshgrid(*xi, copy=True, sparse=False, indexing='xy'):
5052 """
5053 Return a tuple of coordinate matrices from coordinate vectors.
5054
5055 Make N-D coordinate arrays for vectorized evaluations of
5056 N-D scalar/vector fields over N-D grids, given
5057 one-dimensional coordinate arrays x1, x2,..., xn.
5058
5059 Parameters
5060 ----------
5061 x1, x2,..., xn : array_like
5062 1-D arrays representing the coordinates of a grid.
5063 indexing : {'xy', 'ij'}, optional
5064 Cartesian ('xy', default) or matrix ('ij') indexing of output.
5065 See Notes for more details.
5066 sparse : bool, optional
5067 If True the shape of the returned coordinate array for dimension *i*
5068 is reduced from ``(N1, ..., Ni, ... Nn)`` to
5069 ``(1, ..., 1, Ni, 1, ..., 1)``. These sparse coordinate grids are
5070 intended to be used with :ref:`basics.broadcasting`. When all
5071 coordinates are used in an expression, broadcasting still leads to a
5072 fully-dimensonal result array.
5073
5074 Default is False.
5075
5076 copy : bool, optional
5077 If False, a view into the original arrays are returned in order to
5078 conserve memory. Default is True. Please note that
5079 ``sparse=False, copy=False`` will likely return non-contiguous
5080 arrays. Furthermore, more than one element of a broadcast array
5081 may refer to a single memory location. If you need to write to the
5082 arrays, make copies first.
5083
5084 Returns
5085 -------
5086 X1, X2,..., XN : tuple of ndarrays
5087 For vectors `x1`, `x2`,..., `xn` with lengths ``Ni=len(xi)``,
5088 returns ``(N1, N2, N3,..., Nn)`` shaped arrays if indexing='ij'
5089 or ``(N2, N1, N3,..., Nn)`` shaped arrays if indexing='xy'
5090 with the elements of `xi` repeated to fill the matrix along
5091 the first dimension for `x1`, the second for `x2` and so on.
5092
5093 Notes
5094 -----
5095 This function supports both indexing conventions through the indexing
5096 keyword argument. Giving the string 'ij' returns a meshgrid with
5097 matrix indexing, while 'xy' returns a meshgrid with Cartesian indexing.
5098 In the 2-D case with inputs of length M and N, the outputs are of shape
5099 (N, M) for 'xy' indexing and (M, N) for 'ij' indexing. In the 3-D case
5100 with inputs of length M, N and P, outputs are of shape (N, M, P) for
5101 'xy' indexing and (M, N, P) for 'ij' indexing. The difference is
5102 illustrated by the following code snippet::
5103
5104 xv, yv = np.meshgrid(x, y, indexing='ij')
5105 for i in range(nx):
5106 for j in range(ny):
5107 # treat xv[i,j], yv[i,j]
5108
5109 xv, yv = np.meshgrid(x, y, indexing='xy')
5110 for i in range(nx):
5111 for j in range(ny):
5112 # treat xv[j,i], yv[j,i]
5113
5114 In the 1-D and 0-D case, the indexing and sparse keywords have no effect.
5115
5116 See Also
5117 --------
5118 mgrid : Construct a multi-dimensional "meshgrid" using indexing notation.
5119 ogrid : Construct an open multi-dimensional "meshgrid" using indexing
5120 notation.
5121 :ref:`how-to-index`
5122
5123 Examples
5124 --------
5125 >>> import numpy as np
5126 >>> nx, ny = (3, 2)
5127 >>> x = np.linspace(0, 1, nx)
5128 >>> y = np.linspace(0, 1, ny)
5129 >>> xv, yv = np.meshgrid(x, y)
5130 >>> xv
5131 array([[0. , 0.5, 1. ],
5132 [0. , 0.5, 1. ]])
5133 >>> yv
5134 array([[0., 0., 0.],
5135 [1., 1., 1.]])
5136
5137 The result of `meshgrid` is a coordinate grid:
5138
5139 >>> import matplotlib.pyplot as plt
5140 >>> plt.plot(xv, yv, marker='o', color='k', linestyle='none')
5141 >>> plt.show()
5142
5143 You can create sparse output arrays to save memory and computation time.
5144
5145 >>> xv, yv = np.meshgrid(x, y, sparse=True)
5146 >>> xv
5147 array([[0. , 0.5, 1. ]])
5148 >>> yv
5149 array([[0.],
5150 [1.]])
5151
5152 `meshgrid` is very useful to evaluate functions on a grid. If the
5153 function depends on all coordinates, both dense and sparse outputs can be
5154 used.
5155
5156 >>> x = np.linspace(-5, 5, 101)
5157 >>> y = np.linspace(-5, 5, 101)
5158 >>> # full coordinate arrays
5159 >>> xx, yy = np.meshgrid(x, y)
5160 >>> zz = np.sqrt(xx**2 + yy**2)
5161 >>> xx.shape, yy.shape, zz.shape
5162 ((101, 101), (101, 101), (101, 101))
5163 >>> # sparse coordinate arrays
5164 >>> xs, ys = np.meshgrid(x, y, sparse=True)
5165 >>> zs = np.sqrt(xs**2 + ys**2)
5166 >>> xs.shape, ys.shape, zs.shape
5167 ((1, 101), (101, 1), (101, 101))
5168 >>> np.array_equal(zz, zs)
5169 True
5170
5171 >>> h = plt.contourf(x, y, zs)
5172 >>> plt.axis('scaled')
5173 >>> plt.colorbar()
5174 >>> plt.show()
5175 """
5176 ndim = len(xi)
5177
5178 if indexing not in ['xy', 'ij']:
5179 raise ValueError(
5180 "Valid values for `indexing` are 'xy' and 'ij'.")
5181
5182 s0 = (1,) * ndim
5183 output = [np.asanyarray(x).reshape(s0[:i] + (-1,) + s0[i + 1:])
5184 for i, x in enumerate(xi)]
5185
5186 if indexing == 'xy' and ndim > 1:
5187 # switch first and second axis
5188 output[0].shape = (1, -1) + s0[2:]
5189 output[1].shape = (-1, 1) + s0[2:]
5190
5191 if not sparse:
5192 # Return the full N-D matrix (not only the 1-D vector)
5193 output = np.broadcast_arrays(*output, subok=True)
5194
5195 if copy:
5196 output = tuple(x.copy() for x in output)
5197
5198 return output
5199
5200
5201def _delete_dispatcher(arr, obj, axis=None):
5202 return (arr, obj)
5203
5204
5205@array_function_dispatch(_delete_dispatcher)
5206def delete(arr, obj, axis=None):
5207 """
5208 Return a new array with sub-arrays along an axis deleted. For a one
5209 dimensional array, this returns those entries not returned by
5210 `arr[obj]`.
5211
5212 Parameters
5213 ----------
5214 arr : array_like
5215 Input array.
5216 obj : slice, int, array-like of ints or bools
5217 Indicate indices of sub-arrays to remove along the specified axis.
5218
5219 .. versionchanged:: 1.19.0
5220 Boolean indices are now treated as a mask of elements to remove,
5221 rather than being cast to the integers 0 and 1.
5222
5223 axis : int, optional
5224 The axis along which to delete the subarray defined by `obj`.
5225 If `axis` is None, `obj` is applied to the flattened array.
5226
5227 Returns
5228 -------
5229 out : ndarray
5230 A copy of `arr` with the elements specified by `obj` removed. Note
5231 that `delete` does not occur in-place. If `axis` is None, `out` is
5232 a flattened array.
5233
5234 See Also
5235 --------
5236 insert : Insert elements into an array.
5237 append : Append elements at the end of an array.
5238
5239 Notes
5240 -----
5241 Often it is preferable to use a boolean mask. For example:
5242
5243 >>> arr = np.arange(12) + 1
5244 >>> mask = np.ones(len(arr), dtype=bool)
5245 >>> mask[[0,2,4]] = False
5246 >>> result = arr[mask,...]
5247
5248 Is equivalent to ``np.delete(arr, [0,2,4], axis=0)``, but allows further
5249 use of `mask`.
5250
5251 Examples
5252 --------
5253 >>> import numpy as np
5254 >>> arr = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
5255 >>> arr
5256 array([[ 1, 2, 3, 4],
5257 [ 5, 6, 7, 8],
5258 [ 9, 10, 11, 12]])
5259 >>> np.delete(arr, 1, 0)
5260 array([[ 1, 2, 3, 4],
5261 [ 9, 10, 11, 12]])
5262
5263 >>> np.delete(arr, np.s_[::2], 1)
5264 array([[ 2, 4],
5265 [ 6, 8],
5266 [10, 12]])
5267 >>> np.delete(arr, [1,3,5], None)
5268 array([ 1, 3, 5, 7, 8, 9, 10, 11, 12])
5269
5270 """
5271 conv = _array_converter(arr)
5272 arr, = conv.as_arrays(subok=False)
5273
5274 ndim = arr.ndim
5275 arrorder = 'F' if arr.flags.fnc else 'C'
5276 if axis is None:
5277 if ndim != 1:
5278 arr = arr.ravel()
5279 # needed for np.matrix, which is still not 1d after being ravelled
5280 ndim = arr.ndim
5281 axis = ndim - 1
5282 else:
5283 axis = normalize_axis_index(axis, ndim)
5284
5285 slobj = [slice(None)] * ndim
5286 N = arr.shape[axis]
5287 newshape = list(arr.shape)
5288
5289 if isinstance(obj, slice):
5290 start, stop, step = obj.indices(N)
5291 xr = range(start, stop, step)
5292 numtodel = len(xr)
5293
5294 if numtodel <= 0:
5295 return conv.wrap(arr.copy(order=arrorder), to_scalar=False)
5296
5297 # Invert if step is negative:
5298 if step < 0:
5299 step = -step
5300 start = xr[-1]
5301 stop = xr[0] + 1
5302
5303 newshape[axis] -= numtodel
5304 new = empty(newshape, arr.dtype, arrorder)
5305 # copy initial chunk
5306 if start == 0:
5307 pass
5308 else:
5309 slobj[axis] = slice(None, start)
5310 new[tuple(slobj)] = arr[tuple(slobj)]
5311 # copy end chunk
5312 if stop == N:
5313 pass
5314 else:
5315 slobj[axis] = slice(stop - numtodel, None)
5316 slobj2 = [slice(None)] * ndim
5317 slobj2[axis] = slice(stop, None)
5318 new[tuple(slobj)] = arr[tuple(slobj2)]
5319 # copy middle pieces
5320 if step == 1:
5321 pass
5322 else: # use array indexing.
5323 keep = ones(stop - start, dtype=bool)
5324 keep[:stop - start:step] = False
5325 slobj[axis] = slice(start, stop - numtodel)
5326 slobj2 = [slice(None)] * ndim
5327 slobj2[axis] = slice(start, stop)
5328 arr = arr[tuple(slobj2)]
5329 slobj2[axis] = keep
5330 new[tuple(slobj)] = arr[tuple(slobj2)]
5331
5332 return conv.wrap(new, to_scalar=False)
5333
5334 if isinstance(obj, (int, integer)) and not isinstance(obj, bool):
5335 single_value = True
5336 else:
5337 single_value = False
5338 _obj = obj
5339 obj = np.asarray(obj)
5340 # `size == 0` to allow empty lists similar to indexing, but (as there)
5341 # is really too generic:
5342 if obj.size == 0 and not isinstance(_obj, np.ndarray):
5343 obj = obj.astype(intp)
5344 elif obj.size == 1 and obj.dtype.kind in "ui":
5345 # For a size 1 integer array we can use the single-value path
5346 # (most dtypes, except boolean, should just fail later).
5347 obj = obj.item()
5348 single_value = True
5349
5350 if single_value:
5351 # optimization for a single value
5352 if (obj < -N or obj >= N):
5353 raise IndexError(
5354 f"index {obj} is out of bounds for axis {axis} with "
5355 f"size {N}")
5356 if (obj < 0):
5357 obj += N
5358 newshape[axis] -= 1
5359 new = empty(newshape, arr.dtype, arrorder)
5360 slobj[axis] = slice(None, obj)
5361 new[tuple(slobj)] = arr[tuple(slobj)]
5362 slobj[axis] = slice(obj, None)
5363 slobj2 = [slice(None)] * ndim
5364 slobj2[axis] = slice(obj + 1, None)
5365 new[tuple(slobj)] = arr[tuple(slobj2)]
5366 else:
5367 if obj.dtype == bool:
5368 if obj.shape != (N,):
5369 raise ValueError('boolean array argument obj to delete '
5370 'must be one dimensional and match the axis '
5371 f'length of {N}')
5372
5373 # optimization, the other branch is slower
5374 keep = ~obj
5375 else:
5376 keep = ones(N, dtype=bool)
5377 keep[obj,] = False
5378
5379 slobj[axis] = keep
5380 new = arr[tuple(slobj)]
5381
5382 return conv.wrap(new, to_scalar=False)
5383
5384
5385def _insert_dispatcher(arr, obj, values, axis=None):
5386 return (arr, obj, values)
5387
5388
5389@array_function_dispatch(_insert_dispatcher)
5390def insert(arr, obj, values, axis=None):
5391 """
5392 Insert values along the given axis before the given indices.
5393
5394 Parameters
5395 ----------
5396 arr : array_like
5397 Input array.
5398 obj : slice, int, array-like of ints or bools
5399 Object that defines the index or indices before which `values` is
5400 inserted.
5401
5402 .. versionchanged:: 2.1.2
5403 Boolean indices are now treated as a mask of elements to insert,
5404 rather than being cast to the integers 0 and 1.
5405
5406 Support for multiple insertions when `obj` is a single scalar or a
5407 sequence with one element (similar to calling insert multiple
5408 times).
5409 values : array_like
5410 Values to insert into `arr`. If the type of `values` is different
5411 from that of `arr`, `values` is converted to the type of `arr`.
5412 `values` should be shaped so that ``arr[...,obj,...] = values``
5413 is legal.
5414 axis : int, optional
5415 Axis along which to insert `values`. If `axis` is None then `arr`
5416 is flattened first.
5417
5418 Returns
5419 -------
5420 out : ndarray
5421 A copy of `arr` with `values` inserted. Note that `insert`
5422 does not occur in-place: a new array is returned. If
5423 `axis` is None, `out` is a flattened array.
5424
5425 See Also
5426 --------
5427 append : Append elements at the end of an array.
5428 concatenate : Join a sequence of arrays along an existing axis.
5429 delete : Delete elements from an array.
5430
5431 Notes
5432 -----
5433 Note that for higher dimensional inserts ``obj=0`` behaves very different
5434 from ``obj=[0]`` just like ``arr[:,0,:] = values`` is different from
5435 ``arr[:,[0],:] = values``. This is because of the difference between basic
5436 and advanced :ref:`indexing <basics.indexing>`.
5437
5438 Examples
5439 --------
5440 >>> import numpy as np
5441 >>> a = np.arange(6).reshape(3, 2)
5442 >>> a
5443 array([[0, 1],
5444 [2, 3],
5445 [4, 5]])
5446 >>> np.insert(a, 1, 6)
5447 array([0, 6, 1, 2, 3, 4, 5])
5448 >>> np.insert(a, 1, 6, axis=1)
5449 array([[0, 6, 1],
5450 [2, 6, 3],
5451 [4, 6, 5]])
5452
5453 Difference between sequence and scalars,
5454 showing how ``obj=[1]`` behaves different from ``obj=1``:
5455
5456 >>> np.insert(a, [1], [[7],[8],[9]], axis=1)
5457 array([[0, 7, 1],
5458 [2, 8, 3],
5459 [4, 9, 5]])
5460 >>> np.insert(a, 1, [[7],[8],[9]], axis=1)
5461 array([[0, 7, 8, 9, 1],
5462 [2, 7, 8, 9, 3],
5463 [4, 7, 8, 9, 5]])
5464 >>> np.array_equal(np.insert(a, 1, [7, 8, 9], axis=1),
5465 ... np.insert(a, [1], [[7],[8],[9]], axis=1))
5466 True
5467
5468 >>> b = a.flatten()
5469 >>> b
5470 array([0, 1, 2, 3, 4, 5])
5471 >>> np.insert(b, [2, 2], [6, 7])
5472 array([0, 1, 6, 7, 2, 3, 4, 5])
5473
5474 >>> np.insert(b, slice(2, 4), [7, 8])
5475 array([0, 1, 7, 2, 8, 3, 4, 5])
5476
5477 >>> np.insert(b, [2, 2], [7.13, False]) # type casting
5478 array([0, 1, 7, 0, 2, 3, 4, 5])
5479
5480 >>> x = np.arange(8).reshape(2, 4)
5481 >>> idx = (1, 3)
5482 >>> np.insert(x, idx, 999, axis=1)
5483 array([[ 0, 999, 1, 2, 999, 3],
5484 [ 4, 999, 5, 6, 999, 7]])
5485
5486 """
5487 conv = _array_converter(arr)
5488 arr, = conv.as_arrays(subok=False)
5489
5490 ndim = arr.ndim
5491 arrorder = 'F' if arr.flags.fnc else 'C'
5492 if axis is None:
5493 if ndim != 1:
5494 arr = arr.ravel()
5495 # needed for np.matrix, which is still not 1d after being ravelled
5496 ndim = arr.ndim
5497 axis = ndim - 1
5498 else:
5499 axis = normalize_axis_index(axis, ndim)
5500 slobj = [slice(None)] * ndim
5501 N = arr.shape[axis]
5502 newshape = list(arr.shape)
5503
5504 if isinstance(obj, slice):
5505 # turn it into a range object
5506 indices = arange(*obj.indices(N), dtype=intp)
5507 else:
5508 # need to copy obj, because indices will be changed in-place
5509 indices = np.array(obj)
5510 if indices.dtype == bool:
5511 if obj.ndim != 1:
5512 raise ValueError('boolean array argument obj to insert '
5513 'must be one dimensional')
5514 indices = np.flatnonzero(obj)
5515 elif indices.ndim > 1:
5516 raise ValueError(
5517 "index array argument obj to insert must be one dimensional "
5518 "or scalar")
5519 if indices.size == 1:
5520 index = indices.item()
5521 if index < -N or index > N:
5522 raise IndexError(f"index {obj} is out of bounds for axis {axis} "
5523 f"with size {N}")
5524 if (index < 0):
5525 index += N
5526
5527 # There are some object array corner cases here, but we cannot avoid
5528 # that:
5529 values = array(values, copy=None, ndmin=arr.ndim, dtype=arr.dtype)
5530 if indices.ndim == 0:
5531 # broadcasting is very different here, since a[:,0,:] = ... behaves
5532 # very different from a[:,[0],:] = ...! This changes values so that
5533 # it works likes the second case. (here a[:,0:1,:])
5534 values = np.moveaxis(values, 0, axis)
5535 numnew = values.shape[axis]
5536 newshape[axis] += numnew
5537 new = empty(newshape, arr.dtype, arrorder)
5538 slobj[axis] = slice(None, index)
5539 new[tuple(slobj)] = arr[tuple(slobj)]
5540 slobj[axis] = slice(index, index + numnew)
5541 new[tuple(slobj)] = values
5542 slobj[axis] = slice(index + numnew, None)
5543 slobj2 = [slice(None)] * ndim
5544 slobj2[axis] = slice(index, None)
5545 new[tuple(slobj)] = arr[tuple(slobj2)]
5546
5547 return conv.wrap(new, to_scalar=False)
5548
5549 elif indices.size == 0 and not isinstance(obj, np.ndarray):
5550 # Can safely cast the empty list to intp
5551 indices = indices.astype(intp)
5552
5553 indices[indices < 0] += N
5554
5555 numnew = len(indices)
5556 order = indices.argsort(kind='mergesort') # stable sort
5557 indices[order] += np.arange(numnew)
5558
5559 newshape[axis] += numnew
5560 old_mask = ones(newshape[axis], dtype=bool)
5561 old_mask[indices] = False
5562
5563 new = empty(newshape, arr.dtype, arrorder)
5564 slobj2 = [slice(None)] * ndim
5565 slobj[axis] = indices
5566 slobj2[axis] = old_mask
5567 new[tuple(slobj)] = values
5568 new[tuple(slobj2)] = arr
5569
5570 return conv.wrap(new, to_scalar=False)
5571
5572
5573def _append_dispatcher(arr, values, axis=None):
5574 return (arr, values)
5575
5576
5577@array_function_dispatch(_append_dispatcher)
5578def append(arr, values, axis=None):
5579 """
5580 Append values to the end of an array.
5581
5582 Parameters
5583 ----------
5584 arr : array_like
5585 Values are appended to a copy of this array.
5586 values : array_like
5587 These values are appended to a copy of `arr`. It must be of the
5588 correct shape (the same shape as `arr`, excluding `axis`). If
5589 `axis` is not specified, `values` can be any shape and will be
5590 flattened before use.
5591 axis : int, optional
5592 The axis along which `values` are appended. If `axis` is not
5593 given, both `arr` and `values` are flattened before use.
5594
5595 Returns
5596 -------
5597 append : ndarray
5598 A copy of `arr` with `values` appended to `axis`. Note that
5599 `append` does not occur in-place: a new array is allocated and
5600 filled. If `axis` is None, `out` is a flattened array.
5601
5602 See Also
5603 --------
5604 insert : Insert elements into an array.
5605 delete : Delete elements from an array.
5606
5607 Examples
5608 --------
5609 >>> import numpy as np
5610 >>> np.append([1, 2, 3], [[4, 5, 6], [7, 8, 9]])
5611 array([1, 2, 3, ..., 7, 8, 9])
5612
5613 When `axis` is specified, `values` must have the correct shape.
5614
5615 >>> np.append([[1, 2, 3], [4, 5, 6]], [[7, 8, 9]], axis=0)
5616 array([[1, 2, 3],
5617 [4, 5, 6],
5618 [7, 8, 9]])
5619
5620 >>> np.append([[1, 2, 3], [4, 5, 6]], [7, 8, 9], axis=0)
5621 Traceback (most recent call last):
5622 ...
5623 ValueError: all the input arrays must have same number of dimensions, but
5624 the array at index 0 has 2 dimension(s) and the array at index 1 has 1
5625 dimension(s)
5626
5627 >>> a = np.array([1, 2], dtype=int)
5628 >>> c = np.append(a, [])
5629 >>> c
5630 array([1., 2.])
5631 >>> c.dtype
5632 float64
5633
5634 Default dtype for empty ndarrays is `float64` thus making the output of dtype
5635 `float64` when appended with dtype `int64`
5636
5637 """
5638 arr = asanyarray(arr)
5639 if axis is None:
5640 if arr.ndim != 1:
5641 arr = arr.ravel()
5642 values = ravel(values)
5643 axis = arr.ndim - 1
5644 return concatenate((arr, values), axis=axis)
5645
5646
5647def _digitize_dispatcher(x, bins, right=None):
5648 return (x, bins)
5649
5650
5651@array_function_dispatch(_digitize_dispatcher)
5652def digitize(x, bins, right=False):
5653 """
5654 Return the indices of the bins to which each value in input array belongs.
5655
5656 ========= ============= ============================
5657 `right` order of bins returned index `i` satisfies
5658 ========= ============= ============================
5659 ``False`` increasing ``bins[i-1] <= x < bins[i]``
5660 ``True`` increasing ``bins[i-1] < x <= bins[i]``
5661 ``False`` decreasing ``bins[i-1] > x >= bins[i]``
5662 ``True`` decreasing ``bins[i-1] >= x > bins[i]``
5663 ========= ============= ============================
5664
5665 If values in `x` are beyond the bounds of `bins`, 0 or ``len(bins)`` is
5666 returned as appropriate.
5667
5668 Parameters
5669 ----------
5670 x : array_like
5671 Input array to be binned. Prior to NumPy 1.10.0, this array had to
5672 be 1-dimensional, but can now have any shape.
5673 bins : array_like
5674 Array of bins. It has to be 1-dimensional and monotonic.
5675 right : bool, optional
5676 Indicating whether the intervals include the right or the left bin
5677 edge. Default behavior is (right==False) indicating that the interval
5678 does not include the right edge. The left bin end is open in this
5679 case, i.e., bins[i-1] <= x < bins[i] is the default behavior for
5680 monotonically increasing bins.
5681
5682 Returns
5683 -------
5684 indices : ndarray of ints
5685 Output array of indices, of same shape as `x`.
5686
5687 Raises
5688 ------
5689 ValueError
5690 If `bins` is not monotonic.
5691 TypeError
5692 If the type of the input is complex.
5693
5694 See Also
5695 --------
5696 bincount, histogram, unique, searchsorted
5697
5698 Notes
5699 -----
5700 If values in `x` are such that they fall outside the bin range,
5701 attempting to index `bins` with the indices that `digitize` returns
5702 will result in an IndexError.
5703
5704 .. versionadded:: 1.10.0
5705
5706 `numpy.digitize` is implemented in terms of `numpy.searchsorted`.
5707 This means that a binary search is used to bin the values, which scales
5708 much better for larger number of bins than the previous linear search.
5709 It also removes the requirement for the input array to be 1-dimensional.
5710
5711 For monotonically *increasing* `bins`, the following are equivalent::
5712
5713 np.digitize(x, bins, right=True)
5714 np.searchsorted(bins, x, side='left')
5715
5716 Note that as the order of the arguments are reversed, the side must be too.
5717 The `searchsorted` call is marginally faster, as it does not do any
5718 monotonicity checks. Perhaps more importantly, it supports all dtypes.
5719
5720 Examples
5721 --------
5722 >>> import numpy as np
5723 >>> x = np.array([0.2, 6.4, 3.0, 1.6])
5724 >>> bins = np.array([0.0, 1.0, 2.5, 4.0, 10.0])
5725 >>> inds = np.digitize(x, bins)
5726 >>> inds
5727 array([1, 4, 3, 2])
5728 >>> for n in range(x.size):
5729 ... print(bins[inds[n]-1], "<=", x[n], "<", bins[inds[n]])
5730 ...
5731 0.0 <= 0.2 < 1.0
5732 4.0 <= 6.4 < 10.0
5733 2.5 <= 3.0 < 4.0
5734 1.0 <= 1.6 < 2.5
5735
5736 >>> x = np.array([1.2, 10.0, 12.4, 15.5, 20.])
5737 >>> bins = np.array([0, 5, 10, 15, 20])
5738 >>> np.digitize(x,bins,right=True)
5739 array([1, 2, 3, 4, 4])
5740 >>> np.digitize(x,bins,right=False)
5741 array([1, 3, 3, 4, 5])
5742 """
5743 x = _nx.asarray(x)
5744 bins = _nx.asarray(bins)
5745
5746 # here for compatibility, searchsorted below is happy to take this
5747 if np.issubdtype(x.dtype, _nx.complexfloating):
5748 raise TypeError("x may not be complex")
5749
5750 mono = _monotonicity(bins)
5751 if mono == 0:
5752 raise ValueError("bins must be monotonically increasing or decreasing")
5753
5754 # this is backwards because the arguments below are swapped
5755 side = 'left' if right else 'right'
5756 if mono == -1:
5757 # reverse the bins, and invert the results
5758 return len(bins) - _nx.searchsorted(bins[::-1], x, side=side)
5759 else:
5760 return _nx.searchsorted(bins, x, side=side)