Was ist der effizienteste Weg, ein numpy Array mit simplejson zu serialisieren?SimpleJSON- und NumPy-Array
Antwort
I simplejson.dumps(somearray.tolist())
als die bequem Ansatz verwenden würde (wenn ich noch simplejson
überhaupt verwendet wurde, die mit Python 2.5 oder früher stecken impliziert werden; 2.6 und später ein Standard-Bibliotheksmodul json
haben, die auf die gleiche Weise funktioniert Natürlich würde ich das verwenden, wenn die Python-Version in Benutzung es unterstützt ;-).
Auf der Suche nach mehr Effizienz, Sie konnte Unterklasse json.JSONEncoder (in json
, ich weiß nicht, ob die älteren simplejson
bereits angeboten solche Anpassungsmöglichkeiten) und im default
Verfahren, Sonderfall Instanzen numpy.array
durch sie "in der Zeit" in Listen oder Tupel verwandeln. Ich bezweifle jedoch, dass Sie durch einen solchen Ansatz in Bezug auf die Leistung genug gewinnen würden, um den Aufwand zu rechtfertigen.
JSONEncoders Standardmethode muss ein serialisierbares Objekt zurückgeben, so dass es dasselbe sein wird Rückgabe von 'somearray.tolist()'. Wenn Sie etwas schneller wollen, müssen Sie es Element für Element selbst codieren. –
Dies zeigt, wie aus einem 1D NumPy Array zu JSON und zurück in ein Array konvertieren:
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])
Aufbauend auf tlausch's answer, hier ist ein Weg, um JSON kodieren ein NumPy Array unter Beibehaltung Form und Typ eines beliebigen NumPy-Arrays - einschließlich solcher mit komplexem dtype.
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"
Ich fand diesen JSON-Unterklasse-Code für die Serialisierung eindimensionaler numpy Arrays innerhalb eines Wörterbuchs. Ich habe es versucht und es funktioniert für mich.
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)
Mein Wörterbuch ist "Ergebnisse". Hier ist, wie ich in die Datei "data.json" schreiben:
j=json.dumps(results,cls=NumpyAwareJSONEncoder)
f=open("data.json","w")
f.write(j)
f.close()
Dieser Ansatz funktioniert auch, wenn Sie innerhalb eines Diktats ein numpy-Array verschachtelt haben. Diese Antwort (denke ich) implizierte, was ich gerade gesagt habe, aber es ist ein wichtiger Punkt. –
Das hat nicht für mich funktioniert. Ich musste 'return obj.tolist()' anstelle von 'return [x für x in obj]' verwenden. – nwhsvc
Ich bevorzuge die Verwendung von numpy's Objekt, um zu listen - es sollte schneller sein, wenn numpy durch die Liste iteriert, anstatt dass python durch iteriert. –
Verbesserung der On Russ Antwort, würde ich auch die np.generic scalars:
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)
Um diese dtype und Dimension zu halten versuchen:
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
return json.JSONEncoder(self, 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"
Unklar für mich, warum das nicht mehr aufgewertet ist! – tacaswell
Vereinbart, diese Lösung funktioniert im Allgemeinen für verschachtelte Arrays, IE ein Dictionary of Dictionary of Arrays. http://stackoverflow.com/questions/27909658/json-encoder-and-decoder-for-complex-numpy-arrays/27913569#27913569 –
Dies ist einer dieser versteckten aber wertvollen SO-Edelsteine, die Sie Stunden und Stunden spart der Arbeit. –
Wenn Sie Russ Methode zu n-dimensionalen numpy Arrays anwenden möchten, können Sie versuchen, diese
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)
Dies verwandelt einfach ein n-dimensionales Array in eine Liste von Listen mit der Tiefe "n". Um solche Listen wieder in ein numpliges Array umzuwandeln, funktioniert my_nparray = numpy.array(my_list)
unabhängig von der "Tiefe" der Liste.
Sie können auch diese Antwort mit nur einer Funktion in json.dumps
auf diese Weise übergeben:
json.dumps(np.array([1, 2, 3]), default=json_numpy_serializer)
Mit
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)))
validiert:
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)
Eine schnelle, wenn auch nicht wirklich Optimaler Weg ist die Verwendung Pandas:
import pandas as pd
pd.Series(your_array).to_json(orient='values')
entdeckte ich nur die Antwort von tlausch auf diese Frage und erkannte, dass es das für mein Problem fast richtige Antwort gibt, aber zumindest für mich nicht funktioniert in Python 3.5, wegen mehrerer Fehler: 1 - unendliche Rekursion 2 - die Daten wurden als None gespeichert
, da es nicht direkt auf der ursprünglichen Antwort kommentieren kann doch, hier ist meine 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"
[Verwandte] (http://stackoverflow.com/questions/11561932/why-does-json-dumpslistnp-arange5-Fehler-while-json-dumpsnp-arange5-tolis) a nd [einfache Lösung] (http://stackoverflow.com/questions/8230315/python-sets-are-not-json-serializable) durch explizite Übergabe eines [default handler] (http://docs.python.org/2 /library/json.html#json.dumps) für nicht serialisierbare Objekte. –
Noch eine andere Antwort hier: http://stackoverflow.com/questions/26646362/numpy-array-is-not-json-serializable/32850511#32850511 – travelingbones