Effiziente Serialisierung von numpy booleschen Arrays

8

Ich habe hunderttausende NumPy-Boolean-Arrays, die ich gerne als Schlüssel für ein Wörterbuch verwenden würde. (Die Werte dieses Wörterbuchs geben an, wie oft wir jedes dieser Arrays beobachtet haben.) NumPy-Arrays sind nicht hashbar und können nicht selbst als Schlüssel verwendet werden. Ich möchte diese Arrays so effizient wie möglich serialisieren.

Wir haben zwei Definitionen für die Effizienz, die hier angesprochen werden:

  1. Effizienz bei der Speichernutzung; kleiner ist besser
  2. Effizienz bei der rechnerischen Zeitserialisierung und -wiederherstellung des Arrays; weniger Zeit ist besser

Ich versuche, ein gutes Gleichgewicht zwischen diesen beiden konkurrierenden Interessen zu finden, jedoch ist eine effiziente Speichernutzung für mich wichtiger und ich bin bereit, Rechenzeit zu opfern.

Es gibt zwei Eigenschaften, von denen ich hoffe, dass sie diese Aufgabe erleichtern:

  1. Ich kann garantieren, dass alle Arrays die gleiche Größe und Form haben
  2. Die Arrays sind boolesch, was bedeutet, dass es möglich ist, sie einfach als eine Sequenz von 1 s und 0 s, eine Bitsequenz
  3. , darzustellen

Gibt es eine effiziente Python-Datenstruktur (2.7 oder, wenn möglich, 2.6), zu der ich diese serialisieren könnte (vielleicht eine Art Byte-Struktur), und könnten Sie ein Beispiel für die Konvertierung zwischen einem Array und dieser Struktur geben und von der Struktur zurück zum ursprünglichen Array?

Beachten Sie, dass es nicht notwendig ist, Informationen darüber zu speichern, ob jeder Index True oder False war; Eine Struktur, die einfach Indizes speicherte, wo das Array True war, würde ausreichen, um das Array wiederherzustellen.

Eine ausreichende Lösung würde für ein eindimensionales Array funktionieren, aber eine gute Lösung würde auch für ein zweidimensionales Array funktionieren, und eine große Lösung würde für Arrays mit noch höheren Dimensionen funktionieren.

    
gotgenes 14.07.2011, 14:28
quelle

3 Antworten

7

Zunächst habe ich vorgeschlagen, bitarray zu verwenden. Wie jedoch von @senderle richtig ausgeführt wurde, kann bitarray nicht geändert werden, so dass es nicht direkt in dict eingefügt werden kann.

Hier ist eine überarbeitete Lösung (noch intern auf bitarray bezogen):

%Vor%

Dies funktioniert mit Python 2.5+, erfordert ein Bit pro Array-Element und unterstützt Arrays mit beliebigen Formen / Dimensionen.

BEARBEITEN: In den letzten Versionen müssen Sie ba.tostring und ba.fromstring auf ba.tobytes und ba.frombytes (Veraltet seit Version 0.4.0) ersetzen.

    
NPE 14.07.2011, 14:55
quelle
13

Ich habe drei Vorschläge. Meine erste wird von aix gestohlen. Das Problem ist, dass bitarray -Objekte veränderbar sind und ihre hash es inhaltsunabhängig sind (d. H. Für bitarray b , hash(b) == id(b) ). Dies kann umgangen werden, wie die Antwort von aix zeigt, aber in der Tat brauchen Sie bitarray s überhaupt nicht - Sie können einfach tostring verwenden!

%Vor%

Jetzt haben wir eine unveränderliche Bytefolge, die sich perfekt als Wörterbuchschlüssel eignet. Um zu verdeutlichen, dass diese Escapes nicht maskiert sind, ist dies so kompakt, wie es ohne bitstring -style Serialisierung möglich ist.

%Vor%

Das Zurückkonvertieren ist einfach und schnell:

%Vor%

