There's always the one-liner that assumes a
is a row vector (1d array):
(a.reshape((-1, 1)) == np.unique(a)).astype(int)
This works by broadcasting the ==
operation. Let us see how.
When you ask numpy
to apply an operation, first it checks if the dimensions are compatible. If they aren't, an exception is raised. For example, try typing np.ones(3) - np.ones(4)
in an interpreter. After pressing enter, you should see the message
ValueError: operands could not be broadcast together with shapes (3,) (4,).
This happens, because 3 != 4
. Duh. But there's more.
Try a.reshape((-1, 1)) == np.unique(a)
. Despite n!=m
usually holding, numpy
happily calculates a matrix of shape (n, m)
. Why?
This is the magic of broadcasting:
When operating on two arrays, NumPy compares their shapes element-wise. It starts with the trailing (i.e. rightmost) dimensions and works its way left. Two dimensions are compatible when
they are equal, or
one of them is 1
If these conditions are not met, a ValueError: operands could not be broadcast together exception is thrown, indicating that the arrays have incompatible shapes. The size of the resulting array is the size that is not 1 along each axis of the inputs.
How is this rule applied here? Well, the shape of x = a.reshape((-1, 1))
is (n, 1)
, the shape of y = np.unique(a)
is (1, m)
, so the second point from above holds. Therefore numpy expands x
from shape (n, 1)
to xx
of shape (n, m)
by "copying" (to the best of my knowledge there's no copying occurring) its values along the second axis, i.e. respecting the rule
xx[j, k] = x[j] for all j=1..n, k=1..m.
Similarly, y
is expanded from shape (1, m)
to yy
of shape (n, m)
respecting
yy[j, k] = y[k] for all j=1..n, k=1..m
and the operation is applied to xx
and yy
as usual, i.e.
x == y ~> xx == yy ~> :)