这并不是一个numpy的解答,但可以使用一些自制的稀疏符号来降低数据需求量
from numba import jit
@jit
def sparse_outer_eq(A):
n = A.size
c = []
for i in range(n):
for j in range(i + 1, n):
if A[i] == A[j]:
c.append((i, j))
return c
现在,
c
是一个坐标元组列表
(i, j)
,其中
i < j
对应于布尔数组中为“True”的坐标。您可以轻松地对这些集合进行
and
和
or
运算:
list(set(c1) & set(c2))
list(set(c1) | set(c2))
之后,当你想将这个掩码应用到一个数组上时,你可以回溯坐标并使用它们进行高级索引:
i_, j_ = list(np.array(c).T)
i = np.r_[i_, j_, np.arange(n)]
j = np.r_[j_, i_, np.arange(n)]
如果您关心顺序,您可以使用np.lexsort
对i
和j
进行排序。
或者,您可以将sparse_outer_eq
定义为:
@jit
def sparse_outer_eq(A):
n = A.size
c = []
for i in range(n):
for j in range(n):
if A[i] == A[j]:
c.append((i, j))
return c
这个功能可以保留超过2倍的数据,但是坐标会变得简单:
i, j = list(np.array(c).T)
如果您已经进行了任何set
操作,如果您想要一个合理的顺序,这仍然需要进行lexsort
。
如果您的坐标是n位整数,只要您的稀疏度小于1/n->32位约为3%,这应该比布尔格式更节省空间。
关于时间,由于numba
的存在,它甚至比广播更快:
n = 3000
A = np.random.randint(0, 1000, n)
%timeit sparse_outer_eq(A)
100 loops, best of 3: 4.86 ms per loop
%timeit A == A[:, None]
100 loops, best of 3: 11.8 ms per loop
以及比较:
a = A == A[:, None]
b = B == B[:, None]
a_ = sparse_outer_eq(A)
b_ = sparse_outer_eq(B)
%timeit a & b
100 loops, best of 3: 5.9 ms per loop
%timeit list(set(a_) & set(b_))
1000 loops, best of 3: 641 µs per loop
%timeit a | b
100 loops, best of 3: 5.52 ms per loop
%timeit list(set(a_) | set(b_))
1000 loops, best of 3: 955 µs per loop
编辑:如果您想执行&~
(根据您的评论),请使用第二个sparse_outer_eq
方法(这样您就不必跟踪对角线),然后只需执行:
list(set(a_) - set(b_))