Index Types

The ndindex API consists of classes to represent the different kinds of NumPy indices, Integer, Slice, ellipsis, Newaxis, Tuple, IntegerArray, and BooleanArray. Typical usage of ndindex consists of constructing one of these classes, typically with the ndindex() constructor, then using the methods on the objects. With a few exceptions, all index classes have the same set of methods, so that they can be used uniformly regardless of the actual index type. Consequently, many of the method docstrings below are duplicated across all the classes. For classes where there is are particular things of note for a given method, the docstring will be different (for example, Slice.reduce() notes the specific invariants that the reduce() method applies to Slice objects). Such methods will be noted by their “See Also” sections.

class ndindex.Integer(idx)[source]

Represents an integer index on an axis of an nd-array.

Any object that implements __index__ can be used as an integer index.

>>> from ndindex import Integer
>>> idx = Integer(1)
>>> [0, 1, 2][idx.raw]
1
>>> idx = Integer(-3)
>>> [0, 1, 2][idx.raw]
0

Note that Integer itself implements __index__, so it can be used as an index directly. However, it is still recommended to use raw for consistency, as this only works for Integer.

Note

Integer does not represent an integer, but rather an integer index. It does not have most methods that int has, and should not be used in non-indexing contexts. See the document on Type Confusion for more details.

__eq__(other)[source]

Return self==value.

__hash__()[source]

Return hash(self).

__len__()[source]

Returns the number of elements indexed by self

Since self is an integer index, this always returns 1. Note that integer indices always remove an axis.

as_subindex(index)[source]

i.as_subindex(j) produces an index k such that a[j][k] gives all of the elements of a[j] that are also in a[i].

If a[j] is a subset of a[i], then a[j][k] == a[i]. Otherwise, a[j][k] == a[i & j], where i & j is the intersection of i and j, that is, the elements of a that are indexed by both i and j.

For example, in the below diagram, i and j index a subset of the array a. k = i.as_subindex(j) is an index on a[j] that gives the subset of a[j] also included in a[i]:

    +------------ self ------------+
    |                              |
------------------- a -----------------------
       |                                 |
       +------------- index -------------+
       |                           |
       +- self.as_subindex(index) -+

i.as_subindex(j) is currently only implemented when j is a slice with positive steps and nonnegative start and stop, or a Tuple of the same. To use it with slices with negative start or stop, call reduce() with a shape first.

as_subindex can be seen as the left-inverse of composition, that is, if a[i] = a[j][k], then k = i.as_subindex(j), so that k "=" (j^-1)[i] (this only works as a true inverse if j is a subset of i).

Note that due to symmetry, a[j][i.as_subindex(j)] and a[i][j.as_subindex(i)] will give the same subarrays of a, which will be the array of elements indexed by both a[i] and a[j].

i.as_subindex(j) may raise ValueError in the case that the indices i and j do not intersect at all.

Examples

An example usage of as_subindex is to split an index up into subindices of chunks of an array. For example, say a 1-D array a is chunked up into chunks of size N, so that a[0:N], a[N:2*N], [2*N:3*N], etc. are stored separately. Then an index a[i] can be reindexed onto the chunks via i.as_subindex(Slice(0, N)), i.as_subindex(Slice(N, 2*N)), etc.

>>> from ndindex import Slice
>>> i = Slice(5, 15)
>>> j1 = Slice(0, 10)
>>> j2 = Slice(10, 20)
>>> a = list(range(20))
>>> a[i.raw]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> a[j1.raw]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[j2.raw]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> k1 = i.as_subindex(j1)
>>> k1
Slice(5, 10, 1)
>>> k2 = i.as_subindex(j2)
>>> k2
Slice(0, 5, 1)
>>> a[j1.raw][k1.raw]
[5, 6, 7, 8, 9]
>>> a[j2.raw][k2.raw]
[10, 11, 12, 13, 14]

See also

ndindex.ChunkSize.as_subchunks

a high-level iterator that efficiently gives only those chunks that intersect with a given index

ndindex.ChunkSize.num_subchunks

isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
isvalid(shape, _axis=0)[source]

Check whether a given index is valid on an array of a given shape.

Returns True if an array of shape shape can be indexed by self and False if it would raise IndexError.

>>> from ndindex import ndindex
>>> ndindex(3).isvalid((4,))
True
>>> ndindex(3).isvalid((2,))
False

Note that some indices can never be valid and will raise a IndexError or TypeError if you attempt to construct them.

>>> ndindex((..., 0, ...))
Traceback (most recent call last):
  ...
IndexError: an index can only have a single ellipsis ('...')
>>> ndindex(slice(True))
Traceback (most recent call last):
  ...
TypeError: 'bool' object cannot be interpreted as an integer

See also

NDIndex.newshape

newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, axis=0, negative_int=False, axiserror=False)[source]

Reduce an Integer index on an array of shape shape.

The result will either be IndexError if the index is invalid for the given shape, or an Integer index where the value is nonnegative.

If negative_int is True and a shape is provided, then the result will be an Integer index where the value is negative.

>>> from ndindex import Integer
>>> idx = Integer(-5)
>>> idx.reduce((3,))
Traceback (most recent call last):
...
IndexError: index -5 is out of bounds for axis 0 with size 3
>>> idx.reduce((9,))
Integer(4)
selected_indices(shape, axis=None)[source]

Return an iterator over all indices that are selected by self on an array of shape shape.

The result is a set of indices i such that [a[i] for i in idx.selected_indices(a.shape)] is all the elements of a[idx]. The indices are all iterated over in C (i.e., row major) order.

>>> from ndindex import Slice, Tuple
>>> idx = Slice(5, 10)
>>> list(idx.selected_indices(20))
[Integer(5), Integer(6), Integer(7), Integer(8), Integer(9)]
>>> idx = Tuple(Slice(5, 10), Slice(0, 2))
>>> list(idx.selected_indices((20, 3)))
[Tuple(5, 0), Tuple(5, 1),
 Tuple(6, 0), Tuple(6, 1),
 Tuple(7, 0), Tuple(7, 1),
 Tuple(8, 0), Tuple(8, 1),
 Tuple(9, 0), Tuple(9, 1)]

To correspond these indices to the elements of a[idx], you can use iter_indices(idx.newshape(shape)), since both iterators iterate the indices in C order.

>>> from ndindex import iter_indices
>>> idx = Tuple(Slice(3, 5), Slice(0, 2))
>>> shape = (5, 5)
>>> import numpy as np
>>> a = np.arange(25).reshape(shape)
>>> for a_idx, (new_idx,) in zip(
...    idx.selected_indices(shape),
...    iter_indices(idx.newshape(shape))):
...        print(a_idx, new_idx, a[a_idx.raw], a[idx.raw][new_idx.raw])
Tuple(3, 0) Tuple(0, 0) 15 15
Tuple(3, 1) Tuple(0, 1) 16 16
Tuple(4, 0) Tuple(1, 0) 20 20
Tuple(4, 1) Tuple(1, 1) 21 21

See also

ndindex.iter_indices

An iterator of indices to select every element for arrays of a given shape.

ndindex.ChunkSize.as_subchunks

A high-level iterator that efficiently gives only those chunks that intersect with a given index

class ndindex.Slice(start, stop=<class 'ndindex.slice.default'>, step=None)[source]

Represents a slice on an axis of an nd-array.

Slice(x) with one argument is equivalent to Slice(None, x). Slice(x, y) with two arguments is equivalent to Slice(x, y, None).

start and stop can be any integer, or None. step can be any nonzero integer or None.

Slice(a, b) is the same as the syntax a:b in an index and Slice(a, b, c) is the same as a:b:c. An argument being None is equivalent to the syntax where the item is omitted, for example, Slice(None, None, k) is the same as the syntax ::k.

Slice.args always has three arguments, and does not make any distinction between, for instance, Slice(x, y) and Slice(x, y, None). This is because Python itself does not make the distinction between x:y and x:y: syntactically.

See Slices for a description of the semantic meaning of slices on arrays.

