1

I want to find the index of a particular numpy array with in a sequence. For example, given:

import numpy as np

WHITE = np.array([255, 255, 255])
BLUE = np.array([0, 0, 255])
GRAY = np.array([192, 192, 192])
BLACK = np.array([0, 0, 0])
GREEN = np.array([0, 255, 0])
YELLOW = np.array([255, 255, 0])
COLORS = (WHITE, BLUE, GRAY, BLACK, GREEN)

I'd like to be able to ask for something like

print(COLORS.index(GRAY))

but this leads to the error:

ValueError: The truth value of an array with more than one element
is ambiguous. Use a.any() or a.all()

I do have a workaround, but it feels overly acrobatic:

def index_of(x, sequence):
    eq = list((item == x).all() for item in sequence)
    return eq.index(True)

print(index_of(GRAY, COLORS))

Is there are more elegant/Pythonic way to do this?

9
  • 1
    Can't you just use a dict? But essentially, no, since index uses == and == produces an array. Commented Oct 19, 2017 at 21:22
  • next(i for (i, item) in enumerate(sequence) if (item == x).all()) would be slightly more elegant I think, although it is not very pretty either Commented Oct 19, 2017 at 21:30
  • 1
    Although, there are several ways to make your approach more elegant. Starting by not using list(<generator expression>) and just using a list-comprehension. Or better yet, just use a for-loop and return early.... Commented Oct 19, 2017 at 21:30
  • 1
    @tehforsch you should just use a for-loop in that case. Wayyyy more elegant. Commented Oct 19, 2017 at 21:30
  • @juanpa.arrivillaga and then have a break-statement? thats fine too, whether that is more elegant is a matter of taste i would say Commented Oct 19, 2017 at 21:31

3 Answers 3

2

May be a matter of taste but in this case a for loop could be more readable:

def index_of(search_for, arrays):
    for i, array in enumerate(arrays):
        if np.array_equal(search_for, array):
            return i
    raise ValueError('{} not in sequence'.format(search_for))

numpy function np.array_equal allows for comparison of arrays of different sizes in case you'll need it ((item == x).all() will raise an exception for different array sizes)

Also raise a ValueError exception to mimic the index function of the tuple.

Sign up to request clarification or add additional context in comments.

Comments

1

Considering that you index not by values, but by actual objects, you should be able to use is:

def index_of(color, seq):
    return next(i for i, x in enumerate(seq) if x is color)

1 Comment

I'm confused. Isn't that just going to return color, rather than the index where color appears?
0

If COLORS was a list of lists, then the index works:

In [39]: C = [c.tolist() for c in COLORS]
In [40]: C
Out[40]: [[255, 255, 255], [0, 0, 255], [192, 192, 192], [0, 0, 0], [0, 255, 0]]
In [41]: C.index(GRAY.tolist())
Out[41]: 2

With array elements, the index does something like:

In [44]: [c==GRAY for c in COLORS]
Out[44]: 
[array([False, False, False], dtype=bool),
 array([False, False, False], dtype=bool),
 array([ True,  True,  True], dtype=bool),
 array([False, False, False], dtype=bool),
 array([False, False, False], dtype=bool)]

Equality tests with array are done element by element, with the result being a boolean array. But using a boolean array in a context that expects a simple scalar boolean, results in the (all-too-common) ambiguity error. all can reduce those arrays to single values.

In [46]: [(c==GRAY).all() for c in COLORS]
Out[46]: [False, False, True, False, False]

In [47]: [(c==GRAY).all() for c in COLORS].index(True)
Out[47]: 2
In [48]: [(c==BLUE).all() for c in COLORS].index(True)
Out[48]: 1

If COLORS is turned into a 2d array, we can do test like:

In [49]: CA = np.array(COLORS)
In [50]: CA
Out[50]: 
array([[255, 255, 255],
       [  0,   0, 255],
       [192, 192, 192],
       [  0,   0,   0],
       [  0, 255,   0]])
In [51]: CA==GRAY
Out[51]: 
array([[False, False, False],
       [False, False, False],
       [ True,  True,  True],
       [False, False, False],
       [False, False, False]], dtype=bool)
In [52]: (CA==GRAY).all(axis=1)
Out[52]: 
array([False, False,  True, False, False], dtype=bool)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.