散列为numpy数组的最有效属性

时间:2013-05-16 14:12:51

标签: python numpy

我需要能够在numpy中存储array dict用于缓存目的。哈希速度很重要。

array表示标记,因此虽然对象的实际标识不重要,但值为。可变性不是一个问题,因为我只对当前的价值感兴趣。

为了将其存储在dict

,我应该散列什么

我目前的方法是使用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可以提供最佳性能。

虽然对只读缓冲区进行哈希处理很快,但设置可写入标志的开销实际上会使它变慢。

5 个答案:

答案 0 :(得分:37)

如果将其设置为只读,则可以简单地对底层缓冲区进行哈希:

>>> 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]'

答案 1 :(得分:17)

您可以通过xxhash尝试Python binding。对于大型数组,这比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作为哈希函数的人。出于性能原因,这通常可接受,因为那些" secure"哈希函数相当慢。只有当哈希冲突成为最受关注的问题之一时,它们才有用。

然而,哈希冲突一直在发生。如果您只需要为数据数组对象实现__hash__以便它们可以用作Python词典或集合中的键,我认为最好将注意力集中在__hash__的速度上本身让Python处理哈希冲突[1]。

[1]您可能还需要覆盖__eq__,以帮助Python管理哈希冲突。你希望__eq__返回一个布尔值,而不是像numpy那样返回一个布尔数组。

答案 2 :(得分:2)

你有什么样的数据?

  • array-size
  • 您在数组中有多次索引

如果您的数组只包含索引的排列,则可以使用base-convertion

(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()

现在您可以使用数组(shape =(base_size,))而不是dict来访问值。

答案 3 :(得分:1)

迟到了派对,但对于大型阵列,我认为一个不错的方法就是随机对矩阵进行子样本处理并散列该样本:

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))更好,因为后者可能会混淆中间有唯一数据但边缘为零的数组。

答案 4 :(得分:0)

如果您的np.array()很小且处于紧密循环中,那么一种选择是完全跳过hash(),而直接使用np.array().data.tobytes()作为您的字典键:

grid  = np.array([[True, False, True],[False, False, True]])
hash  = grid.data.tobytes()
cache = cache or {}
if hash not in cache:
    cache[hash] = function(grid)
return cache[hash]