Slice has attributes start, stop, and step to access the corresponding attributes.

>>> from ndindex import Slice
>>> s = Slice(10)
>>> s
Slice(None, 10, None)
>>> print(s.start)
None
>>> s.args
(None, 10, None)
>>> s.raw
slice(None, 10, None)

For most use cases, it’s more convenient to create Slice objects using ndindex[slice], which allows using a:b slicing syntax:

>>> from ndindex import ndindex
>>> ndindex[0:10]
Slice(0, 10, None)
__eq__(other)[source]

Return self==value.

__hash__()[source]

Return hash(self).

__len__()[source]

len() gives the maximum size of an axis sliced with self.

An actual array may produce a smaller size if it is smaller than the bounds of the slice. For instance, [0, 1, 2][2:4] only has 1 element but the maximum length of the slice 2:4 is 2.

>>> from ndindex import Slice
>>> [0, 1, 2][2:4]
[2]
>>> len(Slice(2, 4))
2
>>> [0, 1, 2, 3][2:4]
[2, 3]

If there is no such maximum, it raises ValueError.

>>> # From the second element to the end, which could have any size
>>> len(Slice(1, None))
Traceback (most recent call last):
...
ValueError: Cannot determine max length of slice

The Slice.reduce() method with a shape argument returns a Slice that always has a correct len which doesn’t raise ValueError.

>>> Slice(2, 4).reduce(3)
Slice(2, 3, 1)
>>> len(_)
1

Be aware that len(Slice) only gives the size of the axis being sliced. It does not say anything about the total shape of the array. In particular, the array may be empty after slicing if one of its dimensions is 0, but the other dimensions may be nonzero. To check if an array will empty after indexing, use isempty().

See also

isempty

as_subindex(index)[source]

i.as_subindex(j) produces an index k such that a[j][k] gives all of the elements of a[j] that are also in a[i].

If a[j] is a subset of a[i], then a[j][k] == a[i]. Otherwise, a[j][k] == a[i & j], where i & j is the intersection of i and j, that is, the elements of a that are indexed by both i and j.

For example, in the below diagram, i and j index a subset of the array a. k = i.as_subindex(j) is an index on a[j] that gives the subset of a[j] also included in a[i]:

    +------------ self ------------+
    |                              |
------------------- a -----------------------
       |                                 |
       +------------- index -------------+
       |                           |
       +- self.as_subindex(index) -+

i.as_subindex(j) is currently only implemented when j is a slice with positive steps and nonnegative start and stop, or a Tuple of the same. To use it with slices with negative start or stop, call reduce() with a shape first.

as_subindex can be seen as the left-inverse of composition, that is, if a[i] = a[j][k], then k = i.as_subindex(j), so that k "=" (j^-1)[i] (this only works as a true inverse if j is a subset of i).

Note that due to symmetry, a[j][i.as_subindex(j)] and a[i][j.as_subindex(i)] will give the same subarrays of a, which will be the array of elements indexed by both a[i] and a[j].

i.as_subindex(j) may raise ValueError in the case that the indices i and j do not intersect at all.

Examples

An example usage of as_subindex is to split an index up into subindices of chunks of an array. For example, say a 1-D array a is chunked up into chunks of size N, so that a[0:N], a[N:2*N], [2*N:3*N], etc. are stored separately. Then an index a[i] can be reindexed onto the chunks via i.as_subindex(Slice(0, N)), i.as_subindex(Slice(N, 2*N)), etc.

>>> from ndindex import Slice
>>> i = Slice(5, 15)
>>> j1 = Slice(0, 10)
>>> j2 = Slice(10, 20)
>>> a = list(range(20))
>>> a[i.raw]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> a[j1.raw]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[j2.raw]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> k1 = i.as_subindex(j1)
>>> k1
Slice(5, 10, 1)
>>> k2 = i.as_subindex(j2)
>>> k2
Slice(0, 5, 1)
>>> a[j1.raw][k1.raw]
[5, 6, 7, 8, 9]
>>> a[j2.raw][k2.raw]
[10, 11, 12, 13, 14]

See also

ndindex.ChunkSize.as_subchunks

a high-level iterator that efficiently gives only those chunks that intersect with a given index

ndindex.ChunkSize.num_subchunks

isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
isvalid(shape)[source]

Check whether a given index is valid on an array of a given shape.

Returns True if an array of shape shape can be indexed by self and False if it would raise IndexError.

>>> from ndindex import ndindex
>>> ndindex(3).isvalid((4,))
True
>>> ndindex(3).isvalid((2,))
False

Note that some indices can never be valid and will raise a IndexError or TypeError if you attempt to construct them.

>>> ndindex((..., 0, ...))
Traceback (most recent call last):
  ...
IndexError: an index can only have a single ellipsis ('...')
>>> ndindex(slice(True))
Traceback (most recent call last):
  ...
TypeError: 'bool' object cannot be interpreted as an integer

See also

NDIndex.newshape

newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, axis=0, negative_int=False)[source]

Slice.reduce returns a slice that is canonicalized for an array of the given shape, or for any shape if shape is None (the default).

Slice.reduce is a perfect canonicalization, meaning that two slices are equal—for all array shapes if shape=None or for arrays of shape shape otherwise—if and only if they reduce to the same Slice object. Note that ndindex objects do not simplify automatically, and == only does exact equality comparison, so to test that two slices are equal, use slice1.reduce(shape) == slice2.reduce(shape).

  • If shape is None, the following properties hold after calling reduce():

    • start is not None.

    • stop is not None, when possible. The reduced stop can only be None if the original stop is.

    • step is not None.

    • step is as close to 0 as possible.

    • If the slice is always empty, the resulting slice will be Slice(0, 0, 1). However, one should prefer the isempty method to test if a slice is always empty.

    In particular, stop may be None, even after canonicalization with reduce() with no shape. This is because some slices are impossible to represent without None without making assumptions about the array shape. For example, Slice(0, None) cannot be equivalent to a slice with stop != None for all array shapes. To get a slice where the start, stop, and step are always integers, use reduce(shape) with an explicit array shape.

    Note that Slice objects that index a single element are not canonicalized to Integer, because integer indices always remove an axis whereas slices keep the axis. Furthermore, slices cannot raise IndexError except on arrays with shape equal to ().

    >>> from ndindex import Slice
    >>> Slice(10).reduce()
    Slice(0, 10, 1)
    >>> Slice(1, 3, 3).reduce()
    Slice(1, 2, 1)
    
  • If an explicit shape is given, the following properties are true after calling Slice.reduce(shape):

    • start, stop, and step are not None,

    • start is nonnegative.

    • stop is nonnegative whenever possible. In particular, stop is only negative when it has to be to represent the given slice, i.e., a slice with negative step that indexes more than 1 element and indexes the first (index 0) element (in this case, it will be -n - 1 where n is the size of the axis being sliced).

    • stop is as small as possible for positive step or large as possible for negative step.

    • step is as close to 0 as possible.

    • If the slice is empty for the given shape, the resulting slice will be Slice(0, 0, 1). However, one should prefer the isempty method to test if a slice is always empty.

    • If the slice indexes a single element, the resulting slice will be of the form Slice(i, i+1, 1). However, one should prefer using len(s.reduce(shape)) == 1 to test if a slice indexes exactly 1 element.

    • len() gives the true size of the axis for a sliced array of the given shape, and never raises ValueError.

    The axis argument can be used to specify an axis of the shape (by default, axis=0). For convenience, shape can be passed as an integer for a single dimension.

    >>> from ndindex import Slice
    >>> Slice(1, 10).reduce(3)
    Slice(1, 3, 1)
    >>> Slice(-1, 1, -2).reduce(4)
    Slice(3, 4, 1)
    >>> Slice(1, 10, 3).reduce((4, 5), axis=0)
    Slice(1, 2, 1)
    >>> Slice(1, 10, 3).reduce((4, 5), axis=1)
    Slice(1, 5, 3)
    
    >>> s = Slice(2, None)
    >>> len(s)
    Traceback (most recent call last):
    ...
    ValueError: Cannot determine max length of slice
    >>> s.reduce((5,))
    Slice(2, 5, 1)
    >>> len(_)
    3
    
