python json numpy simplejson

python - SimpleJSON y matriz NumPy



(9)

Acabo de descubrir la respuesta de tlausch a esta pregunta y me di cuenta que da la respuesta casi correcta para mi problema, pero al menos para mí no funciona en Python 3.5, debido a varios errores: 1 - recursión infinita 2 - los datos se guardaron como Ninguno

como todavía no puedo comentar directamente la respuesta original, esta es mi versión:

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"

¿Cuál es la forma más eficiente de serializar una matriz numpy usando simplejson?


Encontré este código json de subclase para serializar matrices numpy unidimensionales dentro de un diccionario. Lo intenté y me funciona.

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)

Mi diccionario es ''resultados''. Así es como escribo en el archivo "data.json":

j=json.dumps(results,cls=NumpyAwareJSONEncoder) f=open("data.json","w") f.write(j) f.close()


Esto muestra cómo convertir de una matriz 1D NumPy a JSON y volver a una matriz:

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

Sobre la base de la respuesta de tlausch , aquí hay una forma de codificar JSON una matriz NumPy conservando la forma y el tipo de cualquier matriz NumPy, incluidas las de tipo complejo.

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"


Mejorando la respuesta de Russ, también incluiría los escalares np.generic :

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)


Para mantener el tipo y la dimensión, intente esto:

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"


Si quieres aplicar el método de Russ a matrices numpy n-dimensionales, puedes probar esto

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)

Esto simplemente convertirá una matriz n-dimensional en una lista de listas con profundidad "n". Para convertir estas listas en una matriz numpy, my_nparray = numpy.array(my_list) funcionará independientemente de la lista "profundidad".


También puede responder esto con solo una función que se pasa a json.dumps de esta manera:

json.dumps(np.array([1, 2, 3]), default=json_numpy_serializer)

Con

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

validado

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)


Una forma rápida, aunque no realmente óptima, es usar Pandas :

import pandas as pd pd.Series(your_array).to_json(orient=''values'')


simplejson.dumps(somearray.tolist()) como el enfoque más conveniente (si todavía simplejson usando simplejson , lo que implica estar atrapado con Python 2.5 o anterior; 2.6 y posterior tener un módulo de biblioteca estándar json que funciona de la misma manera, así que por supuesto usaría eso si la versión de Python en uso lo soporta ;-).

En una búsqueda por una mayor eficiencia, podría subclase json.JSONEncoder (en json ; no sé si el antiguo simplejson ya ofrecía tales posibilidades de personalización) y, en el método default , casos de casos especiales de numpy.array al convertirlos en una lista o tuplas "justo a tiempo". En cierto modo, dudo que ganes suficiente con ese enfoque, en términos de rendimiento, para justificar el esfuerzo.