madokaのブログ

勉強したことのoutput先として使ってます。内容はpythonがらみが多いかもです。

numpyをjson.dumpするときに気をつけたいこと

numpyの数値をほかのintやfloat型と同様にjson dumpしようとして、できたりできなかったりすることがあった。

どうやらfloat64はできるのにint64はできないらしい。

どの型ならそのままdumpできるのか

どの型ならそのままdumpできるのか、testしてみました。

testに使うコードは下記。

import numpy as np 
import json

def json_dump(array):
    with open("./sample.json", 'w') as f:
        json.dump({ i: e for i, e in enumerate(array)}, f, indent=4)
    return "success"

int32,int64,float43,float64でためしてみる。

>>> json_dump(np.array([0,1,1], dtype=np.int32))

TypeError: Object of type 'int32' is not JSON serializable

>>> json_dump(np.array([0,1,1], dtype=np.int64))

TypeError: Object of type 'int64' is not JSON serializable

>>> json_dump(np.array([0,1,1], dtype=np.float32))

TypeError: Object of type 'float32' is not JSON serializable

>>> json_dump(np.array([0,1,1], dtype=np.float64))

'success'

float64以外は全滅。。。

解決方法

json dumpにエンコーダーを入れ込む。今回はint,floatだけ扱ったが、ndarrayも同様にそのままではlistとして扱えず、シリアライズできないというエラーが発生する。numpyの数値をそのままjsonにしたいと思ったら下記のclassを作って入れ込むのが良さそうだ。

class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        elif isinstance(obj, np.ndarray):
            return obj.tolist()

        return super(NumpyEncoder, self).encode(obj)

ちなみに

numpyの配列はpythonのlistとは違って、配列の要素を全て同じ型で扱う。配列作成時の特に指定がない場合は配列の要素のデータの型が一番大きいものに合わせるので、おもしろいことに下記のように要素の一つに小数点を入れるだけで型がガラッと変わるのだ。

>>> np.array([0,0,0]).dtype, np.array([0.0,0,0]).dtype
(dtype('int64'), dtype('float64'))

まとめ

むしろnumpyのfloat64が普通にdumpできてしまうことが素晴らしいと思った。数値計算などに使うことが想定されたライブラリだから、int64を扱うことの方がレアだから、対応がされてないのかなとか思ったり。