selected_indices(shape, axis=None)[source]

Return an iterator over all indices that are selected by self on an array of shape shape.

The result is a set of indices i such that [a[i] for i in idx.selected_indices(a.shape)] is all the elements of a[idx]. The indices are all iterated over in C (i.e., row major) order.

>>> from ndindex import Slice, Tuple
>>> idx = Slice(5, 10)
>>> list(idx.selected_indices(20))
[Integer(5), Integer(6), Integer(7), Integer(8), Integer(9)]
>>> idx = Tuple(Slice(5, 10), Slice(0, 2))
>>> list(idx.selected_indices((20, 3)))
[Tuple(5, 0), Tuple(5, 1),
 Tuple(6, 0), Tuple(6, 1),
 Tuple(7, 0), Tuple(7, 1),
 Tuple(8, 0), Tuple(8, 1),
 Tuple(9, 0), Tuple(9, 1)]

To correspond these indices to the elements of a[idx], you can use iter_indices(idx.newshape(shape)), since both iterators iterate the indices in C order.

>>> from ndindex import iter_indices
>>> idx = Tuple(Slice(3, 5), Slice(0, 2))
>>> shape = (5, 5)
>>> import numpy as np
>>> a = np.arange(25).reshape(shape)
>>> for a_idx, (new_idx,) in zip(
...    idx.selected_indices(shape),
...    iter_indices(idx.newshape(shape))):
...        print(a_idx, new_idx, a[a_idx.raw], a[idx.raw][new_idx.raw])
Tuple(3, 0) Tuple(0, 0) 15 15
Tuple(3, 1) Tuple(0, 1) 16 16
Tuple(4, 0) Tuple(1, 0) 20 20
Tuple(4, 1) Tuple(1, 1) 21 21

See also

ndindex.iter_indices

An iterator of indices to select every element for arrays of a given shape.

ndindex.ChunkSize.as_subchunks

A high-level iterator that efficiently gives only those chunks that intersect with a given index

property start

The start value of the slice.

Note that this may be an integer or None.

property step

The step of the slice.

Note that this may be a nonzero integer or None.

property stop

The stop of the slice.

Note that this may be an integer or None.

class ndindex.ellipsis[source]

Represents an ellipsis index, i.e., ... (or Ellipsis).

Ellipsis indices by themselves return the full array. Inside of a tuple index, an ellipsis skips 0 or more axes of the array so that everything after the ellipsis indexes the last axes of the array. A tuple index can have at most one ellipsis.

For example a[(0, ..., -2)] would index the first element on the first axis, the second-to-last element in the last axis, and include all the axes in between.

>>> from numpy import arange
>>> a = arange(2*3*4).reshape((2, 3, 4))
>>> a
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]]])
>>> a[0, ..., -2]
array([ 2,  6, 10])

An ellipsis can go at the beginning of end of a tuple index, and is allowed to match 0 axes.

Note

Unlike the standard Python Ellipsis, ellipsis is the type, not the object (the name is lowercase to avoid conflicting with the built-in). Use ellipsis() or ndindex(...) to create the object. In most ndindex contexts, ... can be used instead of ellipsis(), for instance, when creating a Tuple object. Also unlike Ellipsis, ellipsis() is not singletonized, so you should not use is to compare it. See the document on type confusion for more details.

as_subindex(index)[source]

i.as_subindex(j) produces an index k such that a[j][k] gives all of the elements of a[j] that are also in a[i].

If a[j] is a subset of a[i], then a[j][k] == a[i]. Otherwise, a[j][k] == a[i & j], where i & j is the intersection of i and j, that is, the elements of a that are indexed by both i and j.

For example, in the below diagram, i and j index a subset of the array a. k = i.as_subindex(j) is an index on a[j] that gives the subset of a[j] also included in a[i]:

    +------------ self ------------+
    |                              |
------------------- a -----------------------
       |                                 |
       +------------- index -------------+
       |                           |
       +- self.as_subindex(index) -+

i.as_subindex(j) is currently only implemented when j is a slice with positive steps and nonnegative start and stop, or a Tuple of the same. To use it with slices with negative start or stop, call reduce() with a shape first.

as_subindex can be seen as the left-inverse of composition, that is, if a[i] = a[j][k], then k = i.as_subindex(j), so that k "=" (j^-1)[i] (this only works as a true inverse if j is a subset of i).

Note that due to symmetry, a[j][i.as_subindex(j)] and a[i][j.as_subindex(i)] will give the same subarrays of a, which will be the array of elements indexed by both a[i] and a[j].

i.as_subindex(j) may raise ValueError in the case that the indices i and j do not intersect at all.

Examples

An example usage of as_subindex is to split an index up into subindices of chunks of an array. For example, say a 1-D array a is chunked up into chunks of size N, so that a[0:N], a[N:2*N], [2*N:3*N], etc. are stored separately. Then an index a[i] can be reindexed onto the chunks via i.as_subindex(Slice(0, N)), i.as_subindex(Slice(N, 2*N)), etc.

>>> from ndindex import Slice
>>> i = Slice(5, 15)
>>> j1 = Slice(0, 10)
>>> j2 = Slice(10, 20)
>>> a = list(range(20))
>>> a[i.raw]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> a[j1.raw]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[j2.raw]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> k1 = i.as_subindex(j1)
>>> k1
Slice(5, 10, 1)
>>> k2 = i.as_subindex(j2)
>>> k2
Slice(0, 5, 1)
>>> a[j1.raw][k1.raw]
[5, 6, 7, 8, 9]
>>> a[j2.raw][k2.raw]
[10, 11, 12, 13, 14]

See also

ndindex.ChunkSize.as_subchunks

a high-level iterator that efficiently gives only those chunks that intersect with a given index

ndindex.ChunkSize.num_subchunks

isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
isvalid(shape)[source]

Check whether a given index is valid on an array of a given shape.

Returns True if an array of shape shape can be indexed by self and False if it would raise IndexError.

>>> from ndindex import ndindex
>>> ndindex(3).isvalid((4,))
True
>>> ndindex(3).isvalid((2,))
False

Note that some indices can never be valid and will raise a IndexError or TypeError if you attempt to construct them.

>>> ndindex((..., 0, ...))
Traceback (most recent call last):
  ...
IndexError: an index can only have a single ellipsis ('...')
>>> ndindex(slice(True))
Traceback (most recent call last):
  ...
TypeError: 'bool' object cannot be interpreted as an integer

See also

NDIndex.newshape

newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, negative_int=False)[source]

Reduce an ellipsis index

Since an ellipsis by itself always returns the full array unchanged, ellipsis().reduce() returns Tuple() as a canonical form (the index () also always returns an array unchanged).

>>> from ndindex import ellipsis
>>> ellipsis().reduce()
Tuple()
class ndindex.Newaxis[source]

Represents a np.newaxis (i.e., None) index.

Newaxis adds a shape 1 dimension to the array. If a Newaxis is inside of a tuple index, it adds a shape 1 dimension at that location in the index.

For example, if a has shape (2, 3), then a[newaxis] has shape (1, 2, 3), a[:, newaxis] has shape (2, 1, 3), and so on.

>>> from ndindex import Newaxis
>>> from numpy import arange
>>> a = arange(0,6).reshape(2,3)
>>> a[Newaxis().raw].shape
(1, 2, 3)
>>> a[:, Newaxis().raw, :].shape
(2, 1, 3)

Using Newaxis().raw as an index is equivalent to using numpy.newaxis.

Note

Unlike the NumPy newaxis, Newaxis is the type, not the object (the name is uppercase to avoid conflicting with the NumPy type). Use Newaxis(), ndindex(np.newaxis), or ndindex(None) to create the object. In most ndindex contexts, np.newaxis or None can be used instead of Newaxis(), for instance, when creating a Tuple object. Also unlike None, Newaxis() is not singletonized, so you should not use is to compare it. See the document on Type Confusion for more details.

isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
isvalid(shape)[source]

Check whether a given index is valid on an array of a given shape.

Returns True if an array of shape shape can be indexed by self and False if it would raise IndexError.

>>> from ndindex import ndindex
>>> ndindex(3).isvalid((4,))
True
>>> ndindex(3).isvalid((2,))
False

Note that some indices can never be valid and will raise a IndexError or TypeError if you attempt to construct them.

>>> ndindex((..., 0, ...))
Traceback (most recent call last):
  ...
IndexError: an index can only have a single ellipsis ('...')
>>> ndindex(slice(True))
Traceback (most recent call last):
  ...
TypeError: 'bool' object cannot be interpreted as an integer

See also

NDIndex.newshape

newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, axis=0, negative_int=False)[source]

Reduce a Newaxis index

There is no other index that is equivalent to a newaxis index by itself, so Newaxis().reduce() always returns Newaxis() unchanged.

>>> from ndindex import Newaxis
>>> Newaxis().reduce()
Newaxis()
class ndindex.Tuple(*args)[source]

Represents a tuple of single-axis indices.

Valid single axis indices are

(some of the above are not yet implemented)

Tuple(x1, x2, …, xn) represents the index a[x1, x2, …, xn] or, equivalently, a[(x1, x2, …, xn)]. Tuple() with no arguments is the empty tuple index, a[()], which returns a unchanged.

>>> from ndindex import Tuple, Slice
>>> import numpy as np
>>> idx = Tuple(0, Slice(2, 4))
>>> a = np.arange(10).reshape((2, 5))
>>> a
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
>>> a[0, 2:4]
array([2, 3])
>>> a[idx.raw]
array([2, 3])

Note

Tuple does not represent a tuple, but rather an tuple index. It does not have most methods that tuple has, and should not be used in non-indexing contexts. See the document on Type Confusion for more details.

as_subindex(index)[source]

i.as_subindex(j) produces an index k such that a[j][k] gives all of the elements of a[j] that are also in a[i].

If a[j] is a subset of a[i], then a[j][k] == a[i]. Otherwise, a[j][k] == a[i & j], where i & j is the intersection of i and j, that is, the elements of a that are indexed by both i and j.

For example, in the below diagram, i and j index a subset of the array a. k = i.as_subindex(j) is an index on a[j] that gives the subset of a[j] also included in a[i]:

    +------------ self ------------+
    |                              |
------------------- a -----------------------
       |                                 |
       +------------- index -------------+
       |                           |
       +- self.as_subindex(index) -+

i.as_subindex(j) is currently only implemented when j is a slice with positive steps and nonnegative start and stop, or a Tuple of the same. To use it with slices with negative start or stop, call reduce() with a shape first.

as_subindex can be seen as the left-inverse of composition, that is, if a[i] = a[j][k], then k = i.as_subindex(j), so that k "=" (j^-1)[i] (this only works as a true inverse if j is a subset of i).

Note that due to symmetry, a[j][i.as_subindex(j)] and a[i][j.as_subindex(i)] will give the same subarrays of a, which will be the array of elements indexed by both a[i] and a[j].

i.as_subindex(j) may raise ValueError in the case that the indices i and j do not intersect at all.

Examples

An example usage of as_subindex is to split an index up into subindices of chunks of an array. For example, say a 1-D array a is chunked up into chunks of size N, so that a[0:N], a[N:2*N], [2*N:3*N], etc. are stored separately. Then an index a[i] can be reindexed onto the chunks via i.as_subindex(Slice(0, N)), i.as_subindex(Slice(N, 2*N)), etc.

>>> from ndindex import Slice
>>> i = Slice(5, 15)
>>> j1 = Slice(0, 10)
>>> j2 = Slice(10, 20)
>>> a = list(range(20))
>>> a[i.raw]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> a[j1.raw]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[j2.raw]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> k1 = i.as_subindex(j1)
>>> k1
Slice(5, 10, 1)
>>> k2 = i.as_subindex(j2)
>>> k2
Slice(0, 5, 1)
>>> a[j1.raw][k1.raw]
[5, 6, 7, 8, 9]
>>> a[j2.raw][k2.raw]
[10, 11, 12, 13, 14]

See also

ndindex.ChunkSize.as_subchunks

a high-level iterator that efficiently gives only those chunks that intersect with a given index

ndindex.ChunkSize.num_subchunks

broadcast_arrays()[source]

Broadcast all the array indices in self to a common shape and convert boolean array indices into integer array indices.

The resulting index is equivalent in all contexts where the original index is allowed. However, it is possible for the original index to give an IndexError but for the new index to not, since integer array indices have less stringent shape requirements than boolean array indices. There are also some instances for empty indices (isempty is True) where bounds would be checked before broadcasting but not after.

Any BooleanArray indices are converted to IntegerArray indices. Furthermore, if there are BooleanArray or IntegerArray indices, then any Integer indices are also converted into scalar IntegerArray indices and broadcast. Furthermore, if there are multiple boolean scalar indices (True or False), they are combined into a single one.

Note that array broadcastability is checked in the Tuple constructor, so this method will not raise any exceptions.

This is part of what is performed by expand, but unlike expand, this method does not do any other manipulations, and it does not require a shape.

>>> from ndindex import Tuple
>>> idx = Tuple([[False], [True], [True]], [[4], [5], [5]], -1)
>>> print(idx.broadcast_arrays())
Tuple(IntegerArray([[1 2] [1 2] [1 2]]),
      IntegerArray([[0 0] [0 0] [0 0]]),
      IntegerArray([[4 4] [5 5] [5 5]]),
      IntegerArray([[-1 -1] [-1 -1] [-1 -1]]))

See also

expand

property ellipsis_index

Give the index i of self.args where the ellipsis is.

If self doesn’t have an ellipsis, it gives len(self.args), since tuple indices without an ellipsis always implicitly end in an ellipsis.

The resulting value i is such that self.args[:i] indexes the beginning axes of an array and self.args[i+1:] indexes the end axes of an array.

>>> from ndindex import Tuple
>>> idx = Tuple(0, 1, ..., 2, 3)
>>> i = idx.ellipsis_index
>>> i
2
>>> idx.args[:i]
(Integer(0), Integer(1))
>>> idx.args[i+1:]
(Integer(2), Integer(3))
>>> Tuple(0, 1).ellipsis_index
2
expand(shape)[source]

Expand a Tuple index on an array of shape shape

An expanded index is as explicit as possible. Unlike reduce, which tries to simplify an index and remove redundancies, expand() typically makes an index larger.

If self is invalid for the given shape, an IndexError is raised. Otherwise, the returned index satisfies the following:

  • It is always a Tuple.

  • All the elements of the Tuple are recursively reduced.

  • The length of the .args is equal to the length of the shape plus the number of Newaxis indices in self plus 1 if there is a scalar BooleanArray (True or False).

  • The resulting Tuple has no ellipses. If there are axes that would be matched by an ellipsis or an implicit ellipsis at the end of the tuple, Slice(0, n, 1) indices are inserted, where n is the corresponding axis of the shape.

  • Any array indices in self are broadcast together. If self contains array indices (IntegerArray or BooleanArray), then any Integer indices are converted into IntegerArray indices of shape () and broadcast. Note that broadcasting is done in a memory efficient way so that even if the broadcasted shape is large it will not take up more memory than the original.

  • Scalar BooleanArray arguments (True or False) are combined into a single term (the same as with Tuple.reduce()).

  • Non-scalar BooleanArrays are all converted into equivalent IntegerArrays via nonzero() and broadcasted.

