609 lines
18 KiB
Python
609 lines
18 KiB
Python
from __future__ import division, absolute_import, print_function
|
|
|
|
__all__ = ['atleast_1d', 'atleast_2d', 'atleast_3d', 'block', 'hstack',
|
|
'stack', 'vstack']
|
|
|
|
|
|
from . import numeric as _nx
|
|
from .numeric import array, asanyarray, newaxis
|
|
from .multiarray import normalize_axis_index
|
|
|
|
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(*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[newaxis,:]
|
|
else:
|
|
result = ary
|
|
res.append(result)
|
|
if len(res) == 1:
|
|
return res[0]
|
|
else:
|
|
return res
|
|
|
|
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)
|
|
...
|
|
[[[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[newaxis,:, newaxis]
|
|
elif ary.ndim == 2:
|
|
result = ary[:,:, newaxis]
|
|
else:
|
|
result = ary
|
|
res.append(result)
|
|
if len(res) == 1:
|
|
return res[0]
|
|
else:
|
|
return res
|
|
|
|
|
|
def vstack(tup):
|
|
"""
|
|
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.
|
|
|
|
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.
|
|
|
|
Returns
|
|
-------
|
|
stacked : ndarray
|
|
The array formed by stacking the given arrays, will be at least 2-D.
|
|
|
|
See Also
|
|
--------
|
|
stack : Join a sequence of arrays along a new axis.
|
|
hstack : Stack arrays in sequence horizontally (column wise).
|
|
dstack : Stack arrays in sequence depth wise (along third dimension).
|
|
concatenate : Join a sequence of arrays along an existing axis.
|
|
vsplit : Split array into a list of multiple sub-arrays vertically.
|
|
block : Assemble arrays from blocks.
|
|
|
|
Examples
|
|
--------
|
|
>>> a = np.array([1, 2, 3])
|
|
>>> b = np.array([2, 3, 4])
|
|
>>> np.vstack((a,b))
|
|
array([[1, 2, 3],
|
|
[2, 3, 4]])
|
|
|
|
>>> a = np.array([[1], [2], [3]])
|
|
>>> b = np.array([[2], [3], [4]])
|
|
>>> np.vstack((a,b))
|
|
array([[1],
|
|
[2],
|
|
[3],
|
|
[2],
|
|
[3],
|
|
[4]])
|
|
|
|
"""
|
|
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)
|
|
|
|
def hstack(tup):
|
|
"""
|
|
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.
|
|
|
|
Returns
|
|
-------
|
|
stacked : ndarray
|
|
The array formed by stacking the given arrays.
|
|
|
|
See Also
|
|
--------
|
|
stack : Join a sequence of arrays along a new axis.
|
|
vstack : Stack arrays in sequence vertically (row wise).
|
|
dstack : Stack arrays in sequence depth wise (along third axis).
|
|
concatenate : Join a sequence of arrays along an existing axis.
|
|
hsplit : Split array along second axis.
|
|
block : Assemble arrays from blocks.
|
|
|
|
Examples
|
|
--------
|
|
>>> a = np.array((1,2,3))
|
|
>>> b = np.array((2,3,4))
|
|
>>> np.hstack((a,b))
|
|
array([1, 2, 3, 2, 3, 4])
|
|
>>> a = np.array([[1],[2],[3]])
|
|
>>> b = np.array([[2],[3],[4]])
|
|
>>> np.hstack((a,b))
|
|
array([[1, 2],
|
|
[2, 3],
|
|
[3, 4]])
|
|
|
|
"""
|
|
arrs = [atleast_1d(_m) for _m in tup]
|
|
# As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
|
|
if arrs and arrs[0].ndim == 1:
|
|
return _nx.concatenate(arrs, 0)
|
|
else:
|
|
return _nx.concatenate(arrs, 1)
|
|
|
|
|
|
def stack(arrays, axis=0, out=None):
|
|
"""
|
|
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.
|
|
|
|
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.
|
|
split : Split array into a list of multiple sub-arrays of equal size.
|
|
block : Assemble arrays from blocks.
|
|
|
|
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([2, 3, 4])
|
|
>>> np.stack((a, b))
|
|
array([[1, 2, 3],
|
|
[2, 3, 4]])
|
|
|
|
>>> np.stack((a, b), axis=-1)
|
|
array([[1, 2],
|
|
[2, 3],
|
|
[3, 4]])
|
|
|
|
"""
|
|
arrays = [asanyarray(arr) for arr in arrays]
|
|
if not arrays:
|
|
raise ValueError('need at least one array to stack')
|
|
|
|
shapes = set(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)
|
|
|
|
|
|
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.
|
|
|
|
The parameter `parent_index` is the full index of `arrays` within the
|
|
nested lists passed to _block_check_depths_match at the top of the
|
|
recursion.
|
|
The return value is a pair. The first item returned is the full index
|
|
of an element (specifically the first element) from the bottom of the
|
|
nesting in `arrays`. An empty list at the bottom of the nesting is
|
|
represented by a `None` index.
|
|
The second item is the maximum of the ndims of the arrays nested in
|
|
`arrays`.
|
|
"""
|
|
def format_index(index):
|
|
idx_str = ''.join('[{}]'.format(i) for i in index if i is not None)
|
|
return 'arrays' + idx_str
|
|
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(
|
|
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 = next(idxs_ndims)
|
|
for index, ndim in idxs_ndims:
|
|
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),
|
|
format_index(index)
|
|
)
|
|
)
|
|
return first_index, max_arr_ndim
|
|
elif type(arrays) is list and len(arrays) == 0:
|
|
# We've 'bottomed out' on an empty list
|
|
return parent_index + [None], 0
|
|
else:
|
|
# We've 'bottomed out' - arrays is either a scalar or an array
|
|
return parent_index, _nx.ndim(arrays)
|
|
|
|
|
|
def _block(arrays, max_depth, result_ndim):
|
|
"""
|
|
Internal implementation of block. `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).
|
|
"""
|
|
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 block_recursion(arrays, depth=0):
|
|
if depth < max_depth:
|
|
if len(arrays) == 0:
|
|
raise ValueError('Lists cannot be empty')
|
|
arrs = [block_recursion(arr, depth+1) for arr in arrays]
|
|
return _nx.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)
|
|
|
|
try:
|
|
return block_recursion(arrays)
|
|
finally:
|
|
# recursive closures have a cyclic reference to themselves, which
|
|
# requires gc to collect (gh-10620). To avoid this problem, for
|
|
# performance and PyPy friendliness, we break the cycle:
|
|
block_recursion = None
|
|
|
|
|
|
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 together.
|
|
stack : Stack arrays in sequence along a new dimension.
|
|
hstack : Stack arrays in sequence horizontally (column wise).
|
|
vstack : Stack arrays in sequence vertically (row wise).
|
|
dstack : Stack arrays in sequence depth wise (along third dimension).
|
|
vsplit : Split array into a list of multiple sub-arrays vertically.
|
|
|
|
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([2, 3, 4])
|
|
>>> np.block([a, b, 10]) # hstack([a, b, 10])
|
|
array([1, 2, 3, 2, 3, 4, 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([2, 3, 4])
|
|
>>> np.block([[a], [b]]) # vstack([a, b])
|
|
array([[1, 2, 3],
|
|
[2, 3, 4]])
|
|
|
|
>>> 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]])
|
|
|
|
|
|
"""
|
|
bottom_index, arr_ndim = _block_check_depths_match(arrays)
|
|
list_ndim = len(bottom_index)
|
|
return _block(arrays, list_ndim, max(arr_ndim, list_ndim))
|