Comment sérialiser un tableau numpy tout en préservant les dimensions de la matrice?
numpy.array.tostring
ne semble pas préserver les informations sur les dimensions de la matrice (voir cette question ), exigeant de l'utilisateur d'émettre un appel à numpy.array.reshape
.
y a-t-il un moyen de sérialiser un tableau numpy au format JSON tout en préservant cette information?
Note: les tableaux peuvent contenir des ints, des flotteurs ou des bools. Il est raisonnable de s'attendre à un tableau transposé.
Note 2: ceci est fait avec l'intention de passer le réseau de numpy à travers une topologie de Tempête en utilisant streamparse, au cas où de telles informations finissent par être pertinentes.
7 réponses
pickle.dumps
ou numpy.save
encodent toutes les informations nécessaires à la reconstruction d'un tableau NumPy arbitraire, même en présence de problèmes d'endianthèse, de tableaux non contigus, ou de tuples dtypes bizarres. Les questions d'endianess sont probablement les plus importantes; vous ne voulez pas que array([1])
devienne soudainement array([16777216])
parce que vous avez chargé votre réseau sur une machine big-endian. pickle
est probablement l'option la plus commode, bien que save
a ses propres avantages, donnés dans le npy
justification du format .
l'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
utilise un format binaire, et il a besoin d'écrire dans un fichier, mais vous pouvez contourner cela avec StringIO
:
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
et de desérialiser:
memfile = StringIO.StringIO()
memfile.write(json.loads(serialized).encode('latin-1'))
memfile.seek(0)
a = numpy.load(memfile)
EDIT: comme on peut le lire dans les commentaires de la question Cette solution traite des tableaux vides" normaux " (flotteurs, bols, etc.)...) et non avec des matrices structurées multi-types.
Solution de sérialisation d'un tableau numpy de toutes les dimensions et les types de données
autant que je sache, vous ne pouvez pas simplement sérialiser un tableau numpy avec n'importe quel type de données et n'importe quelle dimension...mais vous pouvez stocker son type de données, dimension et information dans une représentation de liste et ensuite sérialiser en utilisant JSON.
importations nécessaires :
import json
import base64
Pour l'encodage , vous pouvez utiliser ( nparray
est un tableau numpy de tout type de données et de toute dimension):
json.dumps([str(nparray.dtype), base64.b64encode(nparray), nparray.shape])
après cela vous obtenez un dump (chaîne) de vos données, contenant une représentation de liste de son type de données et la forme ainsi que le tableaux de données / contenus codés selon la base64.
et pour le décodage ceci fait le travail ( encStr
est la chaîne codée JSON, chargée de quelque part):
# 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 sont efficaces et Cross-compatibles pour de nombreuses raisons, mais juste prendre JSON conduit à des résultats inattendus si vous voulez stocker et charger des tableaux vides de n'importe quel type et n'importe quelle dimension .
cette solution stocke et charge des tableaux numpy quel que soit le type ou la dimension et la restaure correctement (type de données, dimension, ...)
j'ai moi-même essayé plusieurs solutions il y a plusieurs mois et c'était la seule solution efficace et polyvalente que j'ai trouvée.
j'ai trouvé le code dans Msgpack-num PY utile. https://github.com/lebedov/msgpack-numpy/blob/master/msgpack_numpy.py
j'ai légèrement modifié le DCT sérialisé et ajouté l'encodage base64 pour réduire la taille sérialisée.
en utilisant la même interface que json (fournissant la(Les) charge(s), dump (S)), vous pouvez fournir un remplacement direct pour la sérialisation JSON.
cette même logique peut être étendue pour ajouter toute sérialisation automatique non triviale, comme les objets datetime.
modifier J'ai écrit un analyseur Générique, modulaire, qui fait ceci et plus. https://github.com/someones/jaweson
mon code est le suivant:
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)
vous devriez pouvoir alors faire ce qui suit:
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()
si elle doit être lisible par un humain et que vous savez qu'il s'agit d'un numpy array:
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()
peut-être pas le plus efficace comme les tailles de tableau plus grand, mais fonctionne pour les tableaux plus petits.
Msgpack a les meilleures performances de sérialisation: http://www.benfrederickson.com/dont-pickle-your-data /
utilisez msgpack-numpy. Voir https://github.com/lebedov/msgpack-numpy
Installer:
pip install msgpack-numpy
puis:
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)
Try traitschema
https://traitschema.readthedocs.io/en/latest /
"Créer serializable, type vérifié sur le schéma à l'aide de traits et de Numpy. Un cas d'utilisation typique implique l'économie de plusieurs tableaux Numpy de forme et de type variables."