1import functools
2import warnings
3
4import numpy as np
5import numpy._core.numeric as _nx
6from numpy._core import atleast_3d, overrides, vstack
7from numpy._core._multiarray_umath import _array_converter
8from numpy._core.fromnumeric import reshape, transpose
9from numpy._core.multiarray import normalize_axis_index
10from numpy._core.numeric import (
11 array,
12 asanyarray,
13 asarray,
14 normalize_axis_tuple,
15 zeros,
16 zeros_like,
17)
18from numpy._core.overrides import set_module
19from numpy._core.shape_base import _arrays_for_stack_dispatcher
20from numpy.lib._index_tricks_impl import ndindex
21from numpy.matrixlib.defmatrix import matrix # this raises all the right alarm bells
22
23__all__ = [
24 'column_stack', 'row_stack', 'dstack', 'array_split', 'split',
25 'hsplit', 'vsplit', 'dsplit', 'apply_over_axes', 'expand_dims',
26 'apply_along_axis', 'kron', 'tile', 'take_along_axis',
27 'put_along_axis'
28 ]
29
30
31array_function_dispatch = functools.partial(
32 overrides.array_function_dispatch, module='numpy')
33
34
35def _make_along_axis_idx(arr_shape, indices, axis):
36 # compute dimensions to iterate over
37 if not _nx.issubdtype(indices.dtype, _nx.integer):
38 raise IndexError('`indices` must be an integer array')
39 if len(arr_shape) != indices.ndim:
40 raise ValueError(
41 "`indices` and `arr` must have the same number of dimensions")
42 shape_ones = (1,) * indices.ndim
43 dest_dims = list(range(axis)) + [None] + list(range(axis + 1, indices.ndim))
44
45 # build a fancy index, consisting of orthogonal aranges, with the
46 # requested index inserted at the right location
47 fancy_index = []
48 for dim, n in zip(dest_dims, arr_shape):
49 if dim is None:
50 fancy_index.append(indices)
51 else:
52 ind_shape = shape_ones[:dim] + (-1,) + shape_ones[dim + 1:]
53 fancy_index.append(_nx.arange(n).reshape(ind_shape))
54
55 return tuple(fancy_index)
56
57
58def _take_along_axis_dispatcher(arr, indices, axis=None):
59 return (arr, indices)
60
61
62@array_function_dispatch(_take_along_axis_dispatcher)
63def take_along_axis(arr, indices, axis=-1):
64 """
65 Take values from the input array by matching 1d index and data slices.
66
67 This iterates over matching 1d slices oriented along the specified axis in
68 the index and data arrays, and uses the former to look up values in the
69 latter. These slices can be different lengths.
70
71 Functions returning an index along an axis, like `argsort` and
72 `argpartition`, produce suitable indices for this function.
73
74 Parameters
75 ----------
76 arr : ndarray (Ni..., M, Nk...)
77 Source array
78 indices : ndarray (Ni..., J, Nk...)
79 Indices to take along each 1d slice of ``arr``. This must match the
80 dimension of ``arr``, but dimensions Ni and Nj only need to broadcast
81 against ``arr``.
82 axis : int or None, optional
83 The axis to take 1d slices along. If axis is None, the input array is
84 treated as if it had first been flattened to 1d, for consistency with
85 `sort` and `argsort`.
86
87 .. versionchanged:: 2.3
88 The default value is now ``-1``.
89
90 Returns
91 -------
92 out: ndarray (Ni..., J, Nk...)
93 The indexed result.
94
95 Notes
96 -----
97 This is equivalent to (but faster than) the following use of `ndindex` and
98 `s_`, which sets each of ``ii`` and ``kk`` to a tuple of indices::
99
100 Ni, M, Nk = a.shape[:axis], a.shape[axis], a.shape[axis+1:]
101 J = indices.shape[axis] # Need not equal M
102 out = np.empty(Ni + (J,) + Nk)
103
104 for ii in ndindex(Ni):
105 for kk in ndindex(Nk):
106 a_1d = a [ii + s_[:,] + kk]
107 indices_1d = indices[ii + s_[:,] + kk]
108 out_1d = out [ii + s_[:,] + kk]
109 for j in range(J):
110 out_1d[j] = a_1d[indices_1d[j]]
111
112 Equivalently, eliminating the inner loop, the last two lines would be::
113
114 out_1d[:] = a_1d[indices_1d]
115
116 See Also
117 --------
118 take : Take along an axis, using the same indices for every 1d slice
119 put_along_axis :
120 Put values into the destination array by matching 1d index and data slices
121
122 Examples
123 --------
124 >>> import numpy as np
125
126 For this sample array
127
128 >>> a = np.array([[10, 30, 20], [60, 40, 50]])
129
130 We can sort either by using sort directly, or argsort and this function
131
132 >>> np.sort(a, axis=1)
133 array([[10, 20, 30],
134 [40, 50, 60]])
135 >>> ai = np.argsort(a, axis=1)
136 >>> ai
137 array([[0, 2, 1],
138 [1, 2, 0]])
139 >>> np.take_along_axis(a, ai, axis=1)
140 array([[10, 20, 30],
141 [40, 50, 60]])
142
143 The same works for max and min, if you maintain the trivial dimension
144 with ``keepdims``:
145
146 >>> np.max(a, axis=1, keepdims=True)
147 array([[30],
148 [60]])
149 >>> ai = np.argmax(a, axis=1, keepdims=True)
150 >>> ai
151 array([[1],
152 [0]])
153 >>> np.take_along_axis(a, ai, axis=1)
154 array([[30],
155 [60]])
156
157 If we want to get the max and min at the same time, we can stack the
158 indices first
159
160 >>> ai_min = np.argmin(a, axis=1, keepdims=True)
161 >>> ai_max = np.argmax(a, axis=1, keepdims=True)
162 >>> ai = np.concatenate([ai_min, ai_max], axis=1)
163 >>> ai
164 array([[0, 1],
165 [1, 0]])
166 >>> np.take_along_axis(a, ai, axis=1)
167 array([[10, 30],
168 [40, 60]])
169 """
170 # normalize inputs
171 if axis is None:
172 if indices.ndim != 1:
173 raise ValueError(
174 'when axis=None, `indices` must have a single dimension.')
175 arr = np.array(arr.flat)
176 axis = 0
177 else:
178 axis = normalize_axis_index(axis, arr.ndim)
179
180 # use the fancy index
181 return arr[_make_along_axis_idx(arr.shape, indices, axis)]
182
183
184def _put_along_axis_dispatcher(arr, indices, values, axis):
185 return (arr, indices, values)
186
187
188@array_function_dispatch(_put_along_axis_dispatcher)
189def put_along_axis(arr, indices, values, axis):
190 """
191 Put values into the destination array by matching 1d index and data slices.
192
193 This iterates over matching 1d slices oriented along the specified axis in
194 the index and data arrays, and uses the former to place values into the
195 latter. These slices can be different lengths.
196
197 Functions returning an index along an axis, like `argsort` and
198 `argpartition`, produce suitable indices for this function.
199
200 Parameters
201 ----------
202 arr : ndarray (Ni..., M, Nk...)
203 Destination array.
204 indices : ndarray (Ni..., J, Nk...)
205 Indices to change along each 1d slice of `arr`. This must match the
206 dimension of arr, but dimensions in Ni and Nj may be 1 to broadcast
207 against `arr`.
208 values : array_like (Ni..., J, Nk...)
209 values to insert at those indices. Its shape and dimension are
210 broadcast to match that of `indices`.
211 axis : int
212 The axis to take 1d slices along. If axis is None, the destination
213 array is treated as if a flattened 1d view had been created of it.
214
215 Notes
216 -----
217 This is equivalent to (but faster than) the following use of `ndindex` and
218 `s_`, which sets each of ``ii`` and ``kk`` to a tuple of indices::
219
220 Ni, M, Nk = a.shape[:axis], a.shape[axis], a.shape[axis+1:]
221 J = indices.shape[axis] # Need not equal M
222
223 for ii in ndindex(Ni):
224 for kk in ndindex(Nk):
225 a_1d = a [ii + s_[:,] + kk]
226 indices_1d = indices[ii + s_[:,] + kk]
227 values_1d = values [ii + s_[:,] + kk]
228 for j in range(J):
229 a_1d[indices_1d[j]] = values_1d[j]
230
231 Equivalently, eliminating the inner loop, the last two lines would be::
232
233 a_1d[indices_1d] = values_1d
234
235 See Also
236 --------
237 take_along_axis :
238 Take values from the input array by matching 1d index and data slices
239
240 Examples
241 --------
242 >>> import numpy as np
243
244 For this sample array
245
246 >>> a = np.array([[10, 30, 20], [60, 40, 50]])
247
248 We can replace the maximum values with:
249
250 >>> ai = np.argmax(a, axis=1, keepdims=True)
251 >>> ai
252 array([[1],
253 [0]])
254 >>> np.put_along_axis(a, ai, 99, axis=1)
255 >>> a
256 array([[10, 99, 20],
257 [99, 40, 50]])
258
259 """
260 # normalize inputs
261 if axis is None:
262 if indices.ndim != 1:
263 raise ValueError(
264 'when axis=None, `indices` must have a single dimension.')
265 arr = np.array(arr.flat)
266 axis = 0
267 else:
268 axis = normalize_axis_index(axis, arr.ndim)
269
270 # use the fancy index
271 arr[_make_along_axis_idx(arr.shape, indices, axis)] = values
272
273
274def _apply_along_axis_dispatcher(func1d, axis, arr, *args, **kwargs):
275 return (arr,)
276
277
278@array_function_dispatch(_apply_along_axis_dispatcher)
279def apply_along_axis(func1d, axis, arr, *args, **kwargs):
280 """
281 Apply a function to 1-D slices along the given axis.
282
283 Execute `func1d(a, *args, **kwargs)` where `func1d` operates on 1-D arrays
284 and `a` is a 1-D slice of `arr` along `axis`.
285
286 This is equivalent to (but faster than) the following use of `ndindex` and
287 `s_`, which sets each of ``ii``, ``jj``, and ``kk`` to a tuple of indices::
288
289 Ni, Nk = a.shape[:axis], a.shape[axis+1:]
290 for ii in ndindex(Ni):
291 for kk in ndindex(Nk):
292 f = func1d(arr[ii + s_[:,] + kk])
293 Nj = f.shape
294 for jj in ndindex(Nj):
295 out[ii + jj + kk] = f[jj]
296
297 Equivalently, eliminating the inner loop, this can be expressed as::
298
299 Ni, Nk = a.shape[:axis], a.shape[axis+1:]
300 for ii in ndindex(Ni):
301 for kk in ndindex(Nk):
302 out[ii + s_[...,] + kk] = func1d(arr[ii + s_[:,] + kk])
303
304 Parameters
305 ----------
306 func1d : function (M,) -> (Nj...)
307 This function should accept 1-D arrays. It is applied to 1-D
308 slices of `arr` along the specified axis.
309 axis : integer
310 Axis along which `arr` is sliced.
311 arr : ndarray (Ni..., M, Nk...)
312 Input array.
313 args : any
314 Additional arguments to `func1d`.
315 kwargs : any
316 Additional named arguments to `func1d`.
317
318 Returns
319 -------
320 out : ndarray (Ni..., Nj..., Nk...)
321 The output array. The shape of `out` is identical to the shape of
322 `arr`, except along the `axis` dimension. This axis is removed, and
323 replaced with new dimensions equal to the shape of the return value
324 of `func1d`. So if `func1d` returns a scalar `out` will have one
325 fewer dimensions than `arr`.
326
327 See Also
328 --------
329 apply_over_axes : Apply a function repeatedly over multiple axes.
330
331 Examples
332 --------
333 >>> import numpy as np
334 >>> def my_func(a):
335 ... \"\"\"Average first and last element of a 1-D array\"\"\"
336 ... return (a[0] + a[-1]) * 0.5
337 >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]])
338 >>> np.apply_along_axis(my_func, 0, b)
339 array([4., 5., 6.])
340 >>> np.apply_along_axis(my_func, 1, b)
341 array([2., 5., 8.])
342
343 For a function that returns a 1D array, the number of dimensions in
344 `outarr` is the same as `arr`.
345
346 >>> b = np.array([[8,1,7], [4,3,9], [5,2,6]])
347 >>> np.apply_along_axis(sorted, 1, b)
348 array([[1, 7, 8],
349 [3, 4, 9],
350 [2, 5, 6]])
351
352 For a function that returns a higher dimensional array, those dimensions
353 are inserted in place of the `axis` dimension.
354
355 >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]])
356 >>> np.apply_along_axis(np.diag, -1, b)
357 array([[[1, 0, 0],
358 [0, 2, 0],
359 [0, 0, 3]],
360 [[4, 0, 0],
361 [0, 5, 0],
362 [0, 0, 6]],
363 [[7, 0, 0],
364 [0, 8, 0],
365 [0, 0, 9]]])
366 """
367 # handle negative axes
368 conv = _array_converter(arr)
369 arr = conv[0]
370
371 nd = arr.ndim
372 axis = normalize_axis_index(axis, nd)
373
374 # arr, with the iteration axis at the end
375 in_dims = list(range(nd))
376 inarr_view = transpose(arr, in_dims[:axis] + in_dims[axis + 1:] + [axis])
377
378 # compute indices for the iteration axes, and append a trailing ellipsis to
379 # prevent 0d arrays decaying to scalars, which fixes gh-8642
380 inds = ndindex(inarr_view.shape[:-1])
381 inds = (ind + (Ellipsis,) for ind in inds)
382
383 # invoke the function on the first item
384 try:
385 ind0 = next(inds)
386 except StopIteration:
387 raise ValueError(
388 'Cannot apply_along_axis when any iteration dimensions are 0'
389 ) from None
390 res = asanyarray(func1d(inarr_view[ind0], *args, **kwargs))
391
392 # build a buffer for storing evaluations of func1d.
393 # remove the requested axis, and add the new ones on the end.
394 # laid out so that each write is contiguous.
395 # for a tuple index inds, buff[inds] = func1d(inarr_view[inds])
396 if not isinstance(res, matrix):
397 buff = zeros_like(res, shape=inarr_view.shape[:-1] + res.shape)
398 else:
399 # Matrices are nasty with reshaping, so do not preserve them here.
400 buff = zeros(inarr_view.shape[:-1] + res.shape, dtype=res.dtype)
401
402 # permutation of axes such that out = buff.transpose(buff_permute)
403 buff_dims = list(range(buff.ndim))
404 buff_permute = (
405 buff_dims[0 : axis] +
406 buff_dims[buff.ndim - res.ndim : buff.ndim] +
407 buff_dims[axis : buff.ndim - res.ndim]
408 )
409
410 # save the first result, then compute and save all remaining results
411 buff[ind0] = res
412 for ind in inds:
413 buff[ind] = asanyarray(func1d(inarr_view[ind], *args, **kwargs))
414
415 res = transpose(buff, buff_permute)
416 return conv.wrap(res)
417
418
419def _apply_over_axes_dispatcher(func, a, axes):
420 return (a,)
421
422
423@array_function_dispatch(_apply_over_axes_dispatcher)
424def apply_over_axes(func, a, axes):
425 """
426 Apply a function repeatedly over multiple axes.
427
428 `func` is called as `res = func(a, axis)`, where `axis` is the first
429 element of `axes`. The result `res` of the function call must have
430 either the same dimensions as `a` or one less dimension. If `res`
431 has one less dimension than `a`, a dimension is inserted before
432 `axis`. The call to `func` is then repeated for each axis in `axes`,
433 with `res` as the first argument.
434
435 Parameters
436 ----------
437 func : function
438 This function must take two arguments, `func(a, axis)`.
439 a : array_like
440 Input array.
441 axes : array_like
442 Axes over which `func` is applied; the elements must be integers.
443
444 Returns
445 -------
446 apply_over_axis : ndarray
447 The output array. The number of dimensions is the same as `a`,
448 but the shape can be different. This depends on whether `func`
449 changes the shape of its output with respect to its input.
450
451 See Also
452 --------
453 apply_along_axis :
454 Apply a function to 1-D slices of an array along the given axis.
455
456 Notes
457 -----
458 This function is equivalent to tuple axis arguments to reorderable ufuncs
459 with keepdims=True. Tuple axis arguments to ufuncs have been available since
460 version 1.7.0.
461
462 Examples
463 --------
464 >>> import numpy as np
465 >>> a = np.arange(24).reshape(2,3,4)
466 >>> a
467 array([[[ 0, 1, 2, 3],
468 [ 4, 5, 6, 7],
469 [ 8, 9, 10, 11]],
470 [[12, 13, 14, 15],
471 [16, 17, 18, 19],
472 [20, 21, 22, 23]]])
473
474 Sum over axes 0 and 2. The result has same number of dimensions
475 as the original array:
476
477 >>> np.apply_over_axes(np.sum, a, [0,2])
478 array([[[ 60],
479 [ 92],
480 [124]]])
481
482 Tuple axis arguments to ufuncs are equivalent:
483
484 >>> np.sum(a, axis=(0,2), keepdims=True)
485 array([[[ 60],
486 [ 92],
487 [124]]])
488
489 """
490 val = asarray(a)
491 N = a.ndim
492 if array(axes).ndim == 0:
493 axes = (axes,)
494 for axis in axes:
495 if axis < 0:
496 axis = N + axis
497 args = (val, axis)
498 res = func(*args)
499 if res.ndim == val.ndim:
500 val = res
501 else:
502 res = expand_dims(res, axis)
503 if res.ndim == val.ndim:
504 val = res
505 else:
506 raise ValueError("function is not returning "
507 "an array of the correct shape")
508 return val
509
510
511def _expand_dims_dispatcher(a, axis):
512 return (a,)
513
514
515@array_function_dispatch(_expand_dims_dispatcher)
516def expand_dims(a, axis):
517 """
518 Expand the shape of an array.
519
520 Insert a new axis that will appear at the `axis` position in the expanded
521 array shape.
522
523 Parameters
524 ----------
525 a : array_like
526 Input array.
527 axis : int or tuple of ints
528 Position in the expanded axes where the new axis (or axes) is placed.
529
530 .. deprecated:: 1.13.0
531 Passing an axis where ``axis > a.ndim`` will be treated as
532 ``axis == a.ndim``, and passing ``axis < -a.ndim - 1`` will
533 be treated as ``axis == 0``. This behavior is deprecated.
534
535 Returns
536 -------
537 result : ndarray
538 View of `a` with the number of dimensions increased.
539
540 See Also
541 --------
542 squeeze : The inverse operation, removing singleton dimensions
543 reshape : Insert, remove, and combine dimensions, and resize existing ones
544 atleast_1d, atleast_2d, atleast_3d
545
546 Examples
547 --------
548 >>> import numpy as np
549 >>> x = np.array([1, 2])
550 >>> x.shape
551 (2,)
552
553 The following is equivalent to ``x[np.newaxis, :]`` or ``x[np.newaxis]``:
554
555 >>> y = np.expand_dims(x, axis=0)
556 >>> y
557 array([[1, 2]])
558 >>> y.shape
559 (1, 2)
560
561 The following is equivalent to ``x[:, np.newaxis]``:
562
563 >>> y = np.expand_dims(x, axis=1)
564 >>> y
565 array([[1],
566 [2]])
567 >>> y.shape
568 (2, 1)
569
570 ``axis`` may also be a tuple:
571
572 >>> y = np.expand_dims(x, axis=(0, 1))
573 >>> y
574 array([[[1, 2]]])
575
576 >>> y = np.expand_dims(x, axis=(2, 0))
577 >>> y
578 array([[[1],
579 [2]]])
580
581 Note that some examples may use ``None`` instead of ``np.newaxis``. These
582 are the same objects:
583
584 >>> np.newaxis is None
585 True
586
587 """
588 if isinstance(a, matrix):
589 a = asarray(a)
590 else:
591 a = asanyarray(a)
592
593 if not isinstance(axis, (tuple, list)):
594 axis = (axis,)
595
596 out_ndim = len(axis) + a.ndim
597 axis = normalize_axis_tuple(axis, out_ndim)
598
599 shape_it = iter(a.shape)
600 shape = [1 if ax in axis else next(shape_it) for ax in range(out_ndim)]
601
602 return a.reshape(shape)
603
604
605# NOTE: Remove once deprecation period passes
606@set_module("numpy")
607def row_stack(tup, *, dtype=None, casting="same_kind"):
608 # Deprecated in NumPy 2.0, 2023-08-18
609 warnings.warn(
610 "`row_stack` alias is deprecated. "
611 "Use `np.vstack` directly.",
612 DeprecationWarning,
613 stacklevel=2
614 )
615 return vstack(tup, dtype=dtype, casting=casting)
616
617
618row_stack.__doc__ = vstack.__doc__
619
620
621def _column_stack_dispatcher(tup):
622 return _arrays_for_stack_dispatcher(tup)
623
624
625@array_function_dispatch(_column_stack_dispatcher)
626def column_stack(tup):
627 """
628 Stack 1-D arrays as columns into a 2-D array.
629
630 Take a sequence of 1-D arrays and stack them as columns
631 to make a single 2-D array. 2-D arrays are stacked as-is,
632 just like with `hstack`. 1-D arrays are turned into 2-D columns
633 first.
634
635 Parameters
636 ----------
637 tup : sequence of 1-D or 2-D arrays.
638 Arrays to stack. All of them must have the same first dimension.
639
640 Returns
641 -------
642 stacked : 2-D array
643 The array formed by stacking the given arrays.
644
645 See Also
646 --------
647 stack, hstack, vstack, concatenate
648
649 Examples
650 --------
651 >>> import numpy as np
652 >>> a = np.array((1,2,3))
653 >>> b = np.array((4,5,6))
654 >>> np.column_stack((a,b))
655 array([[1, 4],
656 [2, 5],
657 [3, 6]])
658
659 """
660 arrays = []
661 for v in tup:
662 arr = asanyarray(v)
663 if arr.ndim < 2:
664 arr = array(arr, copy=None, subok=True, ndmin=2).T
665 arrays.append(arr)
666 return _nx.concatenate(arrays, 1)
667
668
669def _dstack_dispatcher(tup):
670 return _arrays_for_stack_dispatcher(tup)
671
672
673@array_function_dispatch(_dstack_dispatcher)
674def dstack(tup):
675 """
676 Stack arrays in sequence depth wise (along third axis).
677
678 This is equivalent to concatenation along the third axis after 2-D arrays
679 of shape `(M,N)` have been reshaped to `(M,N,1)` and 1-D arrays of shape
680 `(N,)` have been reshaped to `(1,N,1)`. Rebuilds arrays divided by
681 `dsplit`.
682
683 This function makes most sense for arrays with up to 3 dimensions. For
684 instance, for pixel-data with a height (first axis), width (second axis),
685 and r/g/b channels (third axis). The functions `concatenate`, `stack` and
686 `block` provide more general stacking and concatenation operations.
687
688 Parameters
689 ----------
690 tup : sequence of arrays
691 The arrays must have the same shape along all but the third axis.
692 1-D or 2-D arrays must have the same shape.
693
694 Returns
695 -------
696 stacked : ndarray
697 The array formed by stacking the given arrays, will be at least 3-D.
698
699 See Also
700 --------
701 concatenate : Join a sequence of arrays along an existing axis.
702 stack : Join a sequence of arrays along a new axis.
703 block : Assemble an nd-array from nested lists of blocks.
704 vstack : Stack arrays in sequence vertically (row wise).
705 hstack : Stack arrays in sequence horizontally (column wise).
706 column_stack : Stack 1-D arrays as columns into a 2-D array.
707 dsplit : Split array along third axis.
708
709 Examples
710 --------
711 >>> import numpy as np
712 >>> a = np.array((1,2,3))
713 >>> b = np.array((4,5,6))
714 >>> np.dstack((a,b))
715 array([[[1, 4],
716 [2, 5],
717 [3, 6]]])
718
719 >>> a = np.array([[1],[2],[3]])
720 >>> b = np.array([[4],[5],[6]])
721 >>> np.dstack((a,b))
722 array([[[1, 4]],
723 [[2, 5]],
724 [[3, 6]]])
725
726 """
727 arrs = atleast_3d(*tup)
728 if not isinstance(arrs, tuple):
729 arrs = (arrs,)
730 return _nx.concatenate(arrs, 2)
731
732
733def _array_split_dispatcher(ary, indices_or_sections, axis=None):
734 return (ary, indices_or_sections)
735
736
737@array_function_dispatch(_array_split_dispatcher)
738def array_split(ary, indices_or_sections, axis=0):
739 """
740 Split an array into multiple sub-arrays.
741
742 Please refer to the ``split`` documentation. The only difference
743 between these functions is that ``array_split`` allows
744 `indices_or_sections` to be an integer that does *not* equally
745 divide the axis. For an array of length l that should be split
746 into n sections, it returns l % n sub-arrays of size l//n + 1
747 and the rest of size l//n.
748
749 See Also
750 --------
751 split : Split array into multiple sub-arrays of equal size.
752
753 Examples
754 --------
755 >>> import numpy as np
756 >>> x = np.arange(8.0)
757 >>> np.array_split(x, 3)
758 [array([0., 1., 2.]), array([3., 4., 5.]), array([6., 7.])]
759
760 >>> x = np.arange(9)
761 >>> np.array_split(x, 4)
762 [array([0, 1, 2]), array([3, 4]), array([5, 6]), array([7, 8])]
763
764 """
765 try:
766 Ntotal = ary.shape[axis]
767 except AttributeError:
768 Ntotal = len(ary)
769 try:
770 # handle array case.
771 Nsections = len(indices_or_sections) + 1
772 div_points = [0] + list(indices_or_sections) + [Ntotal]
773 except TypeError:
774 # indices_or_sections is a scalar, not an array.
775 Nsections = int(indices_or_sections)
776 if Nsections <= 0:
777 raise ValueError('number sections must be larger than 0.') from None
778 Neach_section, extras = divmod(Ntotal, Nsections)
779 section_sizes = ([0] +
780 extras * [Neach_section + 1] +
781 (Nsections - extras) * [Neach_section])
782 div_points = _nx.array(section_sizes, dtype=_nx.intp).cumsum()
783
784 sub_arys = []
785 sary = _nx.swapaxes(ary, axis, 0)
786 for i in range(Nsections):
787 st = div_points[i]
788 end = div_points[i + 1]
789 sub_arys.append(_nx.swapaxes(sary[st:end], axis, 0))
790
791 return sub_arys
792
793
794def _split_dispatcher(ary, indices_or_sections, axis=None):
795 return (ary, indices_or_sections)
796
797
798@array_function_dispatch(_split_dispatcher)
799def split(ary, indices_or_sections, axis=0):
800 """
801 Split an array into multiple sub-arrays as views into `ary`.
802
803 Parameters
804 ----------
805 ary : ndarray
806 Array to be divided into sub-arrays.
807 indices_or_sections : int or 1-D array
808 If `indices_or_sections` is an integer, N, the array will be divided
809 into N equal arrays along `axis`. If such a split is not possible,
810 an error is raised.
811
812 If `indices_or_sections` is a 1-D array of sorted integers, the entries
813 indicate where along `axis` the array is split. For example,
814 ``[2, 3]`` would, for ``axis=0``, result in
815
816 - ary[:2]
817 - ary[2:3]
818 - ary[3:]
819
820 If an index exceeds the dimension of the array along `axis`,
821 an empty sub-array is returned correspondingly.
822 axis : int, optional
823 The axis along which to split, default is 0.
824
825 Returns
826 -------
827 sub-arrays : list of ndarrays
828 A list of sub-arrays as views into `ary`.
829
830 Raises
831 ------
832 ValueError
833 If `indices_or_sections` is given as an integer, but
834 a split does not result in equal division.
835
836 See Also
837 --------
838 array_split : Split an array into multiple sub-arrays of equal or
839 near-equal size. Does not raise an exception if
840 an equal division cannot be made.
841 hsplit : Split array into multiple sub-arrays horizontally (column-wise).
842 vsplit : Split array into multiple sub-arrays vertically (row wise).
843 dsplit : Split array into multiple sub-arrays along the 3rd axis (depth).
844 concatenate : Join a sequence of arrays along an existing axis.
845 stack : Join a sequence of arrays along a new axis.
846 hstack : Stack arrays in sequence horizontally (column wise).
847 vstack : Stack arrays in sequence vertically (row wise).
848 dstack : Stack arrays in sequence depth wise (along third dimension).
849
850 Examples
851 --------
852 >>> import numpy as np
853 >>> x = np.arange(9.0)
854 >>> np.split(x, 3)
855 [array([0., 1., 2.]), array([3., 4., 5.]), array([6., 7., 8.])]
856
857 >>> x = np.arange(8.0)
858 >>> np.split(x, [3, 5, 6, 10])
859 [array([0., 1., 2.]),
860 array([3., 4.]),
861 array([5.]),
862 array([6., 7.]),
863 array([], dtype=float64)]
864
865 """
866 try:
867 len(indices_or_sections)
868 except TypeError:
869 sections = indices_or_sections
870 N = ary.shape[axis]
871 if N % sections:
872 raise ValueError(
873 'array split does not result in an equal division') from None
874 return array_split(ary, indices_or_sections, axis)
875
876
877def _hvdsplit_dispatcher(ary, indices_or_sections):
878 return (ary, indices_or_sections)
879
880
881@array_function_dispatch(_hvdsplit_dispatcher)
882def hsplit(ary, indices_or_sections):
883 """
884 Split an array into multiple sub-arrays horizontally (column-wise).
885
886 Please refer to the `split` documentation. `hsplit` is equivalent
887 to `split` with ``axis=1``, the array is always split along the second
888 axis except for 1-D arrays, where it is split at ``axis=0``.
889
890 See Also
891 --------
892 split : Split an array into multiple sub-arrays of equal size.
893
894 Examples
895 --------
896 >>> import numpy as np
897 >>> x = np.arange(16.0).reshape(4, 4)
898 >>> x
899 array([[ 0., 1., 2., 3.],
900 [ 4., 5., 6., 7.],
901 [ 8., 9., 10., 11.],
902 [12., 13., 14., 15.]])
903 >>> np.hsplit(x, 2)
904 [array([[ 0., 1.],
905 [ 4., 5.],
906 [ 8., 9.],
907 [12., 13.]]),
908 array([[ 2., 3.],
909 [ 6., 7.],
910 [10., 11.],
911 [14., 15.]])]
912 >>> np.hsplit(x, np.array([3, 6]))
913 [array([[ 0., 1., 2.],
914 [ 4., 5., 6.],
915 [ 8., 9., 10.],
916 [12., 13., 14.]]),
917 array([[ 3.],
918 [ 7.],
919 [11.],
920 [15.]]),
921 array([], shape=(4, 0), dtype=float64)]
922
923 With a higher dimensional array the split is still along the second axis.
924
925 >>> x = np.arange(8.0).reshape(2, 2, 2)
926 >>> x
927 array([[[0., 1.],
928 [2., 3.]],
929 [[4., 5.],
930 [6., 7.]]])
931 >>> np.hsplit(x, 2)
932 [array([[[0., 1.]],
933 [[4., 5.]]]),
934 array([[[2., 3.]],
935 [[6., 7.]]])]
936
937 With a 1-D array, the split is along axis 0.
938
939 >>> x = np.array([0, 1, 2, 3, 4, 5])
940 >>> np.hsplit(x, 2)
941 [array([0, 1, 2]), array([3, 4, 5])]
942
943 """
944 if _nx.ndim(ary) == 0:
945 raise ValueError('hsplit only works on arrays of 1 or more dimensions')
946 if ary.ndim > 1:
947 return split(ary, indices_or_sections, 1)
948 else:
949 return split(ary, indices_or_sections, 0)
950
951
952@array_function_dispatch(_hvdsplit_dispatcher)
953def vsplit(ary, indices_or_sections):
954 """
955 Split an array into multiple sub-arrays vertically (row-wise).
956
957 Please refer to the ``split`` documentation. ``vsplit`` is equivalent
958 to ``split`` with `axis=0` (default), the array is always split along the
959 first axis regardless of the array dimension.
960
961 See Also
962 --------
963 split : Split an array into multiple sub-arrays of equal size.
964
965 Examples
966 --------
967 >>> import numpy as np
968 >>> x = np.arange(16.0).reshape(4, 4)
969 >>> x
970 array([[ 0., 1., 2., 3.],
971 [ 4., 5., 6., 7.],
972 [ 8., 9., 10., 11.],
973 [12., 13., 14., 15.]])
974 >>> np.vsplit(x, 2)
975 [array([[0., 1., 2., 3.],
976 [4., 5., 6., 7.]]),
977 array([[ 8., 9., 10., 11.],
978 [12., 13., 14., 15.]])]
979 >>> np.vsplit(x, np.array([3, 6]))
980 [array([[ 0., 1., 2., 3.],
981 [ 4., 5., 6., 7.],
982 [ 8., 9., 10., 11.]]),
983 array([[12., 13., 14., 15.]]),
984 array([], shape=(0, 4), dtype=float64)]
985
986 With a higher dimensional array the split is still along the first axis.
987
988 >>> x = np.arange(8.0).reshape(2, 2, 2)
989 >>> x
990 array([[[0., 1.],
991 [2., 3.]],
992 [[4., 5.],
993 [6., 7.]]])
994 >>> np.vsplit(x, 2)
995 [array([[[0., 1.],
996 [2., 3.]]]),
997 array([[[4., 5.],
998 [6., 7.]]])]
999
1000 """
1001 if _nx.ndim(ary) < 2:
1002 raise ValueError('vsplit only works on arrays of 2 or more dimensions')
1003 return split(ary, indices_or_sections, 0)
1004
1005
1006@array_function_dispatch(_hvdsplit_dispatcher)
1007def dsplit(ary, indices_or_sections):
1008 """
1009 Split array into multiple sub-arrays along the 3rd axis (depth).
1010
1011 Please refer to the `split` documentation. `dsplit` is equivalent
1012 to `split` with ``axis=2``, the array is always split along the third
1013 axis provided the array dimension is greater than or equal to 3.
1014
1015 See Also
1016 --------
1017 split : Split an array into multiple sub-arrays of equal size.
1018
1019 Examples
1020 --------
1021 >>> import numpy as np
1022 >>> x = np.arange(16.0).reshape(2, 2, 4)
1023 >>> x
1024 array([[[ 0., 1., 2., 3.],
1025 [ 4., 5., 6., 7.]],
1026 [[ 8., 9., 10., 11.],
1027 [12., 13., 14., 15.]]])
1028 >>> np.dsplit(x, 2)
1029 [array([[[ 0., 1.],
1030 [ 4., 5.]],
1031 [[ 8., 9.],
1032 [12., 13.]]]), array([[[ 2., 3.],
1033 [ 6., 7.]],
1034 [[10., 11.],
1035 [14., 15.]]])]
1036 >>> np.dsplit(x, np.array([3, 6]))
1037 [array([[[ 0., 1., 2.],
1038 [ 4., 5., 6.]],
1039 [[ 8., 9., 10.],
1040 [12., 13., 14.]]]),
1041 array([[[ 3.],
1042 [ 7.]],
1043 [[11.],
1044 [15.]]]),
1045 array([], shape=(2, 2, 0), dtype=float64)]
1046 """
1047 if _nx.ndim(ary) < 3:
1048 raise ValueError('dsplit only works on arrays of 3 or more dimensions')
1049 return split(ary, indices_or_sections, 2)
1050
1051
1052def get_array_wrap(*args):
1053 """Find the wrapper for the array with the highest priority.
1054
1055 In case of ties, leftmost wins. If no wrapper is found, return None.
1056
1057 .. deprecated:: 2.0
1058 """
1059
1060 # Deprecated in NumPy 2.0, 2023-07-11
1061 warnings.warn(
1062 "`get_array_wrap` is deprecated. "
1063 "(deprecated in NumPy 2.0)",
1064 DeprecationWarning,
1065 stacklevel=2
1066 )
1067
1068 wrappers = sorted((getattr(x, '__array_priority__', 0), -i,
1069 x.__array_wrap__) for i, x in enumerate(args)
1070 if hasattr(x, '__array_wrap__'))
1071 if wrappers:
1072 return wrappers[-1][-1]
1073 return None
1074
1075
1076def _kron_dispatcher(a, b):
1077 return (a, b)
1078
1079
1080@array_function_dispatch(_kron_dispatcher)
1081def kron(a, b):
1082 """
1083 Kronecker product of two arrays.
1084
1085 Computes the Kronecker product, a composite array made of blocks of the
1086 second array scaled by the first.
1087
1088 Parameters
1089 ----------
1090 a, b : array_like
1091
1092 Returns
1093 -------
1094 out : ndarray
1095
1096 See Also
1097 --------
1098 outer : The outer product
1099
1100 Notes
1101 -----
1102 The function assumes that the number of dimensions of `a` and `b`
1103 are the same, if necessary prepending the smallest with ones.
1104 If ``a.shape = (r0,r1,...,rN)`` and ``b.shape = (s0,s1,...,sN)``,
1105 the Kronecker product has shape ``(r0*s0, r1*s1, ..., rN*SN)``.
1106 The elements are products of elements from `a` and `b`, organized
1107 explicitly by::
1108
1109 kron(a,b)[k0,k1,...,kN] = a[i0,i1,...,iN] * b[j0,j1,...,jN]
1110
1111 where::
1112
1113 kt = it * st + jt, t = 0,...,N
1114
1115 In the common 2-D case (N=1), the block structure can be visualized::
1116
1117 [[ a[0,0]*b, a[0,1]*b, ... , a[0,-1]*b ],
1118 [ ... ... ],
1119 [ a[-1,0]*b, a[-1,1]*b, ... , a[-1,-1]*b ]]
1120
1121
1122 Examples
1123 --------
1124 >>> import numpy as np
1125 >>> np.kron([1,10,100], [5,6,7])
1126 array([ 5, 6, 7, ..., 500, 600, 700])
1127 >>> np.kron([5,6,7], [1,10,100])
1128 array([ 5, 50, 500, ..., 7, 70, 700])
1129
1130 >>> np.kron(np.eye(2), np.ones((2,2)))
1131 array([[1., 1., 0., 0.],
1132 [1., 1., 0., 0.],
1133 [0., 0., 1., 1.],
1134 [0., 0., 1., 1.]])
1135
1136 >>> a = np.arange(100).reshape((2,5,2,5))
1137 >>> b = np.arange(24).reshape((2,3,4))
1138 >>> c = np.kron(a,b)
1139 >>> c.shape
1140 (2, 10, 6, 20)
1141 >>> I = (1,3,0,2)
1142 >>> J = (0,2,1)
1143 >>> J1 = (0,) + J # extend to ndim=4
1144 >>> S1 = (1,) + b.shape
1145 >>> K = tuple(np.array(I) * np.array(S1) + np.array(J1))
1146 >>> c[K] == a[I]*b[J]
1147 True
1148
1149 """
1150 # Working:
1151 # 1. Equalise the shapes by prepending smaller array with 1s
1152 # 2. Expand shapes of both the arrays by adding new axes at
1153 # odd positions for 1st array and even positions for 2nd
1154 # 3. Compute the product of the modified array
1155 # 4. The inner most array elements now contain the rows of
1156 # the Kronecker product
1157 # 5. Reshape the result to kron's shape, which is same as
1158 # product of shapes of the two arrays.
1159 b = asanyarray(b)
1160 a = array(a, copy=None, subok=True, ndmin=b.ndim)
1161 is_any_mat = isinstance(a, matrix) or isinstance(b, matrix)
1162 ndb, nda = b.ndim, a.ndim
1163 nd = max(ndb, nda)
1164
1165 if (nda == 0 or ndb == 0):
1166 return _nx.multiply(a, b)
1167
1168 as_ = a.shape
1169 bs = b.shape
1170 if not a.flags.contiguous:
1171 a = reshape(a, as_)
1172 if not b.flags.contiguous:
1173 b = reshape(b, bs)
1174
1175 # Equalise the shapes by prepending smaller one with 1s
1176 as_ = (1,) * max(0, ndb - nda) + as_
1177 bs = (1,) * max(0, nda - ndb) + bs
1178
1179 # Insert empty dimensions
1180 a_arr = expand_dims(a, axis=tuple(range(ndb - nda)))
1181 b_arr = expand_dims(b, axis=tuple(range(nda - ndb)))
1182
1183 # Compute the product
1184 a_arr = expand_dims(a_arr, axis=tuple(range(1, nd * 2, 2)))
1185 b_arr = expand_dims(b_arr, axis=tuple(range(0, nd * 2, 2)))
1186 # In case of `mat`, convert result to `array`
1187 result = _nx.multiply(a_arr, b_arr, subok=(not is_any_mat))
1188
1189 # Reshape back
1190 result = result.reshape(_nx.multiply(as_, bs))
1191
1192 return result if not is_any_mat else matrix(result, copy=False)
1193
1194
1195def _tile_dispatcher(A, reps):
1196 return (A, reps)
1197
1198
1199@array_function_dispatch(_tile_dispatcher)
1200def tile(A, reps):
1201 """
1202 Construct an array by repeating A the number of times given by reps.
1203
1204 If `reps` has length ``d``, the result will have dimension of
1205 ``max(d, A.ndim)``.
1206
1207 If ``A.ndim < d``, `A` is promoted to be d-dimensional by prepending new
1208 axes. So a shape (3,) array is promoted to (1, 3) for 2-D replication,
1209 or shape (1, 1, 3) for 3-D replication. If this is not the desired
1210 behavior, promote `A` to d-dimensions manually before calling this
1211 function.
1212
1213 If ``A.ndim > d``, `reps` is promoted to `A`.ndim by prepending 1's to it.
1214 Thus for an `A` of shape (2, 3, 4, 5), a `reps` of (2, 2) is treated as
1215 (1, 1, 2, 2).
1216
1217 Note : Although tile may be used for broadcasting, it is strongly
1218 recommended to use numpy's broadcasting operations and functions.
1219
1220 Parameters
1221 ----------
1222 A : array_like
1223 The input array.
1224 reps : array_like
1225 The number of repetitions of `A` along each axis.
1226
1227 Returns
1228 -------
1229 c : ndarray
1230 The tiled output array.
1231
1232 See Also
1233 --------
1234 repeat : Repeat elements of an array.
1235 broadcast_to : Broadcast an array to a new shape
1236
1237 Examples
1238 --------
1239 >>> import numpy as np
1240 >>> a = np.array([0, 1, 2])
1241 >>> np.tile(a, 2)
1242 array([0, 1, 2, 0, 1, 2])
1243 >>> np.tile(a, (2, 2))
1244 array([[0, 1, 2, 0, 1, 2],
1245 [0, 1, 2, 0, 1, 2]])
1246 >>> np.tile(a, (2, 1, 2))
1247 array([[[0, 1, 2, 0, 1, 2]],
1248 [[0, 1, 2, 0, 1, 2]]])
1249
1250 >>> b = np.array([[1, 2], [3, 4]])
1251 >>> np.tile(b, 2)
1252 array([[1, 2, 1, 2],
1253 [3, 4, 3, 4]])
1254 >>> np.tile(b, (2, 1))
1255 array([[1, 2],
1256 [3, 4],
1257 [1, 2],
1258 [3, 4]])
1259
1260 >>> c = np.array([1,2,3,4])
1261 >>> np.tile(c,(4,1))
1262 array([[1, 2, 3, 4],
1263 [1, 2, 3, 4],
1264 [1, 2, 3, 4],
1265 [1, 2, 3, 4]])
1266 """
1267 try:
1268 tup = tuple(reps)
1269 except TypeError:
1270 tup = (reps,)
1271 d = len(tup)
1272 if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray):
1273 # Fixes the problem that the function does not make a copy if A is a
1274 # numpy array and the repetitions are 1 in all dimensions
1275 return _nx.array(A, copy=True, subok=True, ndmin=d)
1276 else:
1277 # Note that no copy of zero-sized arrays is made. However since they
1278 # have no data there is no risk of an inadvertent overwrite.
1279 c = _nx.array(A, copy=None, subok=True, ndmin=d)
1280 if (d < c.ndim):
1281 tup = (1,) * (c.ndim - d) + tup
1282 shape_out = tuple(s * t for s, t in zip(c.shape, tup))
1283 n = c.size
1284 if n > 0:
1285 for dim_in, nrep in zip(c.shape, tup):
1286 if nrep != 1:
1287 c = c.reshape(-1, n).repeat(nrep, 0)
1288 n //= dim_in
1289 return c.reshape(shape_out)