For example, trying to make sense of these results:
>>> x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> (x == np.array([[1],[2]])).astype(np.float32)
array([[ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)
>>> (x == np.array([1,2]))
False
>>> (x == np.array([[1]])).astype(np.float32)
array([[ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)
>>> (x == np.array([1])).astype(np.float32)
array([ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)
>>> (x == np.array([[1,3],[2]]))
False
>>>
What's going on here? In the case of [1], it's comparing 1 to each element of x and aggregating the result in an array. In the case of [[1]], same thing. It's easy to figure out what's going to occur for specific array shapes by just experimenting on the repl. But what are the underlying rules where both sides can have arbitrary shapes?