summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Haldane <ealloc@gmail.com>2017-11-28 06:51:56 +0100
committerGitHub <noreply@github.com>2017-11-28 06:51:56 +0100
commitce140e8d5c28f1f057db1102cf78263e48f86d61 (patch)
tree393e51c5ed503976d400ad139977974dcebaaf06
parentf0f8d6e412c62643ef702c1a46352f0ef267a1a1 (diff)
parent21ef1383cb4f6e27af188a6da5cdca93cff1bd07 (diff)
downloadnumpy-ce140e8d5c28f1f057db1102cf78263e48f86d61.tar.gz
Merge pull request #9946 from eric-wieser/improve-take-docs
DOC: describe the expansion of take and apply_along_axis in detail
-rw-r--r--numpy/core/fromnumeric.py44
-rw-r--r--numpy/lib/shape_base.py28
2 files changed, 60 insertions, 12 deletions
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index 568d39781..a335a9f4a 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -66,15 +66,28 @@ def take(a, indices, axis=None, out=None, mode='raise'):
"""
Take elements from an array along an axis.
- This function does the same thing as "fancy" indexing (indexing arrays
- using arrays); however, it can be easier to use if you need elements
- along a given axis.
+ When axis is not None, this function does the same thing as "fancy"
+ indexing (indexing arrays using arrays); however, it can be easier to use
+ if you need elements along a given axis. A call such as
+ ``np.take(arr, indices, axis=3)`` is equivalent to
+ ``arr[:,:,:,indices,...]``.
+
+ Explained without fancy indexing, this is equivalent to the following use
+ of `ndindex`, which sets each of ``ii``, ``jj``, and ``kk`` to a tuple of
+ indices::
+
+ Ni, Nk = a.shape[:axis], a.shape[axis+1:]
+ Nj = indices.shape
+ for ii in ndindex(Ni):
+ for jj in ndindex(Nj):
+ for kk in ndindex(Nk):
+ out[ii + jj + kk] = a[ii + (indices[jj],) + kk]
Parameters
----------
- a : array_like
+ a : array_like (Ni..., M, Nk...)
The source array.
- indices : array_like
+ indices : array_like (Nj...)
The indices of the values to extract.
.. versionadded:: 1.8.0
@@ -83,7 +96,7 @@ def take(a, indices, axis=None, out=None, mode='raise'):
axis : int, optional
The axis over which to select values. By default, the flattened
input array is used.
- out : ndarray, optional
+ out : ndarray, optional (Ni..., Nj..., Nk...)
If provided, the result will be placed in this array. It should
be of the appropriate shape and dtype.
mode : {'raise', 'wrap', 'clip'}, optional
@@ -99,7 +112,7 @@ def take(a, indices, axis=None, out=None, mode='raise'):
Returns
-------
- subarray : ndarray
+ out : ndarray (Ni..., Nj..., Nk...)
The returned array has the same type as `a`.
See Also
@@ -107,6 +120,23 @@ def take(a, indices, axis=None, out=None, mode='raise'):
compress : Take elements using a boolean mask
ndarray.take : equivalent method
+ Notes
+ -----
+
+ By eliminating the inner loop in the description above, and using `s_` to
+ build simple slice objects, `take` can be expressed in terms of applying
+ fancy indexing to each 1-d slice::
+
+ Ni, Nk = a.shape[:axis], a.shape[axis+1:]
+ for ii in ndindex(Ni):
+ for kk in ndindex(Nj):
+ out[ii + s_[...,] + kk] = a[ii + s_[:,] + kk][indices]
+
+ For this reason, it is equivalent to (but faster than) the following use
+ of `apply_along_axis`::
+
+ out = np.apply_along_axis(lambda a_1d: a_1d[indices], axis, a)
+
Examples
--------
>>> a = [4, 3, 5, 7, 6, 8]
diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py
index 83e39f9f5..0e4f25046 100644
--- a/numpy/lib/shape_base.py
+++ b/numpy/lib/shape_base.py
@@ -27,14 +27,32 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs):
Execute `func1d(a, *args)` where `func1d` operates on 1-D arrays and `a`
is a 1-D slice of `arr` along `axis`.
+ This is equivalent to (but faster than) the following use of `ndindex` and
+ `s_`, which sets each of ``ii``, ``jj``, and ``kk`` to a tuple of indices::
+
+ Ni, Nk = a.shape[:axis], a.shape[axis+1:]
+ for ii in ndindex(Ni):
+ for kk in ndindex(Nk):
+ f = func1d(arr[ii + s_[:,] + kk])
+ Nj = f.shape
+ for jj in ndindex(Nj):
+ out[ii + jj + kk] = f[jj]
+
+ Equivalently, eliminating the inner loop, this can be expressed as::
+
+ Ni, Nk = a.shape[:axis], a.shape[axis+1:]
+ for ii in ndindex(Ni):
+ for kk in ndindex(Nk):
+ out[ii + s_[...,] + kk] = func1d(arr[ii + s_[:,] + kk])
+
Parameters
----------
- func1d : function
+ func1d : function (M,) -> (Nj...)
This function should accept 1-D arrays. It is applied to 1-D
slices of `arr` along the specified axis.
axis : integer
Axis along which `arr` is sliced.
- arr : ndarray
+ arr : ndarray (Ni..., M, Nk...)
Input array.
args : any
Additional arguments to `func1d`.
@@ -46,11 +64,11 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs):
Returns
-------
- apply_along_axis : ndarray
- The output array. The shape of `outarr` is identical to the shape of
+ out : ndarray (Ni..., Nj..., Nk...)
+ The output array. The shape of `out` is identical to the shape of
`arr`, except along the `axis` dimension. This axis is removed, and
replaced with new dimensions equal to the shape of the return value
- of `func1d`. So if `func1d` returns a scalar `outarr` will have one
+ of `func1d`. So if `func1d` returns a scalar `out` will have one
fewer dimensions than `arr`.
See Also