>>> from ndindex import Tuple, Slice
>>> Slice(None).expand((2, 3))
Tuple(slice(0, 2, 1), slice(0, 3, 1))
>>> idx = Tuple(slice(0, 10), ..., None, -3)
>>> idx.expand((5, 3))
Tuple(slice(0, 5, 1), None, 0)
>>> idx.expand((1, 2, 3))
Tuple(slice(0, 1, 1), slice(0, 2, 1), None, 0)
>>> idx.expand((5,))
Traceback (most recent call last):
...
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
>>> idx.expand((5, 2))
Traceback (most recent call last):
...
IndexError: index -3 is out of bounds for axis 1 with size 2
>>> idx = Tuple(..., [0, 1], -1)
>>> idx.expand((1, 2, 3))
Tuple(slice(0, 1, 1), [0, 1], [2, 2])
property has_ellipsis

Returns True if self has an ellipsis

isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, negative_int=False)[source]

Reduce a Tuple index on an array of shape shape

A Tuple with a single argument is always reduced to that single argument (because a[idx,] is the same as a[idx]).

>>> from ndindex import Tuple
>>> Tuple(slice(2, 4)).reduce()
Slice(2, 4, 1)

If an explicit array shape is given, the result will either be IndexError if the index is invalid for the given shape, or an index that is as simple as possible:

  • All the elements of the Tuple are recursively reduced.

  • Any axes that can be merged into an ellipsis are removed. This includes the implicit ellipsis at the end of a Tuple that doesn’t contain any explicit ellipses.

  • Ellipses that don’t match any axes are removed.

  • An ellipsis at the end of the Tuple is removed.

  • Scalar BooleanArray arguments (True or False) are combined into a single term (the first boolean scalar is replaced with the AND of all the boolean scalars).

  • If the resulting Tuple would have a single argument, that argument is returned.

>>> idx = Tuple(0, ..., slice(0, 3))
>>> idx.reduce((5, 4))
Tuple(0, slice(0, 3, 1))
>>> idx.reduce((5, 3))
Integer(0)
>>> idx = Tuple(slice(0, 10), -3)
>>> idx.reduce((5,))
Traceback (most recent call last):
...
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
>>> idx.reduce((5, 2))
Traceback (most recent call last):
...
IndexError: index -3 is out of bounds for axis 1 with size 2

Note

ndindex presently does not distinguish between scalar objects and 0-D arrays. It is possible for the original index to produce one and the reduced index to produce the other. In particular, the presence of a redundant ellipsis forces NumPy to return a 0-D array instead of a scalar.

>>> import numpy as np
>>> a = np.array([0, 1])
>>> Tuple(..., 1).reduce(a.shape)
Integer(1)
>>> a[..., 1]
array(1)
>>> a[1] 
np.int64(1)

See https://github.com/Quansight-Labs/ndindex/issues/22.

selected_indices(shape)[source]

Return an iterator over all indices that are selected by self on an array of shape shape.

The result is a set of indices i such that [a[i] for i in idx.selected_indices(a.shape)] is all the elements of a[idx]. The indices are all iterated over in C (i.e., row major) order.

>>> from ndindex import Slice, Tuple
>>> idx = Slice(5, 10)
>>> list(idx.selected_indices(20))
[Integer(5), Integer(6), Integer(7), Integer(8), Integer(9)]
>>> idx = Tuple(Slice(5, 10), Slice(0, 2))
>>> list(idx.selected_indices((20, 3)))
[Tuple(5, 0), Tuple(5, 1),
 Tuple(6, 0), Tuple(6, 1),
 Tuple(7, 0), Tuple(7, 1),
 Tuple(8, 0), Tuple(8, 1),
 Tuple(9, 0), Tuple(9, 1)]

To correspond these indices to the elements of a[idx], you can use iter_indices(idx.newshape(shape)), since both iterators iterate the indices in C order.

>>> from ndindex import iter_indices
>>> idx = Tuple(Slice(3, 5), Slice(0, 2))
>>> shape = (5, 5)
>>> import numpy as np
>>> a = np.arange(25).reshape(shape)
>>> for a_idx, (new_idx,) in zip(
...    idx.selected_indices(shape),
...    iter_indices(idx.newshape(shape))):
...        print(a_idx, new_idx, a[a_idx.raw], a[idx.raw][new_idx.raw])
Tuple(3, 0) Tuple(0, 0) 15 15
Tuple(3, 1) Tuple(0, 1) 16 16
Tuple(4, 0) Tuple(1, 0) 20 20
Tuple(4, 1) Tuple(1, 1) 21 21

See also

ndindex.iter_indices

An iterator of indices to select every element for arrays of a given shape.

ndindex.ChunkSize.as_subchunks

A high-level iterator that efficiently gives only those chunks that intersect with a given index

class ndindex.IntegerArray(idx, shape=None, _copy=True)[source]

Represents an integer array index.

If idx is an n-dimensional integer array with shape s = (s1, ..., sn) and a is any array, a[idx] replaces the first dimension of a with dimensions of size s1, ..., sn, where each entry is indexed according to the entry in idx as an integer index.

Integer arrays can also appear as part of tuple indices. In that case, they replace the axis being indexed. If more than one integer array appears inside of a tuple index, they are broadcast together and iterated as one. Furthermore, if an integer array appears in a tuple index, all integer indices in the tuple are treated as scalar integer arrays and are also broadcast. In general, an Integer index semantically behaves the same as a scalar (shape=()) IntegerArray.

A list of integers may also be used in place of an integer array. Note that NumPy treats a direct list of integers as a tuple index, but this behavior is deprecated and will be replaced with integer array indexing in the future. ndindex always treats lists as arrays.

>>> from ndindex import IntegerArray
>>> import numpy as np
>>> idx = IntegerArray([[0, 1], [1, 2]])
>>> a = np.arange(10)
>>> a[idx.raw]
array([[0, 1],
       [1, 2]])

Note

IntegerArray does not represent an array, but rather an array index. It does not have most methods that numpy.ndarray has, and should not be used in array contexts. See the document on Type Confusion for more details.

dtype

The dtype of IntegerArray is np.intp, which is typically either np.int32 or np.int64 depending on the platform.

args

idx.args contains the arguments needed to create idx.

For an ndindex object idx, idx.args is always a tuple such that

type(idx)(*idx.args) == idx

For Tuple indices, the elements of .args are themselves ndindex types. For other types, .args contains raw Python types. Note that .args contains NumPy arrays for IntegerArray and BooleanArray types, so one should always do equality testing or hashing on the ndindex type itself, not its .args.

property array

Return the NumPy array of self.

This is the same as self.args[0].

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([0, 1]).array
array([0, 1])
>>> BooleanArray([False, True]).array
array([False, True])
as_subindex(index)[source]

i.as_subindex(j) produces an index k such that a[j][k] gives all of the elements of a[j] that are also in a[i].

If a[j] is a subset of a[i], then a[j][k] == a[i]. Otherwise, a[j][k] == a[i & j], where i & j is the intersection of i and j, that is, the elements of a that are indexed by both i and j.

For example, in the below diagram, i and j index a subset of the array a. k = i.as_subindex(j) is an index on a[j] that gives the subset of a[j] also included in a[i]:

    +------------ self ------------+
    |                              |
------------------- a -----------------------
       |                                 |
       +------------- index -------------+
       |                           |
       +- self.as_subindex(index) -+

i.as_subindex(j) is currently only implemented when j is a slice with positive steps and nonnegative start and stop, or a Tuple of the same. To use it with slices with negative start or stop, call reduce() with a shape first.

as_subindex can be seen as the left-inverse of composition, that is, if a[i] = a[j][k], then k = i.as_subindex(j), so that k "=" (j^-1)[i] (this only works as a true inverse if j is a subset of i).

Note that due to symmetry, a[j][i.as_subindex(j)] and a[i][j.as_subindex(i)] will give the same subarrays of a, which will be the array of elements indexed by both a[i] and a[j].

i.as_subindex(j) may raise ValueError in the case that the indices i and j do not intersect at all.

Examples

