diff options
author | Allan Haldane <ealloc@gmail.com> | 2017-11-28 06:51:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-28 06:51:56 +0100 |
commit | ce140e8d5c28f1f057db1102cf78263e48f86d61 (patch) | |
tree | 393e51c5ed503976d400ad139977974dcebaaf06 | |
parent | f0f8d6e412c62643ef702c1a46352f0ef267a1a1 (diff) | |
parent | 21ef1383cb4f6e27af188a6da5cdca93cff1bd07 (diff) | |
download | numpy-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.py | 44 | ||||
-rw-r--r-- | numpy/lib/shape_base.py | 28 |
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 |