方法 #1: 我们可以简单地将数组扩展两个 3D 版本并进行比较,从而让 broadcasting 发挥作用 -
(arr[:,None] == arr).all(2).astype(int)
示例运行 -
In [19]: a,b,c,d,e,f = 4,7,3,1,8,2
...: arr = np.array([
...: [a, b, c],
...: [d ,e, f],
...: [a ,b, c],
...: [d ,e, f]])
...:
In [20]: arr
Out[20]:
array([[4, 7, 3],
[1, 8, 2],
[4, 7, 3],
[1, 8, 2]])
In [21]: (arr[:,None] == arr).all(2).astype(int)
Out[21]:
array([[1, 0, 1, 0],
[0, 1, 0, 1],
[1, 0, 1, 0],
[0, 1, 0, 1]])
方法 #2: 使用 views 的内存高效方法,因为我们将每一行减少到一个 值,然后执行 broadcasted-comparison -
# https://stackoverflow.com/a/44999009/ @Divakar
def view1D(a): # a is array
a = np.ascontiguousarray(a)
void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
return a.view(void_dt).ravel()
arr1D = view1D(arr)
out = (arr1D[:,None] == arr1D).astype(int)
方法 #3: 出于对内置插件的喜爱,这里有另一个在理论上与前一个类似,但使用 np.unique 和新的 axis 功能 -
ids = np.unique(arr, axis=0, return_inverse=1)[1]
out = (ids[:,None] == ids).astype(int)
方法 #4: 另一种根据每行在其他行中的唯一性来标记每行的方法是将每行视为2D 网格上的线性索引等价物,从而为我们提供更多获取ids 的高效方法 -
ids = arr.dot((arr.max()-arr.min()+1)**np.arange(arr.shape[1]))
out = (ids[:,None] == ids).astype(int)
如果我们保证有正数,请跳过arr.min()。
我们在这里需要小心,因为数字的巨大变化或大量的列会导致溢出。因此,在使用这种方法时请牢记这些。
额外的东西
为了发挥最大性能,使用uint8 作为输出dtype,这似乎很好,因为我们只需要在输出中包含0s 和1s,如下面的时序所示 -
In [41]: bool_arr = np.random.rand(100,100)>0.5
In [42]: %timeit bool_arr.astype(int)
...: %timeit bool_arr.astype(np.uint8)
...:
100000 loops, best of 3: 4.15 µs per loop
1000000 loops, best of 3: 897 ns per loop
In [43]: bool_arr = np.random.rand(5000,5000)>0.5
In [44]: %timeit bool_arr.astype(int)
...: %timeit bool_arr.astype(np.uint8)
...:
10 loops, best of 3: 21 ms per loop
100 loops, best of 3: 3.16 ms per loop