it-swarm.com.de

Effizienteste Eigenschaft, die für numpy-Arrays gehasht werden kann

Ich muss in der Lage sein, eine numpyarray in einer dict für Caching-Zwecke zu speichern. Hash-Geschwindigkeit ist wichtig.

Das array stellt Hinweise dar. Während die tatsächliche Identität des Objekts nicht wichtig ist, ist der Wert gleich. Mutabliity ist kein Problem, da ich nur am aktuellen Wert interessiert bin.

Was muss ich hashen, um es in einer dict zu speichern?

Mein aktueller Ansatz ist es, str(arr.data) zu verwenden, was schneller ist als md5 in meinen Tests.


Ich habe einige Beispiele aus den Antworten zusammengestellt, um eine Vorstellung von relativen Zeiten zu bekommen:

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

Es scheint, dass für diesen speziellen Anwendungsfall (kleine Anordnungen von Zeichen) arr.tostring bietet die beste Leistung.

Während das Hashing des Nur-Lese-Puffers von sich aus schnell ist, wird er durch den Mehraufwand beim Setzen des beschreibbaren Flags tatsächlich langsamer.

47
sapi

Sie können den zugrunde liegenden Puffer einfach hashen, wenn Sie ihn schreibgeschützt machen:

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

Bei sehr großen Arrays ist hash(str(a)) viel schneller, berücksichtigt aber nur einen kleinen Teil des Arrays.

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

Sie können versuchen, xxhash über seine Python-Bindung . Bei großen Arrays ist dies viel schneller als hash(x.tostring()).

Beispiel für eine IPython-Sitzung:

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

Übrigens werden Sie in verschiedenen Blogs und Antworten, die auf Stack Overflow gepostet werden, Leute sehen, die sha1 oder md5 als Hash-Funktionen verwenden. Aus Performancegründen ist dies normalerweise nicht akzeptabel, da diese "sicheren" Hash-Funktionen eher langsam sind. Sie sind nur dann nützlich, wenn Hash-Kollisionen eines der Hauptprobleme sind.

Trotzdem kommt es immer wieder zu Hash-Kollisionen. Und wenn Sie nur __hash__ für Datenarrayobjekte implementieren, damit diese als Schlüssel in Python Wörterbüchern oder Mengen verwendet werden können, ist es meiner Meinung nach besser, sich auf die Geschwindigkeit von __hash__ selbst zu konzentrieren und Python behandelt die Hash-Kollision [1].

[1] Möglicherweise müssen Sie auch __eq__ überschreiben, um Python die Hash-Kollision zu verwalten. Sie möchten, dass __eq__ einen Booleschen Wert zurückgibt und nicht ein Array von Booleschen Werten, wie dies von numpy ausgeführt wird.

19
Cong Ma

Welche Daten haben Sie?

  • array-Größe
  • haben Sie einen Index mehrmals im Array

Wenn Ihr Array nur aus der Permutation von Indizes besteht, können Sie eine Basiskonvertierung verwenden

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

und benutze '10' als hash_key via

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

Jetzt können Sie anstelle eines Diktats ein Array (shape = (base_size,)) verwenden, um auf die Werte zuzugreifen.

2
Hensing

Wenn ich zu spät zur Party komme, aber für große Arrays, denke ich, ist es eine vernünftige Möglichkeit, die Matrix zufällig zu subsampeln und das Sample zu hashen:

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)

Ich denke, das ist besser als hash(str(a)), weil letztere Arrays verwirren könnten, die eindeutige Daten in der Mitte, aber Nullen an den Rändern haben.

1
hunse