An example usage of as_subindex is to split an index up into subindices of chunks of an array. For example, say a 1-D array a is chunked up into chunks of size N, so that a[0:N], a[N:2*N], [2*N:3*N], etc. are stored separately. Then an index a[i] can be reindexed onto the chunks via i.as_subindex(Slice(0, N)), i.as_subindex(Slice(N, 2*N)), etc.

>>> from ndindex import Slice
>>> i = Slice(5, 15)
>>> j1 = Slice(0, 10)
>>> j2 = Slice(10, 20)
>>> a = list(range(20))
>>> a[i.raw]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> a[j1.raw]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[j2.raw]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> k1 = i.as_subindex(j1)
>>> k1
Slice(5, 10, 1)
>>> k2 = i.as_subindex(j2)
>>> k2
Slice(0, 5, 1)
>>> a[j1.raw][k1.raw]
[5, 6, 7, 8, 9]
>>> a[j2.raw][k2.raw]
[10, 11, 12, 13, 14]

See also

ndindex.ChunkSize.as_subchunks

a high-level iterator that efficiently gives only those chunks that intersect with a given index

ndindex.ChunkSize.num_subchunks

broadcast_arrays()[source]

Broadcast all the array indices in self to a common shape and convert boolean array indices into integer array indices.

The resulting index is equivalent in all contexts where the original index is allowed. However, it is possible for the original index to give an IndexError but for the new index to not, since integer array indices have less stringent shape requirements than boolean array indices. There are also some instances for empty indices (isempty is True) where bounds would be checked before broadcasting but not after.

Any BooleanArray indices are converted to IntegerArray indices. Furthermore, if there are BooleanArray or IntegerArray indices, then any Integer indices are also converted into scalar IntegerArray indices and broadcast. Furthermore, if there are multiple boolean scalar indices (True or False), they are combined into a single one.

Note that array broadcastability is checked in the Tuple constructor, so this method will not raise any exceptions.

This is part of what is performed by expand, but unlike expand, this method does not do any other manipulations, and it does not require a shape.

>>> from ndindex import Tuple
>>> idx = Tuple([[False], [True], [True]], [[4], [5], [5]], -1)
>>> print(idx.broadcast_arrays())
Tuple(IntegerArray([[1 2] [1 2] [1 2]]),
      IntegerArray([[0 0] [0 0] [0 0]]),
      IntegerArray([[4 4] [5 5] [5 5]]),
      IntegerArray([[-1 -1] [-1 -1] [-1 -1]]))

See also

expand

expand(shape)[source]

Expand a Tuple index on an array of shape shape

An expanded index is as explicit as possible. Unlike reduce, which tries to simplify an index and remove redundancies, expand() typically makes an index larger.

If self is invalid for the given shape, an IndexError is raised. Otherwise, the returned index satisfies the following:

  • It is always a Tuple.

  • All the elements of the Tuple are recursively reduced.

  • The length of the .args is equal to the length of the shape plus the number of Newaxis indices in self plus 1 if there is a scalar BooleanArray (True or False).

  • The resulting Tuple has no ellipses. If there are axes that would be matched by an ellipsis or an implicit ellipsis at the end of the tuple, Slice(0, n, 1) indices are inserted, where n is the corresponding axis of the shape.

  • Any array indices in self are broadcast together. If self contains array indices (IntegerArray or BooleanArray), then any Integer indices are converted into IntegerArray indices of shape () and broadcast. Note that broadcasting is done in a memory efficient way so that even if the broadcasted shape is large it will not take up more memory than the original.

  • Scalar BooleanArray arguments (True or False) are combined into a single term (the same as with Tuple.reduce()).

  • Non-scalar BooleanArrays are all converted into equivalent IntegerArrays via nonzero() and broadcasted.

>>> from ndindex import Tuple, Slice
>>> Slice(None).expand((2, 3))
Tuple(slice(0, 2, 1), slice(0, 3, 1))
>>> idx = Tuple(slice(0, 10), ..., None, -3)
>>> idx.expand((5, 3))
Tuple(slice(0, 5, 1), None, 0)
>>> idx.expand((1, 2, 3))
Tuple(slice(0, 1, 1), slice(0, 2, 1), None, 0)
>>> idx.expand((5,))
Traceback (most recent call last):
...
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
>>> idx.expand((5, 2))
Traceback (most recent call last):
...
IndexError: index -3 is out of bounds for axis 1 with size 2
>>> idx = Tuple(..., [0, 1], -1)
>>> idx.expand((1, 2, 3))
Tuple(slice(0, 1, 1), [0, 1], [2, 2])
isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
isvalid(shape, _axis=0)[source]

Check whether a given index is valid on an array of a given shape.

Returns True if an array of shape shape can be indexed by self and False if it would raise IndexError.

>>> from ndindex import ndindex
>>> ndindex(3).isvalid((4,))
True
>>> ndindex(3).isvalid((2,))
False

Note that some indices can never be valid and will raise a IndexError or TypeError if you attempt to construct them.

>>> ndindex((..., 0, ...))
Traceback (most recent call last):
  ...
IndexError: an index can only have a single ellipsis ('...')
>>> ndindex(slice(True))
Traceback (most recent call last):
  ...
TypeError: 'bool' object cannot be interpreted as an integer

See also

NDIndex.newshape

property ndim

Return the number of dimensions of the array of self.

This is the same as self.array.ndim. Note that this is not the same as the number of dimensions of an array that is indexed by self. Use len on newshape() to get that.

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([[0], [1]]).ndim
2
>>> BooleanArray([[False], [True]]).ndim
2
newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, axis=0, negative_int=False)[source]

Reduce an IntegerArray index on an array of shape shape.

The result will either be IndexError if the index is invalid for the given shape, an IntegerArray index where the values are all nonnegative, or, if self is a scalar array index (self.shape == ()), an Integer whose value is nonnegative.

If negative_int is True and a shape is provided, the result will be an IntegerArray with negative entries instead of positive entries.

>>> from ndindex import IntegerArray
>>> idx = IntegerArray([-5, 2])
>>> idx.reduce((3,))
Traceback (most recent call last):
...
IndexError: index -5 is out of bounds for axis 0 with size 3
>>> idx.reduce((9,))
IntegerArray([4, 2])
>>> idx.reduce((9,), negative_int=True)
IntegerArray([-5, -7])
selected_indices(shape, axis=None)[source]

Return an iterator over all indices that are selected by self on an array of shape shape.

The result is a set of indices i such that [a[i] for i in idx.selected_indices(a.shape)] is all the elements of a[idx]. The indices are all iterated over in C (i.e., row major) order.

>>> from ndindex import Slice, Tuple
>>> idx = Slice(5, 10)
>>> list(idx.selected_indices(20))
[Integer(5), Integer(6), Integer(7), Integer(8), Integer(9)]
>>> idx = Tuple(Slice(5, 10), Slice(0, 2))
>>> list(idx.selected_indices((20, 3)))
[Tuple(5, 0), Tuple(5, 1),
 Tuple(6, 0), Tuple(6, 1),
 Tuple(7, 0), Tuple(7, 1),
 Tuple(8, 0), Tuple(8, 1),
 Tuple(9, 0), Tuple(9, 1)]

To correspond these indices to the elements of a[idx], you can use iter_indices(idx.newshape(shape)), since both iterators iterate the indices in C order.

>>> from ndindex import iter_indices
>>> idx = Tuple(Slice(3, 5), Slice(0, 2))
>>> shape = (5, 5)
>>> import numpy as np
>>> a = np.arange(25).reshape(shape)
>>> for a_idx, (new_idx,) in zip(
...    idx.selected_indices(shape),
...    iter_indices(idx.newshape(shape))):
...        print(a_idx, new_idx, a[a_idx.raw], a[idx.raw][new_idx.raw])
Tuple(3, 0) Tuple(0, 0) 15 15
Tuple(3, 1) Tuple(0, 1) 16 16
Tuple(4, 0) Tuple(1, 0) 20 20
Tuple(4, 1) Tuple(1, 1) 21 21

See also

ndindex.iter_indices

An iterator of indices to select every element for arrays of a given shape.

