Simplejson et NumPy array
Quelle est la façon la plus efficace de sérialiser un tableau numpy en utilisant simplejson?
9 réponses
j'utilise simplejson.dumps(somearray.tolist())
les plus pratique approche (si j'étais encore à l'aide de simplejson
du tout, ce qui implique d'être coincé avec Python 2.5 ou plus tôt; 2.6 et plus tard ont un module de bibliothèque standard json
qui fonctionne de la même manière, donc bien sûr je l'utiliserais si la version Python en cours d'utilisation le supportait;-).
en quête d'une plus grande efficacité, vous sous-classe json.JSONEncoderjson
; je ne sais pas si les plus âgés simplejson
déjà offert de telles possibilités de personnalisation) et, dans le default
méthode, cas spécial instances de numpy.array
en les transformant en list ou tuples "juste à temps". Je doute que vous y gagniez assez en termes de performance pour justifier l'effort.
afin de maintenir dtype et de la dimension essayez ceci:
import base64
import json
import numpy as np
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
"""If input object is an ndarray it will be converted into a dict
holding dtype, shape and the data, base64 encoded.
"""
if isinstance(obj, np.ndarray):
if obj.flags['C_CONTIGUOUS']:
obj_data = obj.data
else:
cont_obj = np.ascontiguousarray(obj)
assert(cont_obj.flags['C_CONTIGUOUS'])
obj_data = cont_obj.data
data_b64 = base64.b64encode(obj_data)
return dict(__ndarray__=data_b64,
dtype=str(obj.dtype),
shape=obj.shape)
# Let the base class default method raise the TypeError
super(JsonNumpy, self).default(obj)
def json_numpy_obj_hook(dct):
"""Decodes a previously encoded numpy ndarray with proper shape and dtype.
:param dct: (dict) json encoded ndarray
:return: (ndarray) if input was an encoded ndarray
"""
if isinstance(dct, dict) and '__ndarray__' in dct:
data = base64.b64decode(dct['__ndarray__'])
return np.frombuffer(data, dct['dtype']).reshape(dct['shape'])
return dct
expected = np.arange(100, dtype=np.float)
dumped = json.dumps(expected, cls=NumpyEncoder)
result = json.loads(dumped, object_hook=json_numpy_obj_hook)
# None of the following assertions will be broken.
assert result.dtype == expected.dtype, "Wrong Type"
assert result.shape == expected.shape, "Wrong Shape"
assert np.allclose(expected, result), "Wrong Values"
j'ai trouvé ce code de sous-classe json pour sérialiser des tableaux numpy unidimensionnels dans un dictionnaire. Je l'ai essayé et cela fonctionne pour moi.
class NumpyAwareJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, numpy.ndarray) and obj.ndim == 1:
return obj.tolist()
return json.JSONEncoder.default(self, obj)
Mon dictionnaire est "résultats". Voici comment j'écris au fichier "data.json":
j=json.dumps(results,cls=NumpyAwareJSONEncoder)
f=open("data.json","w")
f.write(j)
f.close()
ceci montre comment passer d'un tableau 1D de NumPy à JSON et revenir à un tableau:
try:
import json
except ImportError:
import simplejson as json
import numpy as np
def arr2json(arr):
return json.dumps(arr.tolist())
def json2arr(astr,dtype):
return np.fromiter(json.loads(astr),dtype)
arr=np.arange(10)
astr=arr2json(arr)
print(repr(astr))
# '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'
dt=np.int32
arr=json2arr(astr,dt)
print(repr(arr))
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
tlausch réponse ici est un moyen de JSON-encode un tableau NumPy tout en préservant la forme et dtype de n'importe quel NumPy array -- y compris ceux avec dtype complexe.
class NDArrayEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
output = io.BytesIO()
np.savez_compressed(output, obj=obj)
return {'b64npz' : base64.b64encode(output.getvalue())}
return json.JSONEncoder.default(self, obj)
def ndarray_decoder(dct):
if isinstance(dct, dict) and 'b64npz' in dct:
output = io.BytesIO(base64.b64decode(dct['b64npz']))
output.seek(0)
return np.load(output)['obj']
return dct
# Make expected non-contiguous structured array:
expected = np.arange(10)[::2]
expected = expected.view('<i4,<f4')
dumped = json.dumps(expected, cls=NDArrayEncoder)
result = json.loads(dumped, object_hook=ndarray_decoder)
assert result.dtype == expected.dtype, "Wrong Type"
assert result.shape == expected.shape, "Wrong Shape"
assert np.array_equal(expected, result), "Wrong Values"
si vous voulez appliquer la méthode de Russ à n-dimensional numpy arrays vous pouvez essayer ceci
class NumpyAwareJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, numpy.ndarray):
if obj.ndim == 1:
return obj.tolist()
else:
return [self.default(obj[i]) for i in range(obj.shape[0])]
return json.JSONEncoder.default(self, obj)
cela transformera simplement un tableau n-dimensionnel en une liste de listes avec la profondeur "n". De jeter ces listes dans un tableau numpy, my_nparray = numpy.array(my_list)
fonctionnera indépendamment de la liste "profondeur".
en améliorant la réponse de Russ, je voudrais également inclure le np.générique scalaires:
class NumpyAwareJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray) and obj.ndim == 1:
return obj.tolist()
elif isinstance(obj, np.generic):
return obj.item()
return json.JSONEncoder.default(self, obj)
Vous pouvez aussi répondre à cela avec juste une fonction passée en json.dumps
de cette manière:
json.dumps(np.array([1, 2, 3]), default=json_numpy_serializer)
import numpy as np
def json_numpy_serialzer(o):
""" Serialize numpy types for json
Parameters:
o (object): any python object which fails to be serialized by json
Example:
>>> import json
>>> a = np.array([1, 2, 3])
>>> json.dumps(a, default=json_numpy_serializer)
"""
numpy_types = (
np.bool_,
# np.bytes_, -- python `bytes` class is not json serializable
# np.complex64, -- python `complex` class is not json serializable
# np.complex128, -- python `complex` class is not json serializable
# np.complex256, -- special handling below
# np.datetime64, -- python `datetime.datetime` class is not json serializable
np.float16,
np.float32,
np.float64,
# np.float128, -- special handling below
np.int8,
np.int16,
np.int32,
np.int64,
# np.object_ -- should already be evaluated as python native
np.str_,
np.timedelta64,
np.uint8,
np.uint16,
np.uint32,
np.uint64,
np.void,
)
if isinstance(o, np.ndarray):
return o.tolist()
elif isinstance(o, numpy_types):
return o.item()
elif isinstance(o, np.float128):
return o.astype(np.float64).item()
# elif isinstance(o, np.complex256): -- no python native for np.complex256
# return o.astype(np.complex128).item() -- python `complex` class is not json serializable
else:
raise TypeError("{} of type {} is not JSON serializable".format(repr(o), type(o)))
validé:
need_addition_json_handeling = (
np.bytes_,
np.complex64,
np.complex128,
np.complex256,
np.datetime64,
np.float128,
)
numpy_types = tuple(set(np.typeDict.values()))
for numpy_type in numpy_types:
print(numpy_type)
if numpy_type == np.void:
# complex dtypes evaluate as np.void, e.g.
numpy_type = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))])
elif numpy_type in need_addition_json_handeling:
print('python native can not be json serialized')
continue
a = np.ones(1, dtype=nptype)
json.dumps(a, default=json_numpy_serialzer)
une façon rapide, mais pas vraiment optimale est d'utiliser Pandas:
import pandas as pd
pd.Series(your_array).to_json(orient='values')
je viens de découvrir la réponse de tlausch à cette Question et je me suis rendu compte qu'il donne la réponse presque correcte pour mon problème, mais au moins pour moi, il ne fonctionne pas en Python 3.5, en raison de plusieurs erreurs: 1 - une récursion infinie 2 - les données ont été sauvegardées comme aucune
comme je ne peux pas encore commenter directement la réponse originale, voici ma version:
import base64
import json
import numpy as np
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
"""If input object is an ndarray it will be converted into a dict
holding dtype, shape and the data, base64 encoded.
"""
if isinstance(obj, np.ndarray):
if obj.flags['C_CONTIGUOUS']:
obj_data = obj.data
else:
cont_obj = np.ascontiguousarray(obj)
assert(cont_obj.flags['C_CONTIGUOUS'])
obj_data = cont_obj.data
data_b64 = base64.b64encode(obj_data)
return dict(__ndarray__= data_b64.decode('utf-8'),
dtype=str(obj.dtype),
shape=obj.shape)
def json_numpy_obj_hook(dct):
"""Decodes a previously encoded numpy ndarray with proper shape and dtype.
:param dct: (dict) json encoded ndarray
:return: (ndarray) if input was an encoded ndarray
"""
if isinstance(dct, dict) and '__ndarray__' in dct:
data = base64.b64decode(dct['__ndarray__'])
return np.frombuffer(data, dct['dtype']).reshape(dct['shape'])
return dct
expected = np.arange(100, dtype=np.float)
dumped = json.dumps(expected, cls=NumpyEncoder)
result = json.loads(dumped, object_hook=json_numpy_obj_hook)
# None of the following assertions will be broken.
assert result.dtype == expected.dtype, "Wrong Type"
assert result.shape == expected.shape, "Wrong Shape"
assert np.allclose(expected, result), "Wrong Values"