numpy.array.tostring
は、行列の次元に関する情報を保持していないようです( この質問 を参照)。ユーザーにnumpy.array.reshape
。
この情報を保持しながら、numpy配列をJSON形式にシリアル化する方法はありますか?
注:配列には、int、float、またはboolを含めることができます。転置された配列を期待するのは合理的です。
注2:これは、そのような情報が関連するようになった場合に、streamparseを使用してStormトポロジにnumpy配列を渡す目的で行われています。
_pickle.dumps
_ または _numpy.save
_ エンディアンの問題、不連続な配列、または変なTuple dtypes。エンディアンの問題はおそらく最も重要です。ビッグエンディアンのマシンに配列をロードしたため、array([1])
が突然array([16777216])
にならないようにする必要があります。おそらくpickle
がより便利なオプションですが、save
には独自の利点があります。これは npy
format rationale で示されます。
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
_はバイナリ形式を使用し、ファイルに書き込む必要がありますが、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
_
そして、デシリアライズするには:
_memfile = StringIO.StringIO()
memfile.write(json.loads(serialized).encode('latin-1'))
memfile.seek(0)
a = numpy.load(memfile)
_
EDIT:質問のコメントを読むとわかるように、このソリューションは「通常の」numpy配列(float、int、bools ...)を扱い、マルチタイプの構造化配列ではありません。
任意の次元とデータ型のnumpy配列をシリアル化するためのソリューション
私が知る限り、numpy配列を任意のデータ型と次元で単純にシリアル化することはできません...しかし、そのデータ型、次元および情報をリスト表現に保存し、JSONを使用してシリアル化することができます。
インポートが必要:
import json
import base64
エンコードの場合を使用できます(nparray
は、任意のデータ型および任意の次元のnumpy配列です):
json.dumps([str(nparray.dtype), base64.b64encode(nparray), nparray.shape])
この後、データのJSONダンプ(文字列)を取得します。このデータには、そのデータ型と形状のリスト表現と、base64でエンコードされた配列data/contentsが含まれます。
そしてデコード用これは動作します(encStr
はどこかからロードされたエンコードされたJSON文字列です):
# 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ダンプは多くの理由で効率的で相互互換性がありますが、any typeおよびany dimensionのnumpy配列を格納およびロードする場合、JSONを取得するだけで予期しない結果になります。
このソリューションは、タイプまたはディメンションに関係なく、numpy配列を格納およびロードし、それを正しく復元します(データタイプ、ディメンション、...)
私は数ヶ月前に自分でいくつかのソリューションを試しましたが、これは私が出会った唯一の効率的で汎用性の高いソリューションでした。
Msgpack-numpyのコードが役に立ちました。 https://github.com/lebedov/msgpack-numpy/blob/master/msgpack_numpy.py
シリアル化された辞書を少し修正し、base64エンコードを追加して、シリアル化されたサイズを減らしました。
Jsonと同じインターフェイスを使用して(ロード、ダンプを提供)、jsonシリアル化のドロップイン置換を提供できます。
これと同じロジックを拡張して、日時オブジェクトなどの重要な自動シリアル化を追加できます。
[〜#〜] edit [〜#〜]これを行う汎用のモジュール式パーサーを作成しました。 https://github.com/someones/jaweson
私のコードは次のとおりです。
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)
その後、次のことができるはずです。
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()
Msgpackのシリアル化パフォーマンスは最高です: http://www.benfrederickson.com/dont-pickle-your-data/
Msgpack-numpyを使用します。 https://github.com/lebedov/msgpack-numpy を参照してください
インストールしてください:
pip install msgpack-numpy
次に:
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)
試してくださいtraitschema
https://traitschema.readthedocs.io/en/latest/
"特性とNumpyを使用して、シリアル化可能な型チェックスキーマを作成します。典型的な使用例では、さまざまな形状と型の複数のNumpy配列を保存します。"
人間が読めるようにする必要があり、これがnumpy配列であることを知っている場合:
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()
アレイのサイズが大きくなると、おそらく最も効率的ではありませんが、小さいアレイでは機能します。