我有一个2D的NumPy数组,它可以是任何类型,但对于这个例子,我们可以假设它是整数。我正在寻找一种最快的方法来查找数组中所有独特的行。
我的初始策略是将每行转换为元组,并将其添加到集合中。如果集合的长度增加,则意味着找到了一个唯一的行。
我不知道如何快速将每行哈希为字节。有一个问题在这里entire array is hashed here.
我尝试过创建元组,有很多种方法,每种方法都会影响性能。这是我的函数,我展示了4种不同的变化:
避免使用元组构造函数(版本4)可以获得良好的性能提升。
使用
从上面链接的SO问题中,我可以在每一行上使用
我的初始策略是将每行转换为元组,并将其添加到集合中。如果集合的长度增加,则意味着找到了一个唯一的行。
我不知道如何快速将每行哈希为字节。有一个问题在这里entire array is hashed here.
我尝试过创建元组,有很多种方法,每种方法都会影响性能。这是我的函数,我展示了4种不同的变化:
版本1:
def unique_int_tuple1(ndarray[np.int64_t, ndim=2] a):
cdef int i, len_before
cdef int nr = a.shape[0]
cdef int nc = a.shape[1]
cdef set s = set()
cdef ndarray[np.uint8_t, cast = True] idx = np.zeros(nr, dtype='bool')
for i in range(nr):
len_before = len(s)
s.add(tuple(a[i])) # THIS LINE IS CHANGED FOR ALL VERSIONS
if len(s) > len_before:
idx[i] = True
return idx
版本 2:
s.add(tuple([a[i, j] for j in range(nc)]))
第三版:
vals
是一个列表,其长度等于列数。
for j in range(nc):
vals[j] = a[i, j]
s.add(tuple(vals))
版本 4:
s.add((a[i, 0], a[i, 1], a[i, 2], a[i, 3]))
性能
a = np.random.randint(0, 8, (10**5, 4))
%timeit unique_int_tuple1(a)
125 ms ± 1.96 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit unique_int_tuple2(a)
14.5 ms ± 93.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit unique_int_tuple3(a)
11.7 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit unique_int_tuple4(a)
9.59 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
避免使用元组构造函数(版本4)可以获得良好的性能提升。
使用
tostring
:从上面链接的SO问题中,我可以在每一行上使用
tostring
方法,然后进行哈希处理。def unique_int_tostring(ndarray[np.int64_t, ndim=2] a):
cdef int i, j
cdef int nr = a.shape[0]
cdef int nc = a.shape[1]
cdef set s = set()
cdef ndarray[np.uint8_t, cast = True] idx = np.zeros(nr, dtype='bool')
for i in range(nr):
len_before = len(s)
s.add(a[i].tostring())
if len(s) > len_before:
idx[i] = True
return idx
这个可以工作,但非常慢:
%timeit unique_int_tostring(a)
40 ms ± 428 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
使用类型化的memoryview
我认为,减速的一个重要原因是访问每一行 a[i]
。我们可以使用类型化的memoryviews来提高性能,但我不知道如何将类型化的memoryviews元素转换为字符串,以便对它们进行哈希。
def unique_int_memoryview(long[:, :] a):
cdef int i, j
cdef int nr = a.shape[0]
cdef int nc = a.shape[1]
cdef set s = set()
for i in range(nr):
s.add(<SOMETHING>) # NO IDEA HERE
return s
a[i,:]
而不是a[i]
(对于ndarray
和内存视图都适用)来获得一些改进 - 尽管我怀疑这不会有太大的改善。 - DavidWnp.unique(a, axis=0)
的输出是20.6 毫秒 ± 77.5 微秒每次循环(平均值±7 次运行的标准差,每次 10 次循环)
。也许这可以作为起点?我不确定这种方法是否可以在 Cython 中使用。 - roganjosh