Philipp
Philipp

Reputation: 662

np.where(condition is None) not equal to np.where(condition == None)

I'm troubled with the np.where() function. (line 7 in my example)

Background: I am programming the game "Connect Four". This insert_chip() method accesses the variable self.board which is an 8x8 np array of my personal dtype Chip. If there is no chip in an entry of self.board, then the value is None.

For some reason, np.where(col_entries is None) does not return the indices of the elements that are None. And why do I receive a different output, when I write col_entries == None in the condition? It's not a if None has a reference right?

def insert_chip(self, chip, col):
    # slices the entries of the column into a new array
    col_entries = self.board[:, col:col+1]

    # checks for all unoccupied pos in this col (entries are None)
    # gives double array of indexes with the form (array([row_i, ...]), array([col_i, ...]))
    none_indexes = np.where(col_entries is None)

    # the pos where the chip will fall is the one with the highest index
    self.board[len(none_indexes[0]), col] = chip

Upvotes: 8

Views: 15237

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476594

For some reason, np.where(col_entries is None) does not return the indices of the elements that are None.

The is operator checks if the two operands point to the same object. So here it checks if col_entries (the matrix) is None, it thus does not performs "broadcasting" to check if some elements in the matrix refer to None.

In Python one can overload certain operators like <=, ==, etc. Numpy makes use of that to implement specific operators, such that one can write some_matrix == 0 to generate a matrix of booleans. The is operator can not be overloaded, and so Numpy (nor any other library) has control over this. is simply checks if the two operands refer to the same object.

Since here your col_entries refers to a numpy array, this will always be False, hence np.where(col_entries is None) will always return an 1-tuple containing an empty array.

Although there are not that much objects that are equal to None, it is still not very safe to count on that. We can vectorize the is operator, like:

from operator import is_

np.where(np.vectorize(is_)(col_entries, None))

Upvotes: 7

hpaulj
hpaulj

Reputation: 231385

Make an object dtype array:

In [37]: arr = np.zeros((3,3), object)
In [39]: arr[range(3),range(3)]=None
In [40]: arr
Out[40]: 
array([[None, 0, 0],
       [0, None, 0],
       [0, 0, None]], dtype=object)

The is None test:

In [41]: arr is None
Out[41]: False

The == test.

In [42]: arr == None
Out[42]: 
array([[ True, False, False],
       [False,  True, False],
       [False, False,  True]])
In [43]: np.where(arr == None)
Out[43]: (array([0, 1, 2]), array([0, 1, 2]))

Propagation of comparison tests in object arrays has been undergoing some changes. From a recent release_notes: https://docs.scipy.org/doc/numpy-1.15.1/release.html#comparison-ufuncs-accept-dtype-object-overriding-the-default-bool


Similar operations on a list

In [44]: alist = [0,None,0]
In [45]: alist is None
Out[45]: False
In [46]: [i is None for i in alist]
Out[46]: [False, True, False]
In [48]: alist.index(None)
Out[48]: 1

Upvotes: 7

Related Questions