python でクラスをJSONに保存する

# python のクラスをJSONで読み書きする
メンバ変数が非クラスの単純な変数だけで構成されたクラスであれば書き出し・読み込みが可能です
メンバ変数にクラスがあるような場合は素直に pickle を使いましょう

## 出力処理
vars(self) を json.dumps します

## 入力処理
self.\_\_dict\_\_ に対してJSONから読み込んだ変数を入力として update を呼び出します

## ソース

``` python

# numpy を使う時は下記エンコーダを json.dumps に渡す
class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)

class Hoge:
    def __init__(self):
        self.a = 0
        self.b = 100
        self.c = 1000
    def DumpJson(self):
        return json.dumps(vars(self), cls=NumpyEncoder)
    
    def LoadJson(self, jsonStr):
        params = json.loads(jsonStr)
        self.__dict__.update(params)

```

numpyだったメンバ変数はnumpyへ戻す処理が別途必要となりますので忘れないように注意します

## 使い方（書き出し）

``` python
model = Hoge()
jsonStr = model.DumpJson()
# jsonStr をファイル等に保存する
```

## 使い方（読み込み）

``` python
# jsonStr をファイル等から読み出す
jsonStr = "~~ JSON文字列 ~~"
model = Hoge()
model.LoadJson(jsonStr)
```


# 実際の使用例

[seeds_dataset.txt ダウンロード先](https://archive.ics.uci.edu/ml/datasets/seeds)
ダウンロード後に "\t\t" を "\t" に変換しないと正常動作しません

- Test1 で処理を実行しメンバ変数を設定します
- Test1で出力されたJSONテキストをコピーしてクリップボードに保存します
- Test1をコメントアウト
- Test2のjsonStr変数にJSON文字列を貼り付けてTest2のコメントを解除して実行

``` python3

import json
import numpy as np
import sys

class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)

class KMeans:
    def __init__(self, n_clusters=0, n_iterations=100, tol=1e-3):
        self.n_clusters = n_clusters
        self.n_iterations = n_iterations
        self.tol = tol
        self.means = None

    def initRandom(self, X):
        idxs = np.arange(len(X))
        np.random.shuffle(idxs)
        idxs = idxs[:self.n_clusters]
        return X[idxs]

    def eStep(self, X, means):
        predict = []
        for ix in X:
            a = means - ix
            a = a ** 2
            a = np.sum(a, axis=1)
            predict.append(np.argmin(a))
        return np.array(predict)

    def mStep(self, X, predict):
        means = []
        for c in range(self.n_clusters):
            idxs = np.where(predict == c)[0]
            ix = X[idxs]
            means.append(np.mean(ix, axis=0))
        return np.array(means)
    
    def calcMeansDistance(self, a, b):
        v = a - b
        v = v ** 2
        v = np.sum(v) / self.n_clusters
        return np.sqrt(v)
    
    def Fit(self, X):
        means = self.initRandom(X)
        for i in range(self.n_iterations):
            predict = self.eStep(X, means)
            newMeans = self.mStep(X, predict)
            distance = self.calcMeansDistance(means, newMeans)
            print("{}/{} {}".format(i, self.n_iterations, distance))
            means = newMeans
            if distance <= self.tol:
                break
        self.means = means
        return means
    
    def Predict(self, X):
        return self.eStep(X, self.means)
    
    def DumpJson(self):
        return json.dumps(vars(self), cls=NumpyEncoder)
    
    def LoadJson(self, jsonStr):
        params = json.loads(jsonStr)
        self.__dict__.update(params)

def Test1():
    data = np.loadtxt("./seeds_dataset.txt", delimiter="\t")
    xdata = data[:,0:7]
    ydata = data[:,7]
    model = KMeans(
        n_clusters=3,
        n_iterations=100,
        tol=1e-3
    )
    model.Fit(xdata)
    predict = model.Predict(xdata)
    print(predict)
    print(model.DumpJson())

def Test2():
    data = np.loadtxt("./seeds_dataset.txt", delimiter="\t")
    xdata = data[:,0:7]
    ydata = data[:,7]
    
    jsonStr = '{"n_clusters": 3, "n_iterations": 100, "tol": 0.001, "means": [[18.721803278688522, 16.297377049180326, 0.8850868852459014, 6.208934426229506, 3.7226721311475406, 3.603590163934426, 6.0660983606557375], [14.64847222222222, 14.460416666666658, 0.8791666666666667, 5.563777777777778, 3.277902777777778, 2.648933333333333, 5.192319444444446], [11.964415584415585, 13.274805194805198, 0.8522000000000004, 5.229285714285714, 2.8729220779220785, 4.759740259740259, 5.088519480519479]]}'
    model = KMeans()
    model.LoadJson(jsonStr)
    predict = model.Predict(xdata)
    print(predict)
    print(model.DumpJson())

def main():
    #Test1()
    Test2()
    

if __name__ == "__main__":
    main()

# python my_kmeans.py

```

以上です