Wie Aix konnte ich nicht herausfinden, wie man Dimensionsinformationen auf einfache Weise speichert. Wenn Sie das haben müssen, müssen Sie möglicherweise längere Schlüssel ertragen. cPickle scheint jedoch eine gute Wahl zu sein. Dennoch ist seine Ausgabe 10x so groß ...

%Vor%

Es ist auch langsamer:

%Vor%

Mein dritter Vorschlag verwendet bitstring s - speziell bitstring.ConstBitArray . Es ist ähnlich wie die aix Lösung, aber ConstBitArray s sind unveränderlich, also machen sie was du willst, hash -wise.

%Vor%

Sie müssen das numpy Array explizit reduzieren:

%Vor%

Es ist unveränderlich, also hasst es gut:

%Vor%

Es ist superleicht, wieder in ein Array zu konvertieren, aber auch hier gehen die Forminformationen verloren.

%Vor%

Es ist auch die langsamste Option bei weitem:

%Vor%

Hier finden Sie weitere Informationen. Ich habe weiter herumgespielt und Dinge getestet und mir folgendes ausgedacht. Erstens ist bitarray Weg schneller als bitstring , wenn Sie es richtig verwenden:

%Vor%

Zweitens, wie Sie oben sehen können, sind alle tostring shenanigans unnötig; Sie könnten das Array numpy auch explizit reduzieren. Aber tatsächlich ist die Methode von aix schneller, und darauf basieren die jetzt überarbeiteten Zahlen.

Hier also eine vollständige Zusammenfassung der Ergebnisse. Erstens, Definitionen:

%Vor%

keysize ist das Ergebnis von sys.getsizeof({small|big}_nda.tostring()) , sys.getsizeof({small|big}_barray) + sys.getsizeof({small|big}_barray.tostring()) oder sys.getsizeof({small|big}_bstr) + sys.getsizeof({small|big}_bstr.tobytes()) - beide letzteren Methoden geben Bitstrings zurück, die in Bytes gepackt sind. Daher sollten sie eine gute Schätzung des von ihnen belegten Speicherplatzes sein.

speed ist die Zeit, die für die Konvertierung von {small|big}_nda in einen Schlüssel und zurück benötigt wird, plus die Zeit, die zum Konvertieren eines bitarray -Objekts in eine Zeichenfolge für Hashing benötigt wird, die entweder ein einmaliger Aufwand ist Sie können die Zeichenfolge oder eine Kosten pro dict-Operation zwischenspeichern, wenn Sie sie nicht zwischenspeichern.

%Vor%

Wie Sie sehen, ist bitarray beeindruckend schnell und aix 'Vorschlag einer Unterklasse von bitarray sollte gut funktionieren. Sicher ist es viel schneller als bitstring . Freut mich zu sehen, dass du diese Antwort akzeptiert hast.

Auf der anderen Seite fühle ich mich immer noch der Methode numpy.array.tostring() verbunden. Die Schlüssel, die es erzeugt, sind asymptotisch 8x so groß, aber die Beschleunigung, die Sie für große Arrays bekommen, bleibt beträchtlich - ungefähr 30x auf meiner Maschine für große Arrays. Es ist ein guter Kompromiss. Dennoch ist es wahrscheinlich nicht genug, sich damit zu beschäftigen, bis es zum Flaschenhals wird.

    
senderle 14.07.2011 16:44
quelle
0

Ich würde das Array mit np.packbits in ein Bitfeld konvertieren. Dies ist ziemlich speichereffizient, es verwendet alle Bits eines Bytes. Trotzdem ist der Code relativ einfach.

%Vor%

Seien Sie vorsichtig mit Bool-Arrays variabler Länge, der Code unterscheidet nicht zwischen einem Array mit allen Falsen von beispielsweise 6 oder 7 Mitgliedern. Für mehrdimensionale Arrays benötigen Sie etwas Umformung ..

Wenn dies immer noch nicht effizient genug ist und Ihre Arrays groß sind, können Sie den Speicher möglicherweise weiter reduzieren, indem Sie Folgendes packen:

%Vor%

Es funktioniert jedoch nicht für zufällige, nicht komprimierbare Daten

    
Okapi575 06.11.2017 14:38
quelle