emonigma
emonigma

Reputation: 4440

Indexing Numpy arrays with range() throws shape mismatch error

I am new to Numpy and OpenCV. I find it odd that Numpy arrays can be indexed with ranges only for the first dimension:

>>> import numpy
>>> 
>>> a = numpy.zeros((3, 3), dtype=numpy.int8)
>>> 
>>> i_range = range(3)
>>> j_range = range(3)
>>> 
>>> print(i_range)
range(0, 3)
>>> print(j_range)
range(0, 3)
>>> print(a[i_range, j_range])
[0 0 0]
>>> print(a[0:3, 0:3])
[[0 0 0]
 [0 0 0]
 [0 0 0]]
>>> a[i_range, j_range] = numpy.ones((3,3), dtype=numpy.int8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: shape mismatch: value array of shape (3,3) could not be broadcast to indexing result of shape (3,)
>>> a[0:3, 0:3] = numpy.ones((3,3), dtype=numpy.int8)
>>> a
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]], dtype=int8)

Indexing with ranges returns a vector of length 3, indexing with full numbers returns a 3x3 array. The former throws an error when assigning values to indexed arrays, the latter works fine.

Why does that happen?

Upvotes: 0

Views: 150

Answers (2)

hpaulj
hpaulj

Reputation: 231738

That fact that you are using range instead of a list, has nothing to do with your error.

Make an array with distinctive values:

In [30]: a = np.arange(9).reshape(3,3)                                          
In [31]: a                                                                      
Out[31]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

Indexing with two ranges produces a 1d array, in this case the diagonal.

In [32]: a[range(3),range(3)]                                                   
Out[32]: array([0, 4, 8])

a[[0,1,2], [0,1,2]] would do the same thing. The (3,) block in your error refers to this 1d array.

To get a block equivalent to the [0:3, 0:3] slicing, you have to use arrays that broadcast against each other. A handy utility is ix_:

In [33]: np.ix_(range(3), range(3))                                             
Out[33]: 
(array([[0],
        [1],
        [2]]), array([[0, 1, 2]]))

Note that one array is (3,1), the other (1,3); broadcasted together they reference a (3,3) block of values:

In [34]: a[np.ix_(range(3), range(3))]                                          
Out[34]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

Now we can assign a (3,3) array of values to it:

In [35]: a[np.ix_(range(3), range(3))] = np.ones((3,3))                         
In [36]: a                                                                      
Out[36]: 
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])

Upvotes: 1

Sai Krishnan
Sai Krishnan

Reputation: 116

The difference is that in Python 3.x, range() produces an iterator and not a list. Until Python 2.x, this functionality was used by xrange(). However, now, simply calling range() produces an iterator.

Upvotes: 0

Related Questions