wake-up-neo.com

Wie kann ich ein numpy-Array unter Beibehaltung der Matrixabmessungen serialisieren?

numpy.array.tostring scheint keine Informationen über Matrixabmessungen aufzubewahren (siehe diese Frage ), sodass der Benutzer numpy.array.reshape aufrufen muss. 

Gibt es eine Möglichkeit, ein numpy-Array unter Beibehaltung dieser Informationen in das JSON-Format zu serialisieren?

Hinweis: Die Arrays können Ints, Floats oder Bools enthalten. Es ist vernünftig, ein transponiertes Array zu erwarten.

Anmerkung 2: Dies geschieht mit der Absicht, das numpy-Array mithilfe von streamparse durch eine Storm-Topologie zu leiten, falls diese Informationen relevant sind.

30
blz

pickle.dumps oder numpy.save kodiert alle Informationen, die zur Rekonstruktion eines beliebigen NumPy-Arrays erforderlich sind, selbst bei endianness-Problemen, nicht zusammenhängenden Arrays oder ungewöhnlichen Tupel-Typen. Endianness-Themen sind wahrscheinlich die wichtigsten; Sie möchten nicht, dass array([1]) plötzlich array([16777216]) wird, weil Sie Ihr Array auf einem Big-Endian-Computer geladen haben. pickle ist wahrscheinlich die bequemere Option, obwohl save seine eigenen Vorteile hat, die im npy-Format angegeben werden.

Die Option pickle:

import pickle
a = # some NumPy array
serialized = pickle.dumps(a, protocol=0) # protocol 0 is printable ASCII
deserialized_a = pickle.loads(serialized)

numpy.save verwendet ein binäres Format und muss in eine Datei schreiben. Mit StringIO können Sie dies umgehen:

a = # any NumPy array
memfile = StringIO.StringIO()
numpy.save(memfile, a)
memfile.seek(0)
serialized = json.dumps(memfile.read().decode('latin-1'))
# latin-1 maps byte n to unicode code point n

Und zu deserialisieren:

memfile = StringIO.StringIO()
memfile.write(json.loads(serialized).encode('latin-1'))
memfile.seek(0)
a = numpy.load(memfile)
36
user2357112

EDIT: Wie man in den Kommentaren der Frage nachlesen kann, befasst sich diese Lösung mit "normalen" numpy-Arrays (Floats, Ints, Bools ...) und nicht mit strukturierten Arrays mit mehreren Typen.

Lösung zur Serialisierung eines numpy-Arrays beliebiger Dimensionen und Datentypen

Soweit ich weiß, können Sie ein numpy-Array nicht einfach mit einem beliebigen Datentyp und einer beliebigen Dimension serialisieren. Sie können jedoch Datentyp, Dimension und Informationen in einer Listendarstellung speichern und dann mit JSON serialisieren.

Importe benötigt :

import json
import base64

Für die Kodierung könnte man verwenden (nparray ist irgendein Array mit beliebigem Datentyp und beliebiger Dimensionalität):

json.dumps([str(nparray.dtype), base64.b64encode(nparray), nparray.shape])

Danach erhalten Sie einen JSON-Dump (String) Ihrer Daten, der eine Listendarstellung des Datentyps und der Form sowie die Array64-kodierten Daten/Inhalte der Arrays enthält.

Und zum Dekodieren dies erledigt die Arbeit (encStr ist der verschlüsselte JSON-String, der von irgendwo geladen wird):

# get the encoded json dump
enc = json.loads(encStr)

# build the numpy data type
dataType = numpy.dtype(enc[0])

# decode the base64 encoded numpy array data and create a new numpy array with this data & type
dataArray = numpy.frombuffer(base64.decodestring(enc[1]), dataType)

# if the array had more than one data set it has to be reshaped
if len(enc) > 2:
     dataArray.reshape(enc[2])   # return the reshaped numpy array containing several data sets

JSON-Dumps sind aus vielen Gründen effizient und kreuzkompatibel. Die Verwendung von JSON führt jedoch zu unerwarteten Ergebnissen, wenn Sie numpy-Arrays von jedem Typ und einer beliebigen Dimension speichern und laden möchten.

Diese Lösung speichert und lädt numpy-Arrays unabhängig vom Typ oder der Dimension und stellt sie auch korrekt wieder her (Datentyp, Dimension, ...).

Ich habe selbst schon vor Monaten mehrere Lösungen ausprobiert, und dies war die einzige effiziente und vielseitige Lösung, die ich gefunden habe.

10
daniel451