ndindex.ChunkSize.as_subchunks

A high-level iterator that efficiently gives only those chunks that intersect with a given index

property shape

Return the shape of the array of self.

This is the same as self.array.shape. Note that this is not the same as the shape of an array that is indexed by self. Use newshape() to get that.

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([[0], [1]]).shape
(2, 1)
>>> BooleanArray([[False], [True]]).shape
(2, 1)
property size

Return the number of elements of the array of self.

This is the same as self.array.size. Note that this is not the same as the number of elements of an array that is indexed by self. Use np.prod on newshape() to get that.

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([[0], [1]]).size
2
>>> BooleanArray([[False], [True]]).size
2
class ndindex.BooleanArray(idx, shape=None, _copy=True)[source]

Represents a boolean array index (also known as a mask).

If idx is an n-dimensional boolean array with shape s = (s1, ..., sn) and a is an array of shape s = (s1, ..., sn, ..., sm), a[idx] replaces the first n dimensions of a with a single dimensions of size np.nonzero(idx), where each entry is included if the corresponding element of idx is True. The axes in the index shape should match the corresponding axes in the array shape or be 0, or the index produces IndexError.

The typical way of creating a mask is to use boolean operations on an array, then index the array with that. For example, if a is an array of integers, a[a > 0] will produces a flat array of the elements of a that are positive.

Some important things to note about boolean array index semantics:

  1. A boolean array index will remove as many dimensions as the index has, and replace them with a single flat dimension which is the size of the number of True elements in the index.

  2. A boolean array index idx works the same as the integer array index np.nonzero(idx). In particular, the elements of the index are always iterated in row-major, C-style order. This does not apply to 0-dimensional boolean indices.

  3. A 0-dimensional boolean index (i.e., just the scalar True or False) can still be thought of as removing 0 dimensions and adding a single dimension of length 1 for True or 0 for False. Hence, if a has shape (s1, ..., sn), then a[True] has shape (1, s1, ..., sn), and a[False] has shape (0, s1, ..., sn).

  4. If a tuple index has multiple boolean arrays, they are broadcast together and iterated as a single array, similar to IntegerArray. If a boolean array index idx is mixed with an integer array index in a tuple index, it is treated like np.nonzero(idx).

A list of booleans may also be used in place of a boolean array. Note that NumPy treats a direct list of integers as a tuple index, but this behavior is deprecated and will be replaced with integer array indexing in the future. ndindex always treats lists as arrays.

>>> from ndindex import BooleanArray
>>> import numpy as np
>>> idx = BooleanArray([[ True,  True],
...                     [ True, False],
...                     [False, False],
...                     [False,  True],
...                     [False, False]])
>>> a = np.arange(10).reshape((5, 2))
>>> a[idx.raw]
array([0, 1, 2, 7])

Note

BooleanArray does not represent an array, but rather an array index. It does not have most methods that numpy.ndarray has, and should not be used in array contexts. See the document on Type Confusion for more details.

dtype

The dtype of BooleanArray is np.bool_.

args

idx.args contains the arguments needed to create idx.

For an ndindex object idx, idx.args is always a tuple such that

type(idx)(*idx.args) == idx

For Tuple indices, the elements of .args are themselves ndindex types. For other types, .args contains raw Python types. Note that .args contains NumPy arrays for IntegerArray and BooleanArray types, so one should always do equality testing or hashing on the ndindex type itself, not its .args.

property array

Return the NumPy array of self.

This is the same as self.args[0].

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([0, 1]).array
array([0, 1])
>>> BooleanArray([False, True]).array
array([False, True])
as_subindex(index)[source]

i.as_subindex(j) produces an index k such that a[j][k] gives all of the elements of a[j] that are also in a[i].

If a[j] is a subset of a[i], then a[j][k] == a[i]. Otherwise, a[j][k] == a[i & j], where i & j is the intersection of i and j, that is, the elements of a that are indexed by both i and j.

For example, in the below diagram, i and j index a subset of the array a. k = i.as_subindex(j) is an index on a[j] that gives the subset of a[j] also included in a[i]:

    +------------ self ------------+
    |                              |
------------------- a -----------------------
       |                                 |
       +------------- index -------------+
       |                           |
       +- self.as_subindex(index) -+

i.as_subindex(j) is currently only implemented when j is a slice with positive steps and nonnegative start and stop, or a Tuple of the same. To use it with slices with negative start or stop, call reduce() with a shape first.

as_subindex can be seen as the left-inverse of composition, that is, if a[i] = a[j][k], then k = i.as_subindex(j), so that k "=" (j^-1)[i] (this only works as a true inverse if j is a subset of i).

Note that due to symmetry, a[j][i.as_subindex(j)] and a[i][j.as_subindex(i)] will give the same subarrays of a, which will be the array of elements indexed by both a[i] and a[j].

i.as_subindex(j) may raise ValueError in the case that the indices i and j do not intersect at all.

Examples

An example usage of as_subindex is to split an index up into subindices of chunks of an array. For example, say a 1-D array a is chunked up into chunks of size N, so that a[0:N], a[N:2*N], [2*N:3*N], etc. are stored separately. Then an index a[i] can be reindexed onto the chunks via i.as_subindex(Slice(0, N)), i.as_subindex(Slice(N, 2*N)), etc.

>>> from ndindex import Slice
>>> i = Slice(5, 15)
>>> j1 = Slice(0, 10)
>>> j2 = Slice(10, 20)
>>> a = list(range(20))
>>> a[i.raw]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> a[j1.raw]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[j2.raw]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> k1 = i.as_subindex(j1)
>>> k1
Slice(5, 10, 1)
>>> k2 = i.as_subindex(j2)
>>> k2
Slice(0, 5, 1)
>>> a[j1.raw][k1.raw]
[5, 6, 7, 8, 9]
>>> a[j2.raw][k2.raw]
[10, 11, 12, 13, 14]

See also

ndindex.ChunkSize.as_subchunks

a high-level iterator that efficiently gives only those chunks that intersect with a given index

ndindex.ChunkSize.num_subchunks

broadcast_arrays()[source]

Broadcast all the array indices in self to a common shape and convert boolean array indices into integer array indices.

The resulting index is equivalent in all contexts where the original index is allowed. However, it is possible for the original index to give an IndexError but for the new index to not, since integer array indices have less stringent shape requirements than boolean array indices. There are also some instances for empty indices (isempty is True) where bounds would be checked before broadcasting but not after.

Any BooleanArray indices are converted to IntegerArray indices. Furthermore, if there are BooleanArray or IntegerArray indices, then any Integer indices are also converted into scalar IntegerArray indices and broadcast. Furthermore, if there are multiple boolean scalar indices (True or False), they are combined into a single one.

Note that array broadcastability is checked in the Tuple constructor, so this method will not raise any exceptions.

This is part of what is performed by expand, but unlike expand, this method does not do any other manipulations, and it does not require a shape.

>>> from ndindex import Tuple
>>> idx = Tuple([[False], [True], [True]], [[4], [5], [5]], -1)
>>> print(idx.broadcast_arrays())
Tuple(IntegerArray([[1 2] [1 2] [1 2]]),
      IntegerArray([[0 0] [0 0] [0 0]]),
      IntegerArray([[4 4] [5 5] [5 5]]),
      IntegerArray([[-1 -1] [-1 -1] [-1 -1]]))

See also

expand

property count_nonzero

Returns the number of elements indexed by self.

In general, if shapes match, when indexed by self, the first n dimensions of an array are replaced with a single dimension of size count_nonzero, where n is self.shape.

This is the same as np.count_nonzero(self.array). Note, to get the shape of an array indexed by self, use newshape(), not this method.

>>> from ndindex import BooleanArray
>>> BooleanArray([True, False, True]).count_nonzero
2
expand(shape)[source]

Expand a Tuple index on an array of shape shape

An expanded index is as explicit as possible. Unlike reduce, which tries to simplify an index and remove redundancies, expand() typically makes an index larger.

