123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938 |
- __all__ = ['atleast_1d', 'atleast_2d', 'atleast_3d', 'block', 'hstack',
- 'stack', 'vstack']
- import functools
- import itertools
- import operator
- import warnings
- from . import numeric as _nx
- from . import overrides
- from .multiarray import array, asanyarray, normalize_axis_index
- from . import fromnumeric as _from_nx
- array_function_dispatch = functools.partial(
- overrides.array_function_dispatch, module='numpy')
- def _atleast_1d_dispatcher(*arys):
- return arys
- @array_function_dispatch(_atleast_1d_dispatcher)
- def atleast_1d(*arys):
- """
- Convert inputs to arrays with at least one dimension.
- Scalar inputs are converted to 1-dimensional arrays, whilst
- higher-dimensional inputs are preserved.
- Parameters
- ----------
- arys1, arys2, ... : array_like
- One or more input arrays.
- Returns
- -------
- ret : ndarray
- An array, or list of arrays, each with ``a.ndim >= 1``.
- Copies are made only if necessary.
- See Also
- --------
- atleast_2d, atleast_3d
- Examples
- --------
- >>> np.atleast_1d(1.0)
- array([1.])
- >>> x = np.arange(9.0).reshape(3,3)
- >>> np.atleast_1d(x)
- array([[0., 1., 2.],
- [3., 4., 5.],
- [6., 7., 8.]])
- >>> np.atleast_1d(x) is x
- True
- >>> np.atleast_1d(1, [3, 4])
- [array([1]), array([3, 4])]
- """
- res = []
- for ary in arys:
- ary = asanyarray(ary)
- if ary.ndim == 0:
- result = ary.reshape(1)
- else:
- result = ary
- res.append(result)
- if len(res) == 1:
- return res[0]
- else:
- return res
- def _atleast_2d_dispatcher(*arys):
- return arys
- @array_function_dispatch(_atleast_2d_dispatcher)
- def atleast_2d(*arys):
- """
- View inputs as arrays with at least two dimensions.
- Parameters
- ----------
- arys1, arys2, ... : array_like
- One or more array-like sequences. Non-array inputs are converted
- to arrays. Arrays that already have two or more dimensions are
- preserved.
- Returns
- -------
- res, res2, ... : ndarray
- An array, or list of arrays, each with ``a.ndim >= 2``.
- Copies are avoided where possible, and views with two or more
- dimensions are returned.
- See Also
- --------
- atleast_1d, atleast_3d
- Examples
- --------
- >>> np.atleast_2d(3.0)
- array([[3.]])
- >>> x = np.arange(3.0)
- >>> np.atleast_2d(x)
- array([[0., 1., 2.]])
- >>> np.atleast_2d(x).base is x
- True
- >>> np.atleast_2d(1, [1, 2], [[1, 2]])
- [array([[1]]), array([[1, 2]]), array([[1, 2]])]
- """
- res = []
- for ary in arys:
- ary = asanyarray(ary)
- if ary.ndim == 0:
- result = ary.reshape(1, 1)
- elif ary.ndim == 1:
- result = ary[_nx.newaxis, :]
- else:
- result = ary
- res.append(result)
- if len(res) == 1:
- return res[0]
- else:
- return res
- def _atleast_3d_dispatcher(*arys):
- return arys
- @array_function_dispatch(_atleast_3d_dispatcher)
- def atleast_3d(*arys):
- """
- View inputs as arrays with at least three dimensions.
- Parameters
- ----------
- arys1, arys2, ... : array_like
- One or more array-like sequences. Non-array inputs are converted to
- arrays. Arrays that already have three or more dimensions are
- preserved.
- Returns
- -------
- res1, res2, ... : ndarray
- An array, or list of arrays, each with ``a.ndim >= 3``. Copies are
- avoided where possible, and views with three or more dimensions are
- returned. For example, a 1-D array of shape ``(N,)`` becomes a view
- of shape ``(1, N, 1)``, and a 2-D array of shape ``(M, N)`` becomes a
- view of shape ``(M, N, 1)``.
- See Also
- --------
- atleast_1d, atleast_2d
- Examples
- --------
- >>> np.atleast_3d(3.0)
- array([[[3.]]])
- >>> x = np.arange(3.0)
- >>> np.atleast_3d(x).shape
- (1, 3, 1)
- >>> x = np.arange(12.0).reshape(4,3)
- >>> np.atleast_3d(x).shape
- (4, 3, 1)
- >>> np.atleast_3d(x).base is x.base # x is a reshape, so not base itself
- True
- >>> for arr in np.atleast_3d([1, 2], [[1, 2]], [[[1, 2]]]):
- ... print(arr, arr.shape) # doctest: +SKIP
- ...
- [[[1]
- [2]]] (1, 2, 1)
- [[[1]
- [2]]] (1, 2, 1)
- [[[1 2]]] (1, 1, 2)
- """
- res = []
- for ary in arys:
- ary = asanyarray(ary)
- if ary.ndim == 0:
- result = ary.reshape(1, 1, 1)
- elif ary.ndim == 1:
- result = ary[_nx.newaxis, :, _nx.newaxis]
- elif ary.ndim == 2:
- result = ary[:, :, _nx.newaxis]
- else:
- result = ary
- res.append(result)
- if len(res) == 1:
- return res[0]
- else:
- return res
- def _arrays_for_stack_dispatcher(arrays, stacklevel=4):
- if not hasattr(arrays, '__getitem__') and hasattr(arrays, '__iter__'):
- warnings.warn('arrays to stack must be passed as a "sequence" type '
- 'such as list or tuple. Support for non-sequence '
- 'iterables such as generators is deprecated as of '
- 'NumPy 1.16 and will raise an error in the future.',
- FutureWarning, stacklevel=stacklevel)
- return ()
- return arrays
- def _vhstack_dispatcher(tup, *,
- dtype=None, casting=None):
- return _arrays_for_stack_dispatcher(tup)
- @array_function_dispatch(_vhstack_dispatcher)
- def vstack(tup, *, dtype=None, casting="same_kind"):
- """
- Stack arrays in sequence vertically (row wise).
- This is equivalent to concatenation along the first axis after 1-D arrays
- of shape `(N,)` have been reshaped to `(1,N)`. Rebuilds arrays divided by
- `vsplit`.
- This function makes most sense for arrays with up to 3 dimensions. For
- instance, for pixel-data with a height (first axis), width (second axis),
- and r/g/b channels (third axis). The functions `concatenate`, `stack` and
- `block` provide more general stacking and concatenation operations.
- ``np.row_stack`` is an alias for `vstack`. They are the same function.
- Parameters
- ----------
- tup : sequence of ndarrays
- The arrays must have the same shape along all but the first axis.
- 1-D arrays must have the same length.
- dtype : str or dtype
- If provided, the destination array will have this dtype. Cannot be
- provided together with `out`.
- .. versionadded:: 1.24
- casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
- Controls what kind of data casting may occur. Defaults to 'same_kind'.
- .. versionadded:: 1.24
- Returns
- -------
- stacked : ndarray
- The array formed by stacking the given arrays, will be at least 2-D.
- See Also
- --------
- concatenate : Join a sequence of arrays along an existing axis.
- stack : Join a sequence of arrays along a new axis.
- block : Assemble an nd-array from nested lists of blocks.
- hstack : Stack arrays in sequence horizontally (column wise).
- dstack : Stack arrays in sequence depth wise (along third axis).
- column_stack : Stack 1-D arrays as columns into a 2-D array.
- vsplit : Split an array into multiple sub-arrays vertically (row-wise).
- Examples
- --------
- >>> a = np.array([1, 2, 3])
- >>> b = np.array([4, 5, 6])
- >>> np.vstack((a,b))
- array([[1, 2, 3],
- [4, 5, 6]])
- >>> a = np.array([[1], [2], [3]])
- >>> b = np.array([[4], [5], [6]])
- >>> np.vstack((a,b))
- array([[1],
- [2],
- [3],
- [4],
- [5],
- [6]])
- """
- if not overrides.ARRAY_FUNCTION_ENABLED:
- # raise warning if necessary
- _arrays_for_stack_dispatcher(tup, stacklevel=2)
- arrs = atleast_2d(*tup)
- if not isinstance(arrs, list):
- arrs = [arrs]
- return _nx.concatenate(arrs, 0, dtype=dtype, casting=casting)
- @array_function_dispatch(_vhstack_dispatcher)
- def hstack(tup, *, dtype=None, casting="same_kind"):
- """
- Stack arrays in sequence horizontally (column wise).
- This is equivalent to concatenation along the second axis, except for 1-D
- arrays where it concatenates along the first axis. Rebuilds arrays divided
- by `hsplit`.
- This function makes most sense for arrays with up to 3 dimensions. For
- instance, for pixel-data with a height (first axis), width (second axis),
- and r/g/b channels (third axis). The functions `concatenate`, `stack` and
- `block` provide more general stacking and concatenation operations.
- Parameters
- ----------
- tup : sequence of ndarrays
- The arrays must have the same shape along all but the second axis,
- except 1-D arrays which can be any length.
- dtype : str or dtype
- If provided, the destination array will have this dtype. Cannot be
- provided together with `out`.
- .. versionadded:: 1.24
- casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
- Controls what kind of data casting may occur. Defaults to 'same_kind'.
- .. versionadded:: 1.24
- Returns
- -------
- stacked : ndarray
- The array formed by stacking the given arrays.
- See Also
- --------
- concatenate : Join a sequence of arrays along an existing axis.
- stack : Join a sequence of arrays along a new axis.
- block : Assemble an nd-array from nested lists of blocks.
- vstack : Stack arrays in sequence vertically (row wise).
- dstack : Stack arrays in sequence depth wise (along third axis).
- column_stack : Stack 1-D arrays as columns into a 2-D array.
- hsplit : Split an array into multiple sub-arrays horizontally (column-wise).
- Examples
- --------
- >>> a = np.array((1,2,3))
- >>> b = np.array((4,5,6))
- >>> np.hstack((a,b))
- array([1, 2, 3, 4, 5, 6])
- >>> a = np.array([[1],[2],[3]])
- >>> b = np.array([[4],[5],[6]])
- >>> np.hstack((a,b))
- array([[1, 4],
- [2, 5],
- [3, 6]])
- """
- if not overrides.ARRAY_FUNCTION_ENABLED:
- # raise warning if necessary
- _arrays_for_stack_dispatcher(tup, stacklevel=2)
- arrs = atleast_1d(*tup)
- if not isinstance(arrs, list):
- arrs = [arrs]
- # As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
- if arrs and arrs[0].ndim == 1:
- return _nx.concatenate(arrs, 0, dtype=dtype, casting=casting)
- else:
- return _nx.concatenate(arrs, 1, dtype=dtype, casting=casting)
- def _stack_dispatcher(arrays, axis=None, out=None, *,
- dtype=None, casting=None):
- arrays = _arrays_for_stack_dispatcher(arrays, stacklevel=6)
- if out is not None:
- # optimize for the typical case where only arrays is provided
- arrays = list(arrays)
- arrays.append(out)
- return arrays
- @array_function_dispatch(_stack_dispatcher)
- def stack(arrays, axis=0, out=None, *, dtype=None, casting="same_kind"):
- """
- Join a sequence of arrays along a new axis.
- The ``axis`` parameter specifies the index of the new axis in the
- dimensions of the result. For example, if ``axis=0`` it will be the first
- dimension and if ``axis=-1`` it will be the last dimension.
- .. versionadded:: 1.10.0
- Parameters
- ----------
- arrays : sequence of array_like
- Each array must have the same shape.
- axis : int, optional
- The axis in the result array along which the input arrays are stacked.
- out : ndarray, optional
- If provided, the destination to place the result. The shape must be
- correct, matching that of what stack would have returned if no
- out argument were specified.
- dtype : str or dtype
- If provided, the destination array will have this dtype. Cannot be
- provided together with `out`.
- .. versionadded:: 1.24
- casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
- Controls what kind of data casting may occur. Defaults to 'same_kind'.
- .. versionadded:: 1.24
- Returns
- -------
- stacked : ndarray
- The stacked array has one more dimension than the input arrays.
- See Also
- --------
- concatenate : Join a sequence of arrays along an existing axis.
- block : Assemble an nd-array from nested lists of blocks.
- split : Split array into a list of multiple sub-arrays of equal size.
- Examples
- --------
- >>> arrays = [np.random.randn(3, 4) for _ in range(10)]
- >>> np.stack(arrays, axis=0).shape
- (10, 3, 4)
- >>> np.stack(arrays, axis=1).shape
- (3, 10, 4)
- >>> np.stack(arrays, axis=2).shape
- (3, 4, 10)
- >>> a = np.array([1, 2, 3])
- >>> b = np.array([4, 5, 6])
- >>> np.stack((a, b))
- array([[1, 2, 3],
- [4, 5, 6]])
- >>> np.stack((a, b), axis=-1)
- array([[1, 4],
- [2, 5],
- [3, 6]])
- """
- if not overrides.ARRAY_FUNCTION_ENABLED:
- # raise warning if necessary
- _arrays_for_stack_dispatcher(arrays, stacklevel=2)
- arrays = [asanyarray(arr) for arr in arrays]
- if not arrays:
- raise ValueError('need at least one array to stack')
- shapes = {arr.shape for arr in arrays}
- if len(shapes) != 1:
- raise ValueError('all input arrays must have the same shape')
- result_ndim = arrays[0].ndim + 1
- axis = normalize_axis_index(axis, result_ndim)
- sl = (slice(None),) * axis + (_nx.newaxis,)
- expanded_arrays = [arr[sl] for arr in arrays]
- return _nx.concatenate(expanded_arrays, axis=axis, out=out,
- dtype=dtype, casting=casting)
- # Internal functions to eliminate the overhead of repeated dispatch in one of
- # the two possible paths inside np.block.
- # Use getattr to protect against __array_function__ being disabled.
- _size = getattr(_from_nx.size, '__wrapped__', _from_nx.size)
- _ndim = getattr(_from_nx.ndim, '__wrapped__', _from_nx.ndim)
- _concatenate = getattr(_from_nx.concatenate,
- '__wrapped__', _from_nx.concatenate)
- def _block_format_index(index):
- """
- Convert a list of indices ``[0, 1, 2]`` into ``"arrays[0][1][2]"``.
- """
- idx_str = ''.join('[{}]'.format(i) for i in index if i is not None)
- return 'arrays' + idx_str
- def _block_check_depths_match(arrays, parent_index=[]):
- """
- Recursive function checking that the depths of nested lists in `arrays`
- all match. Mismatch raises a ValueError as described in the block
- docstring below.
- The entire index (rather than just the depth) needs to be calculated
- for each innermost list, in case an error needs to be raised, so that
- the index of the offending list can be printed as part of the error.
- Parameters
- ----------
- arrays : nested list of arrays
- The arrays to check
- parent_index : list of int
- The full index of `arrays` within the nested lists passed to
- `_block_check_depths_match` at the top of the recursion.
- Returns
- -------
- first_index : list of int
- The full index of an element from the bottom of the nesting in
- `arrays`. If any element at the bottom is an empty list, this will
- refer to it, and the last index along the empty axis will be None.
- max_arr_ndim : int
- The maximum of the ndims of the arrays nested in `arrays`.
- final_size: int
- The number of elements in the final array. This is used the motivate
- the choice of algorithm used using benchmarking wisdom.
- """
- if type(arrays) is tuple:
- # not strictly necessary, but saves us from:
- # - more than one way to do things - no point treating tuples like
- # lists
- # - horribly confusing behaviour that results when tuples are
- # treated like ndarray
- raise TypeError(
- '{} is a tuple. '
- 'Only lists can be used to arrange blocks, and np.block does '
- 'not allow implicit conversion from tuple to ndarray.'.format(
- _block_format_index(parent_index)
- )
- )
- elif type(arrays) is list and len(arrays) > 0:
- idxs_ndims = (_block_check_depths_match(arr, parent_index + [i])
- for i, arr in enumerate(arrays))
- first_index, max_arr_ndim, final_size = next(idxs_ndims)
- for index, ndim, size in idxs_ndims:
- final_size += size
- if ndim > max_arr_ndim:
- max_arr_ndim = ndim
- if len(index) != len(first_index):
- raise ValueError(
- "List depths are mismatched. First element was at depth "
- "{}, but there is an element at depth {} ({})".format(
- len(first_index),
- len(index),
- _block_format_index(index)
- )
- )
- # propagate our flag that indicates an empty list at the bottom
- if index[-1] is None:
- first_index = index
- return first_index, max_arr_ndim, final_size
- elif type(arrays) is list and len(arrays) == 0:
- # We've 'bottomed out' on an empty list
- return parent_index + [None], 0, 0
- else:
- # We've 'bottomed out' - arrays is either a scalar or an array
- size = _size(arrays)
- return parent_index, _ndim(arrays), size
- def _atleast_nd(a, ndim):
- # Ensures `a` has at least `ndim` dimensions by prepending
- # ones to `a.shape` as necessary
- return array(a, ndmin=ndim, copy=False, subok=True)
- def _accumulate(values):
- return list(itertools.accumulate(values))
- def _concatenate_shapes(shapes, axis):
- """Given array shapes, return the resulting shape and slices prefixes.
- These help in nested concatenation.
- Returns
- -------
- shape: tuple of int
- This tuple satisfies::
- shape, _ = _concatenate_shapes([arr.shape for shape in arrs], axis)
- shape == concatenate(arrs, axis).shape
- slice_prefixes: tuple of (slice(start, end), )
- For a list of arrays being concatenated, this returns the slice
- in the larger array at axis that needs to be sliced into.
- For example, the following holds::
- ret = concatenate([a, b, c], axis)
- _, (sl_a, sl_b, sl_c) = concatenate_slices([a, b, c], axis)
- ret[(slice(None),) * axis + sl_a] == a
- ret[(slice(None),) * axis + sl_b] == b
- ret[(slice(None),) * axis + sl_c] == c
- These are called slice prefixes since they are used in the recursive
- blocking algorithm to compute the left-most slices during the
- recursion. Therefore, they must be prepended to rest of the slice
- that was computed deeper in the recursion.
- These are returned as tuples to ensure that they can quickly be added
- to existing slice tuple without creating a new tuple every time.
- """
- # Cache a result that will be reused.
- shape_at_axis = [shape[axis] for shape in shapes]
- # Take a shape, any shape
- first_shape = shapes[0]
- first_shape_pre = first_shape[:axis]
- first_shape_post = first_shape[axis+1:]
- if any(shape[:axis] != first_shape_pre or
- shape[axis+1:] != first_shape_post for shape in shapes):
- raise ValueError(
- 'Mismatched array shapes in block along axis {}.'.format(axis))
- shape = (first_shape_pre + (sum(shape_at_axis),) + first_shape[axis+1:])
- offsets_at_axis = _accumulate(shape_at_axis)
- slice_prefixes = [(slice(start, end),)
- for start, end in zip([0] + offsets_at_axis,
- offsets_at_axis)]
- return shape, slice_prefixes
- def _block_info_recursion(arrays, max_depth, result_ndim, depth=0):
- """
- Returns the shape of the final array, along with a list
- of slices and a list of arrays that can be used for assignment inside the
- new array
- Parameters
- ----------
- arrays : nested list of arrays
- The arrays to check
- max_depth : list of int
- The number of nested lists
- result_ndim : int
- The number of dimensions in thefinal array.
- Returns
- -------
- shape : tuple of int
- The shape that the final array will take on.
- slices: list of tuple of slices
- The slices into the full array required for assignment. These are
- required to be prepended with ``(Ellipsis, )`` to obtain to correct
- final index.
- arrays: list of ndarray
- The data to assign to each slice of the full array
- """
- if depth < max_depth:
- shapes, slices, arrays = zip(
- *[_block_info_recursion(arr, max_depth, result_ndim, depth+1)
- for arr in arrays])
- axis = result_ndim - max_depth + depth
- shape, slice_prefixes = _concatenate_shapes(shapes, axis)
- # Prepend the slice prefix and flatten the slices
- slices = [slice_prefix + the_slice
- for slice_prefix, inner_slices in zip(slice_prefixes, slices)
- for the_slice in inner_slices]
- # Flatten the array list
- arrays = functools.reduce(operator.add, arrays)
- return shape, slices, arrays
- else:
- # We've 'bottomed out' - arrays is either a scalar or an array
- # type(arrays) is not list
- # Return the slice and the array inside a list to be consistent with
- # the recursive case.
- arr = _atleast_nd(arrays, result_ndim)
- return arr.shape, [()], [arr]
- def _block(arrays, max_depth, result_ndim, depth=0):
- """
- Internal implementation of block based on repeated concatenation.
- `arrays` is the argument passed to
- block. `max_depth` is the depth of nested lists within `arrays` and
- `result_ndim` is the greatest of the dimensions of the arrays in
- `arrays` and the depth of the lists in `arrays` (see block docstring
- for details).
- """
- if depth < max_depth:
- arrs = [_block(arr, max_depth, result_ndim, depth+1)
- for arr in arrays]
- return _concatenate(arrs, axis=-(max_depth-depth))
- else:
- # We've 'bottomed out' - arrays is either a scalar or an array
- # type(arrays) is not list
- return _atleast_nd(arrays, result_ndim)
- def _block_dispatcher(arrays):
- # Use type(...) is list to match the behavior of np.block(), which special
- # cases list specifically rather than allowing for generic iterables or
- # tuple. Also, we know that list.__array_function__ will never exist.
- if type(arrays) is list:
- for subarrays in arrays:
- yield from _block_dispatcher(subarrays)
- else:
- yield arrays
- @array_function_dispatch(_block_dispatcher)
- def block(arrays):
- """
- Assemble an nd-array from nested lists of blocks.
- Blocks in the innermost lists are concatenated (see `concatenate`) along
- the last dimension (-1), then these are concatenated along the
- second-last dimension (-2), and so on until the outermost list is reached.
- Blocks can be of any dimension, but will not be broadcasted using the normal
- rules. Instead, leading axes of size 1 are inserted, to make ``block.ndim``
- the same for all blocks. This is primarily useful for working with scalars,
- and means that code like ``np.block([v, 1])`` is valid, where
- ``v.ndim == 1``.
- When the nested list is two levels deep, this allows block matrices to be
- constructed from their components.
- .. versionadded:: 1.13.0
- Parameters
- ----------
- arrays : nested list of array_like or scalars (but not tuples)
- If passed a single ndarray or scalar (a nested list of depth 0), this
- is returned unmodified (and not copied).
- Elements shapes must match along the appropriate axes (without
- broadcasting), but leading 1s will be prepended to the shape as
- necessary to make the dimensions match.
- Returns
- -------
- block_array : ndarray
- The array assembled from the given blocks.
- The dimensionality of the output is equal to the greatest of:
- * the dimensionality of all the inputs
- * the depth to which the input list is nested
- Raises
- ------
- ValueError
- * If list depths are mismatched - for instance, ``[[a, b], c]`` is
- illegal, and should be spelt ``[[a, b], [c]]``
- * If lists are empty - for instance, ``[[a, b], []]``
- See Also
- --------
- concatenate : Join a sequence of arrays along an existing axis.
- stack : Join a sequence of arrays along a new axis.
- vstack : Stack arrays in sequence vertically (row wise).
- hstack : Stack arrays in sequence horizontally (column wise).
- dstack : Stack arrays in sequence depth wise (along third axis).
- column_stack : Stack 1-D arrays as columns into a 2-D array.
- vsplit : Split an array into multiple sub-arrays vertically (row-wise).
- Notes
- -----
- When called with only scalars, ``np.block`` is equivalent to an ndarray
- call. So ``np.block([[1, 2], [3, 4]])`` is equivalent to
- ``np.array([[1, 2], [3, 4]])``.
- This function does not enforce that the blocks lie on a fixed grid.
- ``np.block([[a, b], [c, d]])`` is not restricted to arrays of the form::
- AAAbb
- AAAbb
- cccDD
- But is also allowed to produce, for some ``a, b, c, d``::
- AAAbb
- AAAbb
- cDDDD
- Since concatenation happens along the last axis first, `block` is _not_
- capable of producing the following directly::
- AAAbb
- cccbb
- cccDD
- Matlab's "square bracket stacking", ``[A, B, ...; p, q, ...]``, is
- equivalent to ``np.block([[A, B, ...], [p, q, ...]])``.
- Examples
- --------
- The most common use of this function is to build a block matrix
- >>> A = np.eye(2) * 2
- >>> B = np.eye(3) * 3
- >>> np.block([
- ... [A, np.zeros((2, 3))],
- ... [np.ones((3, 2)), B ]
- ... ])
- array([[2., 0., 0., 0., 0.],
- [0., 2., 0., 0., 0.],
- [1., 1., 3., 0., 0.],
- [1., 1., 0., 3., 0.],
- [1., 1., 0., 0., 3.]])
- With a list of depth 1, `block` can be used as `hstack`
- >>> np.block([1, 2, 3]) # hstack([1, 2, 3])
- array([1, 2, 3])
- >>> a = np.array([1, 2, 3])
- >>> b = np.array([4, 5, 6])
- >>> np.block([a, b, 10]) # hstack([a, b, 10])
- array([ 1, 2, 3, 4, 5, 6, 10])
- >>> A = np.ones((2, 2), int)
- >>> B = 2 * A
- >>> np.block([A, B]) # hstack([A, B])
- array([[1, 1, 2, 2],
- [1, 1, 2, 2]])
- With a list of depth 2, `block` can be used in place of `vstack`:
- >>> a = np.array([1, 2, 3])
- >>> b = np.array([4, 5, 6])
- >>> np.block([[a], [b]]) # vstack([a, b])
- array([[1, 2, 3],
- [4, 5, 6]])
- >>> A = np.ones((2, 2), int)
- >>> B = 2 * A
- >>> np.block([[A], [B]]) # vstack([A, B])
- array([[1, 1],
- [1, 1],
- [2, 2],
- [2, 2]])
- It can also be used in places of `atleast_1d` and `atleast_2d`
- >>> a = np.array(0)
- >>> b = np.array([1])
- >>> np.block([a]) # atleast_1d(a)
- array([0])
- >>> np.block([b]) # atleast_1d(b)
- array([1])
- >>> np.block([[a]]) # atleast_2d(a)
- array([[0]])
- >>> np.block([[b]]) # atleast_2d(b)
- array([[1]])
- """
- arrays, list_ndim, result_ndim, final_size = _block_setup(arrays)
- # It was found through benchmarking that making an array of final size
- # around 256x256 was faster by straight concatenation on a
- # i7-7700HQ processor and dual channel ram 2400MHz.
- # It didn't seem to matter heavily on the dtype used.
- #
- # A 2D array using repeated concatenation requires 2 copies of the array.
- #
- # The fastest algorithm will depend on the ratio of CPU power to memory
- # speed.
- # One can monitor the results of the benchmark
- # https://pv.github.io/numpy-bench/#bench_shape_base.Block2D.time_block2d
- # to tune this parameter until a C version of the `_block_info_recursion`
- # algorithm is implemented which would likely be faster than the python
- # version.
- if list_ndim * final_size > (2 * 512 * 512):
- return _block_slicing(arrays, list_ndim, result_ndim)
- else:
- return _block_concatenate(arrays, list_ndim, result_ndim)
- # These helper functions are mostly used for testing.
- # They allow us to write tests that directly call `_block_slicing`
- # or `_block_concatenate` without blocking large arrays to force the wisdom
- # to trigger the desired path.
- def _block_setup(arrays):
- """
- Returns
- (`arrays`, list_ndim, result_ndim, final_size)
- """
- bottom_index, arr_ndim, final_size = _block_check_depths_match(arrays)
- list_ndim = len(bottom_index)
- if bottom_index and bottom_index[-1] is None:
- raise ValueError(
- 'List at {} cannot be empty'.format(
- _block_format_index(bottom_index)
- )
- )
- result_ndim = max(arr_ndim, list_ndim)
- return arrays, list_ndim, result_ndim, final_size
- def _block_slicing(arrays, list_ndim, result_ndim):
- shape, slices, arrays = _block_info_recursion(
- arrays, list_ndim, result_ndim)
- dtype = _nx.result_type(*[arr.dtype for arr in arrays])
- # Test preferring F only in the case that all input arrays are F
- F_order = all(arr.flags['F_CONTIGUOUS'] for arr in arrays)
- C_order = all(arr.flags['C_CONTIGUOUS'] for arr in arrays)
- order = 'F' if F_order and not C_order else 'C'
- result = _nx.empty(shape=shape, dtype=dtype, order=order)
- # Note: In a c implementation, the function
- # PyArray_CreateMultiSortedStridePerm could be used for more advanced
- # guessing of the desired order.
- for the_slice, arr in zip(slices, arrays):
- result[(Ellipsis,) + the_slice] = arr
- return result
- def _block_concatenate(arrays, list_ndim, result_ndim):
- result = _block(arrays, list_ndim, result_ndim)
- if list_ndim == 0:
- # Catch an edge case where _block returns a view because
- # `arrays` is a single numpy array and not a list of numpy arrays.
- # This might copy scalars or lists twice, but this isn't a likely
- # usecase for those interested in performance
- result = result.copy()
- return result
|