【发布时间】:2018-02-19 20:53:03
【问题描述】:
我有一个可以是任何类型的 2D NumPy 数组,但是对于这个例子,我们可以假设它是整数。我正在寻找找到数组中所有唯一行的最快方法。
我最初的策略是将每一行转换为一个元组并将其添加到一个集合中。如果集合的长度增加,则意味着找到了唯一的行。
我不知道如何将每一行快速散列为字节。有一个问题是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)]))
版本 3:
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)
避免使用 tuple 构造函数(第 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)
使用类型化的内存视图
我相信,减速的很大一部分是对每一行 a[i] 的访问。我们可以使用类型化内存视图来提高性能,但是我不知道如何将类型化内存视图的元素转换为字符串以便它们可以被散列。
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和memoryviews) - 我怀疑它是否会很多 -
@DavidW 不,不幸的是,这没有帮助。其他想法包括在循环之前将整个数组转换为字符串。另外,我不确定将一行转换为字符串是否能保证唯一性。
-
np.unique(a, axis=0)对我来说是20.6 ms ± 77.5 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)。也许这是一个开始的基础?我不确定该方法是否可以在 Cython 中使用。 -
@roganjosh np.unique 非常慢(除非数据几乎没有重复)并首先对数据进行排序。我正在寻找基于哈希的解决方案。
-
为什么不使用自己的哈希函数呢?我已经成功地在纯 cython 中实现了 FNV 哈希:github.com/yt-project/yt/blob/…