PythonユーザーがJSONファイルを渡す時の話

Fringe81 Advent Calendar 2019、12日目の記事です。

今回は（僕みたいな）Pythonしか触ったことない人が実務で必要になるであろう小ネタを書きます。タイトルの通り、データをPythonでゴニョゴニョした後、フロントエンジニアの人にJSONファイルでデータを渡す時の話です。

# データの準備

例えば新入社員の自己紹介用の画面を作りたい！という時に、以下のような形でデータが欲しいと言われたとします（それぞれのデータはCSVファイルみたいなので個別にあったとします）。

```
interface Member {
    id: string;
    name: string;
    profile: {
        age: number;
        height: number;
        weight: number:
    };
    likes: {
        name: string;
        reason: string;
    }[];
}[];
```

これをPythonしか使えない新米はpandasで整理してアウトプットできないかと考えます。まずはデータを準備します。


```python
import pandas as pd

# 以下のデータは別々にファイルが存在し、それをpandasで読み込んだ状態と一致するものとする
df1 = pd.DataFrame(
    {
        "id": ["01", "02", "03"],
        "name": ["Aさん", "Bさん", "Cさん"]
    }
)
df2 = pd.DataFrame(
    {
        "id": ["01", "02", "03"],
        "age": [22, 24, 27],
        "height": [177, 171, 167],
        "weight": [64, 57, 62]
    }
)
df3 = pd.DataFrame(
    {
        "id": ["01", "02", "03"],
        "name": ["寿司・ゴルフ", "野球・カメラ", "アヒル・村人・戦士"],
        "reason": ["美味いから・スカッとするから", "盛り上がるから・記憶に残したいから", "可愛いから・親切だから・カッコいいから"]
    }
)
```

データの中身はこんな感じです。


```python
df1
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>id</th>
      <th>name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>01</td>
      <td>Aさん</td>
    </tr>
    <tr>
      <th>1</th>
      <td>02</td>
      <td>Bさん</td>
    </tr>
    <tr>
      <th>2</th>
      <td>03</td>
      <td>Cさん</td>
    </tr>
  </tbody>
</table>
</div>




```python
df2
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>id</th>
      <th>age</th>
      <th>height</th>
      <th>weight</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>01</td>
      <td>22</td>
      <td>177</td>
      <td>64</td>
    </tr>
    <tr>
      <th>1</th>
      <td>02</td>
      <td>24</td>
      <td>171</td>
      <td>57</td>
    </tr>
    <tr>
      <th>2</th>
      <td>03</td>
      <td>27</td>
      <td>167</td>
      <td>62</td>
    </tr>
  </tbody>
</table>
</div>




```python
df3
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>id</th>
      <th>name</th>
      <th>reason</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>01</td>
      <td>寿司・ゴルフ</td>
      <td>美味いから・スカッとするから</td>
    </tr>
    <tr>
      <th>1</th>
      <td>02</td>
      <td>野球・カメラ</td>
      <td>盛り上がるから・記憶に残したいから</td>
    </tr>
    <tr>
      <th>2</th>
      <td>03</td>
      <td>アヒル・村人・戦士</td>
      <td>可愛いから・親切だから・カッコいいから</td>
    </tr>
  </tbody>
</table>
</div>



# データの整形

続いて、データの整形をしていきます。とりあえず形を整えつつ、一つのデータフレームにまとめます。


```python
# df2の整形
# "profile"というカラムに辞書にまとめたものを入力
df2["profile"] = df2.apply(lambda row: {"age": row["age"], "height": row["height"], "weight": row["weight"]}, axis=1)
df2 = df2[["id", "profile"]]

# df3の整形
# 「・」で区切られているテキストを分割し、リスト型に内包させる
# "likes"というカラムに辞書にまとめたものを入力
df3["name"] = df3["name"].str.split("・")
df3["reason"] = df3["reason"].str.split("・")
df3["likes"] = df3.apply(lambda row: [{"name": name, "reason": reason} for name, reason in zip(row["name"], row["reason"])], axis=1)
df3 = df3[["id", "likes"]]

# データの結合
df = df1.merge(df2, on="id").merge(df3, on="id")
```

中身はこんな感じです。


```python
df
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>id</th>
      <th>name</th>
      <th>profile</th>
      <th>likes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>01</td>
      <td>Aさん</td>
      <td>{'age': 22, 'height': 177, 'weight': 64}</td>
      <td>[{'name': '寿司', 'reason': '美味いから'}, {'name': '...</td>
    </tr>
    <tr>
      <th>1</th>
      <td>02</td>
      <td>Bさん</td>
      <td>{'age': 24, 'height': 171, 'weight': 57}</td>
      <td>[{'name': '野球', 'reason': '盛り上がるから'}, {'name':...</td>
    </tr>
    <tr>
      <th>2</th>
      <td>03</td>
      <td>Cさん</td>
      <td>{'age': 27, 'height': 167, 'weight': 62}</td>
      <td>[{'name': 'アヒル', 'reason': '可愛いから'}, {'name': ...</td>
    </tr>
  </tbody>
</table>
</div>



# データの出力

最後にデータをJSONファイルに書き出します。


```python
import json

# アウトプット用のdictを作成
# orientはリスト内の入れ子構造をそのまま保存できるように設定（ここの引数が重要）
output = df.to_dict(orient="records")

# 保存先のファイルを開いてデータを記入。
with open("introduction.json", "w") as f:
    # indentはファイルの中身が見やすいように設定
    # ensure_asciiは日本語の文字化けが起きないように設定
    json.dump(output, f, indent=4, ensure_ascii=False)
```

ちなみ中身はこんな感じになってるはずです。


```python
output
```




    [{'id': '01',
      'name': 'Aさん',
      'profile': {'age': 22, 'height': 177, 'weight': 64},
      'likes': [{'name': '寿司', 'reason': '美味いから'},
       {'name': 'ゴルフ', 'reason': 'スカッとするから'}]},
     {'id': '02',
      'name': 'Bさん',
      'profile': {'age': 24, 'height': 171, 'weight': 57},
      'likes': [{'name': '野球', 'reason': '盛り上がるから'},
       {'name': 'カメラ', 'reason': '記憶に残したいから'}]},
     {'id': '03',
      'name': 'Cさん',
      'profile': {'age': 27, 'height': 167, 'weight': 62},
      'likes': [{'name': 'アヒル', 'reason': '可愛いから'},
       {'name': '村人', 'reason': '親切だから'},
       {'name': '戦士', 'reason': 'カッコいいから'}]}]



以上です。これとは別に、dict型だけでまとめた方がやり易いかもしれませんが、今回はpandasのデータフレームで読み込んだものをそのまま変換する方法を書きました。

# 最後に

最後まで読んでいただきありがとうございました。次回はデザイナーの[mirinrin](https://qiita.com/mirinrin)です！引き続きFringe81 Advent Calendar 2019をお楽しみください。