最有效的属性散列numpy数组

我需要能够在dict存储一个numpy array来进行caching。 哈希速度很重要。

array表示的是标记,所以虽然对象的实际标识并不重要,但值是。 Mutabliity不是一个问题,因为我只对目前的价值感兴趣。

我应该散列什么来存储在dict

我目前的做法是在我的testing中使用str(arr.data) ,它比md5快。


我已经从答案中结合了一些例子来获得相对时代的想法:

 In [121]: %timeit hash(str(y)) 10000 loops, best of 3: 68.7 us per loop In [122]: %timeit hash(y.tostring()) 1000000 loops, best of 3: 383 ns per loop In [123]: %timeit hash(str(y.data)) 1000000 loops, best of 3: 543 ns per loop In [124]: %timeit y.flags.writeable = False ; hash(y.data) 1000000 loops, best of 3: 1.15 us per loop In [125]: %timeit hash((b*y).sum()) 100000 loops, best of 3: 8.12 us per loop 

看起来,对于这个特定的用例(小数组arr.tostring ), arr.tostring提供了最好的性能。

虽然散列只读缓冲区本身是快速的,但是设置可写入标志的开销实际上使其变慢。

你可以简单地散列底层的缓冲区,如果你把它设为只读:

 >>> a = random.randint(10, 100, 100000) >>> a.flags.writeable = False >>> %timeit hash(a.data) 100 loops, best of 3: 2.01 ms per loop >>> %timeit hash(a.tostring()) 100 loops, best of 3: 2.28 ms per loop 

对于非常大的数组, hash(str(a))要快得多,但是只考虑了数组的一小部分。

 >>> %timeit hash(str(a)) 10000 loops, best of 3: 55.5 us per loop >>> str(a) '[63 30 33 ..., 96 25 60]' 

你可以通过它的Python绑定来尝试xxhash 。 对于大型数组,这比hash(x.tostring())快得多。

IPython会话示例:

 >>> import xxhash >>> import numpy >>> x = numpy.random.rand(1024 * 1024 * 16) >>> h = xxhash.xxh64() >>> %timeit hash(x.tostring()) 1 loops, best of 3: 208 ms per loop >>> %timeit h.update(x); h.intdigest(); h.reset() 100 loops, best of 3: 10.2 ms per loop 

顺便说一下,在发布到Stack Overflow的各种博客和答案中,您将看到使用sha1md5作为哈希函数的人。 出于性能原因,这通常是不可接受的,因为这些“安全”散列函数相当慢。 只有散列冲突是最关心的问题之一,它们才有用。

尽pipe如此,哈希碰撞总是发生。 如果你只需要为数据数组对象实现__hash__ ,以便它们可以用作Python字典或集合中的键,我认为最好把注意力放在__hash__本身的速度上,让Python处理散列冲突[1]。

[1]您可能还需要重写__eq__ ,以帮助Pythonpipe理散列冲突。 你会想__eq__返回一个布尔值,而不是像numpy所做的布尔值数组。

你有什么样的数据?

  • arrays尺寸
  • 数组中有多less次索引?

如果你的数组只包含索引的排列,你可以使用基转换

 (1, 0, 2) -> 1 * 3**0 + 0 * 3**1 + 2 * 3**2 = 10(base3) 

并使用“10”作为hash_key通过

 import numpy as num base_size = 3 base = base_size ** num.arange(base_size) max_base = (base * num.arange(base_size)).sum() hashed_array = (base * array).sum() 

现在你可以使用一个数组(形状=(base_size,))而不是一个字典来访问这些值。

对于晚会来说,但是对于大型数组,我认为一个体面的方法是随机对matrix进行二次抽样并散列该样本:

 def subsample_hash(a): rng = np.random.RandomState(89) inds = rng.randint(low=0, high=a.size, size=1000) b = a.flat[inds] b.flags.writeable = False return hash(b.data) 

我认为这比hash(str(a))更好,因为后者可能会混淆在中间具有唯一数据但在边缘为零的数组。