Ich fand den Code in Msgpack-numpy hilfreich . https://github.com/lebedov/msgpack-numpy/blob/master/msgpack_numpy.py

Ich habe das serialisierte Dikt etwas geändert und die Base64-Codierung hinzugefügt, um die serialisierte Größe zu reduzieren.

Wenn Sie dieselbe Schnittstelle wie json verwenden (Bereitstellung von Lasten, Dumps), können Sie einen Drop-In-Ersatz für die Json-Serialisierung bereitstellen.

Dieselbe Logik kann erweitert werden, um jede automatische, nicht triviale Serialisierung hinzuzufügen, wie z. B. datetime-Objekte.


EDIT Ich habe einen generischen, modularen Parser geschrieben, der dies und mehr ausführt . https://github.com/someones/jaweson


Mein Code lautet wie folgt:

np_json.py

from json import *
import json
import numpy as np
import base64

def to_json(obj):
    if isinstance(obj, (np.ndarray, np.generic)):
        if isinstance(obj, np.ndarray):
            return {
                '__ndarray__': base64.b64encode(obj.tostring()),
                'dtype': obj.dtype.str,
                'shape': obj.shape,
            }
        Elif isinstance(obj, (np.bool_, np.number)):
            return {
                '__npgeneric__': base64.b64encode(obj.tostring()),
                'dtype': obj.dtype.str,
            }
    if isinstance(obj, set):
        return {'__set__': list(obj)}
    if isinstance(obj, Tuple):
        return {'__Tuple__': list(obj)}
    if isinstance(obj, complex):
        return {'__complex__': obj.__repr__()}

    # Let the base class default method raise the TypeError
    raise TypeError('Unable to serialise object of type {}'.format(type(obj)))


def from_json(obj):
    # check for numpy
    if isinstance(obj, dict):
        if '__ndarray__' in obj:
            return np.fromstring(
                base64.b64decode(obj['__ndarray__']),
                dtype=np.dtype(obj['dtype'])
            ).reshape(obj['shape'])
        if '__npgeneric__' in obj:
            return np.fromstring(
                base64.b64decode(obj['__npgeneric__']),
                dtype=np.dtype(obj['dtype'])
            )[0]
        if '__set__' in obj:
            return set(obj['__set__'])
        if '__Tuple__' in obj:
            return Tuple(obj['__Tuple__'])
        if '__complex__' in obj:
            return complex(obj['__complex__'])

    return obj

# over-write the load(s)/dump(s) functions
def load(*args, **kwargs):
    kwargs['object_hook'] = from_json
    return json.load(*args, **kwargs)


def loads(*args, **kwargs):
    kwargs['object_hook'] = from_json
    return json.loads(*args, **kwargs)


def dump(*args, **kwargs):
    kwargs['default'] = to_json
    return json.dump(*args, **kwargs)


def dumps(*args, **kwargs):
    kwargs['default'] = to_json
    return json.dumps(*args, **kwargs)

Sie sollten dann Folgendes tun können:

import numpy as np
import np_json as json
np_data = np.zeros((10,10), dtype=np.float32)
new_data = json.loads(json.dumps(np_data))
assert (np_data == new_data).all()
4
Rebs

Msgpack hat die beste Serialisierungsleistung: http://www.benfrederickson.com/dont-pickle-your-data/

Verwenden Sie msgpack-numpy. Siehe https://github.com/lebedov/msgpack-numpy

Es installieren:

pip install msgpack-numpy

Dann:

import msgpack
import msgpack_numpy as m
import numpy as np

x = np.random.Rand(5)
x_enc = msgpack.packb(x, default=m.encode)
x_rec = msgpack.unpackb(x_enc, object_hook=m.decode)
1
thayne

Versuchen Sie traitschemahttps://traitschema.readthedocs.io/de/latest/

"Erstellen Sie ein serialisierbares, typgeprüftes Schema mit Traits und Numpy. Bei einem typischen Anwendungsfall werden mehrere Numpy-Arrays unterschiedlicher Form und Art gespeichert."

0
SemanticBeeng

Wenn es für Menschen lesbar sein muss und Sie wissen, dass dies ein numpy-Array ist:

import numpy as np; 
import json;

a = np.random.normal(size=(50,120,150))
a_reconstructed = np.asarray(json.loads(json.dumps(a.tolist())))
print np.allclose(a,a_reconstructed)
print (a==a_reconstructed).all()

Möglicherweise ist dies nicht der effizienteste, da die Array-Größen größer werden, funktioniert jedoch für kleinere Arrays.

0
Chris.Wilson