If self is invalid for the given shape, an IndexError is raised. Otherwise, the returned index satisfies the following:

  • It is always a Tuple.

  • All the elements of the Tuple are recursively reduced.

  • The length of the .args is equal to the length of the shape plus the number of Newaxis indices in self plus 1 if there is a scalar BooleanArray (True or False).

  • The resulting Tuple has no ellipses. If there are axes that would be matched by an ellipsis or an implicit ellipsis at the end of the tuple, Slice(0, n, 1) indices are inserted, where n is the corresponding axis of the shape.

  • Any array indices in self are broadcast together. If self contains array indices (IntegerArray or BooleanArray), then any Integer indices are converted into IntegerArray indices of shape () and broadcast. Note that broadcasting is done in a memory efficient way so that even if the broadcasted shape is large it will not take up more memory than the original.

  • Scalar BooleanArray arguments (True or False) are combined into a single term (the same as with Tuple.reduce()).

  • Non-scalar BooleanArrays are all converted into equivalent IntegerArrays via nonzero() and broadcasted.

>>> from ndindex import Tuple, Slice
>>> Slice(None).expand((2, 3))
Tuple(slice(0, 2, 1), slice(0, 3, 1))
>>> idx = Tuple(slice(0, 10), ..., None, -3)
>>> idx.expand((5, 3))
Tuple(slice(0, 5, 1), None, 0)
>>> idx.expand((1, 2, 3))
Tuple(slice(0, 1, 1), slice(0, 2, 1), None, 0)
>>> idx.expand((5,))
Traceback (most recent call last):
...
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
>>> idx.expand((5, 2))
Traceback (most recent call last):
...
IndexError: index -3 is out of bounds for axis 1 with size 2
>>> idx = Tuple(..., [0, 1], -1)
>>> idx.expand((1, 2, 3))
Tuple(slice(0, 1, 1), [0, 1], [2, 2])
isempty(shape=None)[source]

Returns whether self always indexes an empty array

An empty array is an array whose shape contains at least one 0. Note that scalars (arrays with shape ()) are not considered empty.

shape can be None (the default), or an array shape. If it is None, isempty() will return True when self is always empty for any array shape. However, if it gives False, it could still give an empty array for some array shapes, but not all. If you know the shape of the array that will be indexed, use idx.isempty(shape) and the result will be correct for arrays of shape shape. If shape is given and self would raise an IndexError on an array of shape shape, isempty() also raises IndexError.

>>> from ndindex import Tuple, Slice
>>> Tuple(0, slice(0, 1)).isempty()
False
>>> Tuple(0, slice(0, 0)).isempty()
True
>>> Slice(5, 10).isempty()
False
>>> Slice(5, 10).isempty(4)
True
isvalid(shape, _axis=0)[source]

Check whether a given index is valid on an array of a given shape.

Returns True if an array of shape shape can be indexed by self and False if it would raise IndexError.

>>> from ndindex import ndindex
>>> ndindex(3).isvalid((4,))
True
>>> ndindex(3).isvalid((2,))
False

Note that some indices can never be valid and will raise a IndexError or TypeError if you attempt to construct them.

>>> ndindex((..., 0, ...))
Traceback (most recent call last):
  ...
IndexError: an index can only have a single ellipsis ('...')
>>> ndindex(slice(True))
Traceback (most recent call last):
  ...
TypeError: 'bool' object cannot be interpreted as an integer

See also

NDIndex.newshape

property ndim

Return the number of dimensions of the array of self.

This is the same as self.array.ndim. Note that this is not the same as the number of dimensions of an array that is indexed by self. Use len on newshape() to get that.

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([[0], [1]]).ndim
2
>>> BooleanArray([[False], [True]]).ndim
2
newshape(shape)[source]

Returns the shape of a[idx.raw], assuming a has shape shape.

shape should be a tuple of ints, or an int, which is equivalent to a 1-D shape.

Raises IndexError if self would be invalid for an array of shape shape.

>>> from ndindex import Slice, Integer, Tuple
>>> shape = (6, 7, 8)
>>> Integer(1).newshape(shape)
(7, 8)
>>> Integer(10).newshape(shape)
Traceback (most recent call last):
...
IndexError: index 10 is out of bounds for axis 0 with size 6
>>> Slice(2, 5).newshape(shape)
(3, 7, 8)
>>> Tuple(0, ..., Slice(1, 3)).newshape(shape)
(7, 2)

See also

NDIndex.isvalid

property raw

Return the equivalent of self that can be used as an index

NumPy does not allow custom objects to be used as indices, with the exception of integer indices, so to use an ndindex object as an index, it is necessary to use raw.

>>> from ndindex import Slice
>>> import numpy as np
>>> a = np.arange(5)
>>> s = Slice(2, 4)
>>> a[s]
Traceback (most recent call last):
...
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> a[s.raw]
array([2, 3])
reduce(shape=None, *, axis=0, negative_int=False)[source]

Reduce a BooleanArray index on an array of shape shape.

The result will either be IndexError if the index is invalid for the given shape, or a BooleanArray index. Presently, no simplifications are done for BooleanArray: if reduce() does not produce an IndexArray the index returned will be the same as self.

>>> from ndindex import BooleanArray
>>> idx = BooleanArray([True, False])
>>> idx.reduce((3,))
Traceback (most recent call last):
...
IndexError: boolean index did not match indexed array along axis 0; size of axis is 3 but size of corresponding boolean axis is 2
>>> idx.reduce((2,))
BooleanArray([True, False])
selected_indices(shape, axis=0)[source]

Return an iterator over all indices that are selected by self on an array of shape shape.

The result is a set of indices i such that [a[i] for i in idx.selected_indices(a.shape)] is all the elements of a[idx]. The indices are all iterated over in C (i.e., row major) order.

>>> from ndindex import Slice, Tuple
>>> idx = Slice(5, 10)
>>> list(idx.selected_indices(20))
[Integer(5), Integer(6), Integer(7), Integer(8), Integer(9)]
>>> idx = Tuple(Slice(5, 10), Slice(0, 2))
>>> list(idx.selected_indices((20, 3)))
[Tuple(5, 0), Tuple(5, 1),
 Tuple(6, 0), Tuple(6, 1),
 Tuple(7, 0), Tuple(7, 1),
 Tuple(8, 0), Tuple(8, 1),
 Tuple(9, 0), Tuple(9, 1)]

To correspond these indices to the elements of a[idx], you can use iter_indices(idx.newshape(shape)), since both iterators iterate the indices in C order.

>>> from ndindex import iter_indices
>>> idx = Tuple(Slice(3, 5), Slice(0, 2))
>>> shape = (5, 5)
>>> import numpy as np
>>> a = np.arange(25).reshape(shape)
>>> for a_idx, (new_idx,) in zip(
...    idx.selected_indices(shape),
...    iter_indices(idx.newshape(shape))):
...        print(a_idx, new_idx, a[a_idx.raw], a[idx.raw][new_idx.raw])
Tuple(3, 0) Tuple(0, 0) 15 15
Tuple(3, 1) Tuple(0, 1) 16 16
Tuple(4, 0) Tuple(1, 0) 20 20
Tuple(4, 1) Tuple(1, 1) 21 21

See also

ndindex.iter_indices

An iterator of indices to select every element for arrays of a given shape.

ndindex.ChunkSize.as_subchunks

A high-level iterator that efficiently gives only those chunks that intersect with a given index

property shape

Return the shape of the array of self.

This is the same as self.array.shape. Note that this is not the same as the shape of an array that is indexed by self. Use newshape() to get that.

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([[0], [1]]).shape
(2, 1)
>>> BooleanArray([[False], [True]]).shape
(2, 1)
property size

Return the number of elements of the array of self.

This is the same as self.array.size. Note that this is not the same as the number of elements of an array that is indexed by self. Use np.prod on newshape() to get that.

>>> from ndindex import IntegerArray, BooleanArray
>>> IntegerArray([[0], [1]]).size
2
>>> BooleanArray([[False], [True]]).size
2