diff options
| author | Mukulika <mukulikapahari@gmail.com> | 2021-07-05 17:54:33 +0530 |
|---|---|---|
| committer | Mukulika <mukulikapahari@gmail.com> | 2021-08-18 22:13:26 +0530 |
| commit | 31803caed1aec021778d79fcf5fafc516becd3a6 (patch) | |
| tree | 7c5e1a032188474d28c3be49f1a2cde7bfc490a6 /doc/source/reference | |
| parent | f520fc17373023598f6ee11d9770faf5bdd13e4d (diff) | |
| download | numpy-31803caed1aec021778d79fcf5fafc516becd3a6.tar.gz | |
DOC: Rearranged parts of the Indexing docs to consolidate content
Diffstat (limited to 'doc/source/reference')
| -rw-r--r-- | doc/source/reference/arrays.indexing.rst | 567 |
1 files changed, 364 insertions, 203 deletions
diff --git a/doc/source/reference/arrays.indexing.rst b/doc/source/reference/arrays.indexing.rst index 9f82875ea..58b87dd7f 100644 --- a/doc/source/reference/arrays.indexing.rst +++ b/doc/source/reference/arrays.indexing.rst @@ -8,7 +8,9 @@ Indexing .. seealso:: - :ref:`Indexing basics <basics.indexing>` + :ref:`basics.indexing` + + :ref:`Indexing routines <routines.indexing>` .. sectionauthor:: adapted from "Guide to NumPy" by Travis E. Oliphant @@ -18,17 +20,76 @@ Indexing :class:`ndarrays <ndarray>` can be indexed using the standard Python ``x[obj]`` syntax, where *x* is the array and *obj* the selection. -There are three kinds of indexing available: field access, basic -slicing, advanced indexing. Which one occurs depends on *obj*. +There are four kinds of indexing available depending on *obj*: +single element indexing, basic slicing, advanced indexing and field access. + +Most of the following examples show the use of indexing when +referencing data in an array. The examples work just as well +when assigning to an array. See :ref:`assigning-values-to-indexed-arrays` for +specific examples and explanations on how assignments work. + +Note that in Python, ``x[(exp1, exp2, ..., expN)]`` is equivalent to +``x[exp1, exp2, ..., expN]``; the latter is just syntactic sugar +for the former. + +.. _single-element-indexing: + +Single element indexing +----------------------- + +Single element indexing works +exactly like that for other standard Python sequences. It is 0-based, +and accepts negative indices for indexing from the end of the array. :: + + >>> x = np.arange(10) + >>> x[2] + 2 + >>> x[-2] + 8 + +It is not necessary to +separate each dimension's index into its own set of square brackets. :: + + >>> x.shape = (2,5) # now x is 2-dimensional + >>> x[1,3] + 8 + >>> x[1,-1] + 9 + +Note that if one indexes a multidimensional array with fewer indices +than dimensions, one gets a subdimensional array. For example: :: + + >>> x[0] + array([0, 1, 2, 3, 4]) + +That is, each index specified selects the array corresponding to the +rest of the dimensions selected. In the above example, choosing 0 +means that the remaining dimension of length 5 is being left unspecified, +and that what is returned is an array of that dimensionality and size. +It must be noted that the returned array is not a copy of the original, +but points to the same values in memory as does the original array. +In this case, the 1-D array at the first position (0) is returned. +So using a single index on the returned array, results in a single +element being returned. That is: :: + + >>> x[0][2] + 2 + +So note that ``x[0,2] = x[0][2]`` though the second case is more +inefficient as a new temporary array is created after the first index +that is subsequently indexed by 2. .. note:: - In Python, ``x[(exp1, exp2, ..., expN)]`` is equivalent to - ``x[exp1, exp2, ..., expN]``; the latter is just syntactic sugar - for the former. + NumPy uses C-order indexing. That means that the last + index usually represents the most rapidly changing memory location, + unlike Fortran or IDL, where the first index represents the most + rapidly changing location in memory. This difference represents a + great potential for confusion. +.. _basic-slicing-and-indexing: -Basic Slicing and Indexing +Basic slicing and indexing -------------------------- Basic slicing extends Python's basic concept of slicing to N @@ -67,7 +128,7 @@ of the original array. .. note:: NumPy slicing creates a :term:`view` instead of a copy as in the case of - builtin Python sequences such as string, tuple and list. + built-in Python sequences such as string, tuple and list. Care must be taken when extracting a small portion from a large array which becomes useless after the extraction, because the small portion extracted contains a reference @@ -85,9 +146,8 @@ concepts to remember include: index values *i*, *i + k*, ..., *i + (m - 1) k* where :math:`m = q + (r\neq0)` and *q* and *r* are the quotient and remainder obtained by dividing *j - i* by *k*: *j - i = q k + r*, so that - *i + (m - 1) k < j*. - - .. admonition:: Example + *i + (m - 1) k < j*. + For example:: >>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> x[1:7:2] @@ -96,8 +156,7 @@ concepts to remember include: - Negative *i* and *j* are interpreted as *n + i* and *n + j* where *n* is the number of elements in the corresponding dimension. Negative *k* makes stepping go towards smaller indices. - - .. admonition:: Example + From the above example:: >>> x[-2:10] array([8, 9]) @@ -110,16 +169,14 @@ concepts to remember include: and *-n-1* for *k < 0* . If *k* is not given it defaults to 1. Note that ``::`` is the same as ``:`` and means select all indices along this axis. - - .. admonition:: Example + From the above example:: >>> x[5:] array([5, 6, 7, 8, 9]) - If the number of objects in the selection tuple is less than *N*, then ``:`` is assumed for any subsequent dimensions. - - .. admonition:: Example + For example:: >>> x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) >>> x.shape @@ -129,27 +186,6 @@ concepts to remember include: [5], [6]]]) -- :py:data:`Ellipsis` expands to the number of ``:`` objects needed for the - selection tuple to index all dimensions. In most cases, this means that - length of the expanded selection tuple is ``x.ndim``. There may only be a - single ellipsis present. - - .. admonition:: Example - - >>> x[...,0] - array([[1, 2, 3], - [4, 5, 6]]) - -- Each :const:`newaxis` object in the selection tuple serves to expand - the dimensions of the resulting selection by one unit-length - dimension. The added dimension is the position of the :const:`newaxis` - object in the selection tuple. - - .. admonition:: Example - - >>> x[:,np.newaxis,:,:].shape - (2, 1, 3, 1) - - An integer, *i*, returns the same values as ``i:i+1`` **except** the dimensionality of the returned object is reduced by 1. In particular, a selection tuple with the *p*-th @@ -178,31 +214,71 @@ concepts to remember include: ``x[obj] = value`` must be (broadcastable) to the same shape as ``x[obj]``. +- A slicing tuple can always be constructed as *obj* + and used in the ``x[obj]`` notation. Slice objects can be used in + the construction in place of the ``[start:stop:step]`` + notation. For example, ``x[1:10:5,::-1]`` can also be implemented + as ``obj = (slice(1,10,5), slice(None,None,-1)); x[obj]`` . This + can be useful for constructing generic code that works on arrays + of arbitrary dimensions. See :ref:`dealing-with-variable-indices` + for more information. + .. index:: pair: ndarray; view -.. note:: +Structural indexing tools +^^^^^^^^^^^^^^^^^^^^^^^^^ +There are some tools to facilitate the easy matching of array shapes with +expressions and in assignments. + +:py:data:`Ellipsis` expands to the number of ``:`` objects needed for the +selection tuple to index all dimensions. In most cases, this means that the +length of the expanded selection tuple is ``x.ndim``. There may only be a +single ellipsis present. +From the above example:: + + >>> x[...,0] + array([[1, 2, 3], + [4, 5, 6]]) + +This is equivalent to:: - Remember that a slicing tuple can always be constructed as *obj* - and used in the ``x[obj]`` notation. Slice objects can be used in - the construction in place of the ``[start:stop:step]`` - notation. For example, ``x[1:10:5,::-1]`` can also be implemented - as ``obj = (slice(1,10,5), slice(None,None,-1)); x[obj]`` . This - can be useful for constructing generic code that works on arrays - of arbitrary dimension. + >>> x[:,:,0] + array([[1, 2, 3], + [4, 5, 6]]) -.. data:: newaxis - :noindex: +Each :const:`newaxis` object in the selection tuple serves to expand +the dimensions of the resulting selection by one unit-length +dimension. The added dimension is the position of the :const:`newaxis` +object in the selection tuple. :const:`newaxis` is an alias for +'None', and 'None' can be used in place of this with the same result. +From the above example:: + + >>> x[:,np.newaxis,:,:].shape + (2, 1, 3, 1) + >>> x[:,None,:,:].shape + (2, 1, 3, 1) + +This can be handy to combine two +arrays in a way that otherwise would require explicitly reshaping +operations. For example:: + + >>> x = np.arange(5) + >>> x[:,np.newaxis] + x[np.newaxis,:] + array([[0, 1, 2, 3, 4], + [1, 2, 3, 4, 5], + [2, 3, 4, 5, 6], + [3, 4, 5, 6, 7], + [4, 5, 6, 7, 8]]) - The :const:`newaxis` object can be used in all slicing operations to - create an axis of length one. :const:`newaxis` is an alias for - 'None', and 'None' can be used in place of this with the same result. .. _advanced-indexing: -Advanced Indexing +Advanced indexing ----------------- +.. seealso:: :ref:`basics.broadcasting` + Advanced indexing is triggered when the selection object, *obj*, is a non-tuple sequence object, an :class:`ndarray` (of data type integer or bool), or a tuple with at least one sequence object or ndarray (of data type @@ -229,29 +305,40 @@ Integer array indexing Integer array indexing allows selection of arbitrary items in the array based on their *N*-dimensional index. Each integer array represents a number -of indexes into that dimension. +of indices into that dimension. + +Negative values are permitted in the index arrays and work as they do with +single indices or slices. If the index values are out of bounds then an +``IndexError`` is thrown:: + + >>> x = np.array([[1, 2], [3, 4], [5, 6]]) + >>> x[np.array([1, -1])] + array([[3, 4], + [5, 6]]) + >>> x[np.array([3, 4])] + IndexError: index 3 is out of bounds for axis 0 with size 3 -Purely integer array indexing -""""""""""""""""""""""""""""" -When the index consists of as many integer arrays as the array being indexed -has dimensions, the indexing is straight forward, but different from slicing. +When the index consists of as many integer arrays as dimensions of the array +being indexed, the indexing is straightforward, but different from slicing. -Advanced indexes always are :ref:`broadcast<ufuncs.broadcasting>` and +Advanced indices always are :ref:`broadcast<ufuncs.broadcasting>` and iterated as *one*:: result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M], ..., ind_N[i_1, ..., i_M]] -Note that the result shape is identical to the (broadcast) indexing array -shapes ``ind_1, ..., ind_N``. +Note that the resulting shape is identical to the (broadcast) indexing array +shapes ``ind_1, ..., ind_N``. If the indices cannot be broadcast to the +same shape, an exception ``IndexError: shape mismatch: indexing arrays could +not be broadcast together with shapes...`` is raised. -.. admonition:: Example +.. rubric:: Example - From each row, a specific element should be selected. The row index is just - ``[0, 1, 2]`` and the column index specifies the element to choose for the - corresponding row, here ``[0, 1, 0]``. Using both together the task - can be solved using advanced indexing: +From each row, a specific element should be selected. The row index is just +``[0, 1, 2]`` and the column index specifies the element to choose for the +corresponding row, here ``[0, 1, 0]``. Using both together the task +can be solved using advanced indexing:: >>> x = np.array([[1, 2], [3, 4], [5, 6]]) >>> x[[0, 1, 2], [0, 1, 0]] @@ -261,13 +348,13 @@ To achieve a behaviour similar to the basic slicing above, broadcasting can be used. The function :func:`ix_` can help with this broadcasting. This is best understood with an example. -.. admonition:: Example +.. rubric:: Example - From a 4x3 array the corner elements should be selected using advanced - indexing. Thus all elements for which the column is one of ``[0, 2]`` and - the row is one of ``[0, 3]`` need to be selected. To use advanced indexing - one needs to select all elements *explicitly*. Using the method explained - previously one could write: +From a 4x3 array the corner elements should be selected using advanced +indexing. Thus all elements for which the column is one of ``[0, 2]`` and +the row is one of ``[0, 3]`` need to be selected. To use advanced indexing +one needs to select all elements *explicitly*. Using the method explained +previously one could write:: >>> x = np.array([[ 0, 1, 2], ... [ 3, 4, 5], @@ -281,9 +368,9 @@ understood with an example. array([[ 0, 2], [ 9, 11]]) - However, since the indexing arrays above just repeat themselves, - broadcasting can be used (compare operations such as - ``rows[:, np.newaxis] + columns``) to simplify this: +However, since the indexing arrays above just repeat themselves, +broadcasting can be used (compare operations such as +``rows[:, np.newaxis] + columns``) to simplify this:: >>> rows = np.array([0, 3], dtype=np.intp) >>> columns = np.array([0, 2], dtype=np.intp) @@ -294,88 +381,42 @@ understood with an example. array([[ 0, 2], [ 9, 11]]) - This broadcasting can also be achieved using the function :func:`ix_`: +This broadcasting can also be achieved using the function :func:`ix_`: >>> x[np.ix_(rows, columns)] array([[ 0, 2], [ 9, 11]]) - Note that without the ``np.ix_`` call, only the diagonal elements would - be selected, as was used in the previous example. This difference is the - most important thing to remember about indexing with multiple advanced - indexes. - -.. _combining-advanced-and-basic-indexing: - -Combining advanced and basic indexing -""""""""""""""""""""""""""""""""""""" - -When there is at least one slice (``:``), ellipsis (``...``) or :const:`newaxis` -in the index (or the array has more dimensions than there are advanced indexes), -then the behaviour can be more complicated. It is like concatenating the -indexing result for each advanced index element - -In the simplest case, there is only a *single* advanced index. A single -advanced index can for example replace a slice and the result array will be -the same, however, it is a copy and may have a different memory layout. -A slice is preferable when it is possible. - -.. admonition:: Example - - >>> x[1:2, 1:3] - array([[4, 5]]) - >>> x[1:2, [1, 2]] - array([[4, 5]]) +Note that without the ``np.ix_`` call, only the diagonal elements would +be selected, as was used in the previous example. This difference is the +most important thing to remember about indexing with multiple advanced +indices. -The easiest way to understand the situation may be to think in -terms of the result shape. There are two parts to the indexing operation, -the subspace defined by the basic indexing (excluding integers) and the -subspace from the advanced indexing part. Two cases of index combination -need to be distinguished: - -* The advanced indexes are separated by a slice, :py:data:`Ellipsis` or :const:`newaxis`. - For example ``x[arr1, :, arr2]``. -* The advanced indexes are all next to each other. - For example ``x[..., arr1, arr2, :]`` but *not* ``x[arr1, :, 1]`` - since ``1`` is an advanced index in this regard. - -In the first case, the dimensions resulting from the advanced indexing -operation come first in the result array, and the subspace dimensions after -that. -In the second case, the dimensions from the advanced indexing operations -are inserted into the result array at the same spot as they were in the -initial array (the latter logic is what makes simple advanced indexing -behave just like slicing). +.. rubric:: Example -.. admonition:: Example +The broadcasting mechanism permits index arrays to be combined with +scalars for other indices. The effect is that the scalar value is used +for all the corresponding values of the index arrays:: - Suppose ``x.shape`` is (10,20,30) and ``ind`` is a (2,3,4)-shaped - indexing :class:`intp` array, then ``result = x[...,ind,:]`` has - shape (10,2,3,4,30) because the (20,)-shaped subspace has been - replaced with a (2,3,4)-shaped broadcasted indexing subspace. If - we let *i, j, k* loop over the (2,3,4)-shaped subspace then - ``result[...,i,j,k,:] = x[...,ind[i,j,k],:]``. This example - produces the same result as :meth:`x.take(ind, axis=-2) <ndarray.take>`. + >>> x = np.arange(35).reshape(5,7) + >>> x[np.array([0,2,4]), 1] + array([ 1, 15, 29]) -.. admonition:: Example +.. rubric:: Example - Let ``x.shape`` be (10,20,30,40,50) and suppose ``ind_1`` - and ``ind_2`` can be broadcast to the shape (2,3,4). Then - ``x[:,ind_1,ind_2]`` has shape (10,2,3,4,40,50) because the - (20,30)-shaped subspace from X has been replaced with the - (2,3,4) subspace from the indices. However, - ``x[:,ind_1,:,ind_2]`` has shape (2,3,4,10,30,50) because there - is no unambiguous place to drop in the indexing subspace, thus - it is tacked-on to the beginning. It is always possible to use - :meth:`.transpose() <ndarray.transpose>` to move the subspace - anywhere desired. Note that this example cannot be replicated - using :func:`take`. +A real-life example of where advanced indexing may be useful is for a color +lookup table where we want to map the values of an image into RGB triples for +display. The lookup table could have a shape (nlookup, 3). Indexing +such an array with an image with shape (ny, nx) with dtype=np.uint8 +(or any integer type so long as values are with the bounds of the +lookup table) will result in an array of shape (ny, nx, 3) where a +triple of RGB values is associated with each pixel location. Boolean array indexing ^^^^^^^^^^^^^^^^^^^^^^ -This advanced indexing occurs when obj is an array object of Boolean +This advanced indexing occurs when *obj* is an array object of Boolean type, such as may be returned from comparison operators. A single boolean index array is practically identical to ``x[obj.nonzero()]`` where, as described above, :meth:`obj.nonzero() <ndarray.nonzero>` returns a @@ -390,17 +431,15 @@ C-style. If *obj* has :py:data:`True` values at entries that are outside of the bounds of *x*, then an index error will be raised. If *obj* is smaller than *x* it is identical to filling it with :py:data:`False`. -.. admonition:: Example - - A common use case for this is filtering for desired element values. - For example one may wish to select all entries from an array which - are not NaN: +A common use case for this is filtering for desired element values. +For example, one may wish to select all entries from an array which +are not NaN:: >>> x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]]) >>> x[~np.isnan(x)] array([1., 2., 3.]) - Or wish to add a constant to all negative elements: +Or wish to add a constant to all negative elements:: >>> x = np.array([1., -1., -2., 3]) >>> x[x < 0] += 20 @@ -418,9 +457,9 @@ this is straight forward. Care must only be taken to make sure that the boolean index has *exactly* as many dimensions as it is supposed to work with. -.. admonition:: Example +.. rubric:: Example - From an array, select all rows which sum up to less or equal two: +From an array, select all rows which sum up to less or equal two:: >>> x = np.array([[0, 1], [1, 1], [2, 2]]) >>> rowsum = x.sum(-1) @@ -434,12 +473,12 @@ indexing array can best be understood with the :meth:`obj.nonzero() <ndarray.nonzero>` analogy. The function :func:`ix_` also supports boolean arrays and will work without any surprises. -.. admonition:: Example +.. rubric:: Example - Use boolean indexing to select all rows adding up to an even - number. At the same time columns 0 and 2 should be selected with an - advanced integer index. Using the :func:`ix_` function this can be done - with: +Use boolean indexing to select all rows adding up to an even +number. At the same time columns 0 and 2 should be selected with an +advanced integer index. Using the :func:`ix_` function this can be done +with:: >>> x = np.array([[ 0, 1, 2], ... [ 3, 4, 5], @@ -453,64 +492,145 @@ also supports boolean arrays and will work without any surprises. array([[ 3, 5], [ 9, 11]]) - Without the ``np.ix_`` call, only the diagonal elements would be - selected. +Without the ``np.ix_`` call, only the diagonal elements would be +selected. - Or without ``np.ix_`` (compare the integer array examples): +Or without ``np.ix_`` (compare the integer array examples):: >>> rows = rows.nonzero()[0] >>> x[rows[:, np.newaxis], columns] array([[ 3, 5], [ 9, 11]]) -Detailed notes --------------- +.. rubric:: Example -These are some detailed notes, which are not of importance for day to day -indexing (in no particular order): +If x has more dimensions than b then the result will be multi-dimensional:: -* The native NumPy indexing type is ``intp`` and may differ from the - default integer array type. ``intp`` is the smallest data type - sufficient to safely index any array; for advanced indexing it may be - faster than other types. -* For advanced assignments, there is in general no guarantee for the - iteration order. This means that if an element is set more than once, - it is not possible to predict the final result. -* An empty (tuple) index is a full scalar index into a zero dimensional array. - ``x[()]`` returns a *scalar* if ``x`` is zero dimensional and a view - otherwise. On the other hand ``x[...]`` always returns a view. -* If a zero dimensional array is present in the index *and* it is a full - integer index the result will be a *scalar* and not a zero dimensional array. - (Advanced indexing is not triggered.) -* When an ellipsis (``...``) is present but has no size (i.e. replaces zero - ``:``) the result will still always be an array. A view if no advanced index - is present, otherwise a copy. -* the ``nonzero`` equivalence for Boolean arrays does not hold for zero - dimensional boolean arrays. -* When the result of an advanced indexing operation has no elements but an - individual index is out of bounds, whether or not an ``IndexError`` is - raised is undefined (e.g. ``x[[], [123]]`` with ``123`` being out of bounds). -* When a *casting* error occurs during assignment (for example updating a - numerical array using a sequence of strings), the array being assigned - to may end up in an unpredictable partially updated state. - However, if any other error (such as an out of bounds index) occurs, the - array will remain unchanged. -* The memory layout of an advanced indexing result is optimized for each - indexing operation and no particular memory order can be assumed. -* When using a subclass (especially one which manipulates its shape), the - default ``ndarray.__setitem__`` behaviour will call ``__getitem__`` for - *basic* indexing but not for *advanced* indexing. For such a subclass it may - be preferable to call ``ndarray.__setitem__`` with a *base class* ndarray - view on the data. This *must* be done if the subclasses ``__getitem__`` does - not return views. + >>> x = np.arange(35).reshape(5,7) + >>> b = x>20 + >>> b[:,5] + array([False, False, False, True, True]) + >>> x[b[:,5]] + array([[21, 22, 23, 24, 25, 26, 27], + [28, 29, 30, 31, 32, 33, 34]]) -.. _arrays.indexing.fields: +Here the 4th and 5th rows are selected from the indexed array and +combined to make a 2-D array. + +.. rubric:: Example +Using a 2-D boolean array of shape (2,3) +with four True elements to select rows from a 3-D array of shape +(2,3,5) results in a 2-D result of shape (4,5):: -Field Access + >>> x = np.arange(30).reshape(2,3,5) + >>> x + array([[[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]], + [[15, 16, 17, 18, 19], + [20, 21, 22, 23, 24], + [25, 26, 27, 28, 29]]]) + >>> b = np.array([[True, True, False], [False, True, True]]) + >>> x[b] + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [20, 21, 22, 23, 24], + [25, 26, 27, 28, 29]]) + + +.. _combining-advanced-and-basic-indexing: + +Combining advanced and basic indexing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When there is at least one slice (``:``), ellipsis (``...``) or :const:`newaxis` +in the index (or the array has more dimensions than there are advanced indices), +then the behaviour can be more complicated. It is like concatenating the +indexing result for each advanced index element. + +In the simplest case, there is only a *single* advanced index. A single +advanced index can for example replace a slice and the result array will be +the same, however, it is a copy and may have a different memory layout. +A slice is preferable when it is possible. +For example:: + + >>> x = np.array([[ 0, 1, 2], + ... [ 3, 4, 5], + ... [ 6, 7, 8], + ... [ 9, 10, 11]]) + >>> x[1:2, 1:3] + array([[4, 5]]) + >>> x[1:2, [1, 2]] + array([[4, 5]]) + +The easiest way to understand the situation may be to think in +terms of the resulting shape. There are two parts to the indexing operation, +the subspace defined by the basic indexing (excluding integers) and the +subspace from the advanced indexing part. Two cases of index combination +need to be distinguished: + +* The advanced indices are separated by a slice, :py:data:`Ellipsis` or + :const:`newaxis`. For example ``x[arr1, :, arr2]``. +* The advanced indices are all next to each other. + For example ``x[..., arr1, arr2, :]`` but *not* ``x[arr1, :, 1]`` + since ``1`` is an advanced index in this regard. + +In the first case, the dimensions resulting from the advanced indexing +operation come first in the result array, and the subspace dimensions after +that. +In the second case, the dimensions from the advanced indexing operations +are inserted into the result array at the same spot as they were in the +initial array (the latter logic is what makes simple advanced indexing +behave just like slicing). + +.. rubric:: Example + +Suppose ``x.shape`` is (10,20,30) and ``ind`` is a (2,3,4)-shaped +indexing :class:`intp` array, then ``result = x[...,ind,:]`` has +shape (10,2,3,4,30) because the (20,)-shaped subspace has been +replaced with a (2,3,4)-shaped broadcasted indexing subspace. If +we let *i, j, k* loop over the (2,3,4)-shaped subspace then +``result[...,i,j,k,:] = x[...,ind[i,j,k],:]``. This example +produces the same result as :meth:`x.take(ind, axis=-2) <ndarray.take>`. + +.. rubric:: Example + +Let ``x.shape`` be (10,20,30,40,50) and suppose ``ind_1`` +and ``ind_2`` can be broadcast to the shape (2,3,4). Then +``x[:,ind_1,ind_2]`` has shape (10,2,3,4,40,50) because the +(20,30)-shaped subspace from X has been replaced with the +(2,3,4) subspace from the indices. However, +``x[:,ind_1,:,ind_2]`` has shape (2,3,4,10,30,50) because there +is no unambiguous place to drop in the indexing subspace, thus +it is tacked-on to the beginning. It is always possible to use +:meth:`.transpose() <ndarray.transpose>` to move the subspace +anywhere desired. Note that this example cannot be replicated +using :func:`take`. + +.. rubric:: Example + +Slicing can be combined with broadcasted boolean indices:: + + >>> b = y > 20 + >>> b + array([[False, False, False, False, False, False, False], + [False, False, False, False, False, False, False], + [False, False, False, False, False, False, False], + [ True, True, True, True, True, True, True], + [ True, True, True, True, True, True, True]]) + >>> y[b[:,5],1:3] + array([[22, 23], + [29, 30]]) + + +.. _arrays.indexing.fields: + +Field access ------------- -.. seealso:: :ref:`arrays.dtypes`, :ref:`arrays.scalars` +.. seealso:: :ref:`arrays.dtypes`, :ref:`arrays.scalars`, + :ref:`structured_arrays` If the :class:`ndarray` object is a structured array the :term:`fields <field>` of the array can be accessed by indexing the array with strings, @@ -519,19 +639,18 @@ dictionary-like. Indexing ``x['field-name']`` returns a new :term:`view` to the array, which is of the same shape as *x* (except when the field is a sub-array) but of data type ``x.dtype['field-name']`` and contains -only the part of the data in the specified field. Also +only the part of the data in the specified field. Also, :ref:`record array <arrays.classes.rec>` scalars can be "indexed" this way. Indexing into a structured array can also be done with a list of field names, -*e.g.* ``x[['field-name1','field-name2']]``. As of NumPy 1.16 this returns a -view containing only those fields. In older versions of numpy it returned a +e.g. ``x[['field-name1','field-name2']]``. As of NumPy 1.16, this returns a +view containing only those fields. In older versions of NumPy, it returned a copy. See the user guide section on :ref:`structured_arrays` for more information on multifield indexing. If the accessed field is a sub-array, the dimensions of the sub-array are appended to the shape of the result. - -.. admonition:: Example +For example:: >>> x = np.zeros((2,2), dtype=[('a', np.int32), ('b', np.float64, (3,3))]) >>> x['a'].shape @@ -543,6 +662,7 @@ are appended to the shape of the result. >>> x['b'].dtype dtype('float64') +.. _flat-iterator-indexing: Flat Iterator indexing ---------------------- @@ -559,3 +679,44 @@ returned array is therefore the shape of the integer indexing object. .. index:: single: indexing single: ndarray + +Detailed notes +-------------- + +These are some detailed notes, which are not of importance for day to day +indexing (in no particular order): + +* The native NumPy indexing type is ``intp`` and may differ from the + default integer array type. ``intp`` is the smallest data type + sufficient to safely index any array; for advanced indexing it may be + faster than other types. +* For advanced assignments, there is in general no guarantee for the + iteration order. This means that if an element is set more than once, + it is not possible to predict the final result. +* An empty (tuple) index is a full scalar index into a zero-dimensional array. + ``x[()]`` returns a *scalar* if ``x`` is zero-dimensional and a view + otherwise. On the other hand, ``x[...]`` always returns a view. +* If a zero-dimensional array is present in the index *and* it is a full + integer index the result will be a *scalar* and not a zero-dimensional array. + (Advanced indexing is not triggered.) +* When an ellipsis (``...``) is present but has no size (i.e. replaces zero + ``:``) the result will still always be an array. A view if no advanced index + is present, otherwise a copy. +* The ``nonzero`` equivalence for Boolean arrays does not hold for zero + dimensional boolean arrays. +* When the result of an advanced indexing operation has no elements but an + individual index is out of bounds, whether or not an ``IndexError`` is + raised is undefined (e.g. ``x[[], [123]]`` with ``123`` being out of bounds). +* When a *casting* error occurs during assignment (for example updating a + numerical array using a sequence of strings), the array being assigned + to may end up in an unpredictable partially updated state. + However, if any other error (such as an out of bounds index) occurs, the + array will remain unchanged. +* The memory layout of an advanced indexing result is optimized for each + indexing operation and no particular memory order can be assumed. +* When using a subclass (especially one which manipulates its shape), the + default ``ndarray.__setitem__`` behaviour will call ``__getitem__`` for + *basic* indexing but not for *advanced* indexing. For such a subclass it may + be preferable to call ``ndarray.__setitem__`` with a *base class* ndarray + view on the data. This *must* be done if the subclasses ``__getitem__`` does + not return views.
\ No newline at end of file |
