最有效的属性散列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的各种博客和答案中,您将看到使用sha1
或md5
作为哈希函数的人。 出于性能原因,这通常是不可接受的,因为这些“安全”散列函数相当慢。 只有散列冲突是最关心的问题之一,它们才有用。
尽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))
更好,因为后者可能会混淆在中间具有唯一数据但在边缘为零的数组。