qtatsuの週報

初心者ですわぁ

pythonでJSON Linesを作る方法

前書き

Json Linesというのは以下のような形式です。

{"name":"reimu","score":1}
{"name":"marisa","score":1}

JSON Linesに解説されていますが

  1. UTF-8 Encodingであること
  2. 1行が1つのjsonオブジェクトであること
  3. 各行は\nで区切られていること
  4. (必須ではないが)拡張子はjsonlであること

そんなファイルです。 pysparkのデータ読み込みや、Elasticsearchのbulkでのデータ投入などで使ったりします。

このファイルをPythonを使って作成する方法を示しました。

参考リンク

環境

バージョン
MacOS Big Sur 11.1
Python3 3.9.1
pandas 1.1.4

Pythonのみを使う方法

以下のようなデータを用意します。

data_set =  [
 {'year': '2021', 'month': '01', 'day': '01', 'lang': 'Python', 'note': 'ぱいそん'},
 {'year': '2021', 'month': '01', 'day': '01', 'lang': 'Ruby', 'note': 'るびぃ'},
 {'year': '2021', 'month': '01', 'day': '01', 'lang': 'C++', 'note': 'しーぷらぷら'}
]

以下のように、リストの中身のオブジェクトを1行ずつ書き出します。

import json

with open('test.jsonl', mode='w', encoding='utf-8') as fout:
    for obj in data_set:
        json.dump(obj, fout, ensure_ascii=False)
        fout.write('\n')

上述の、JSON Linesのルールを守るために以下の設定を行っています。

  1. utf-8エンコーディングを確約するために、open関数でencodingを指定する。
  2. 1行が一つのjsonとなるように、for文を使いながら毎行、json.dumpを行っています。
  3. 各行を\nで区切っています。最終行の後にも付加されますが、それはどちらでもOKだとJSON Linesに明記されていました。
  4. 拡張子はjsonlにしています。

Pandasを使う方法

こちらの方が圧倒的に簡単です。

以下のようなデータを用意します(上の例と同じ)。

data_set =  [
 {'year': '2021', 'month': '01', 'day': '01', 'lang': 'Python', 'note': 'ぱいそん'},
 {'year': '2021', 'month': '01', 'day': '01', 'lang': 'Ruby', 'note': 'るびぃ'},
 {'year': '2021', 'month': '01', 'day': '01', 'lang': 'C++', 'note': 'しーぷらぷら'}
]

以下のように、DataFrameを作ります。

import pandas as pd

df = pd.DataFrame(data_set)
df
##    year month day    lang    note
## 0  2021    01  01  Python    ぱいそん
## 1  2021    01  01    Ruby     るびぃ
## 2  2021    01  01     C++  しーぷらぷら

書き出します。

df.to_json('test_pandas.jsonl', force_ascii=False, lines=True, orient='records')

force_asciiについては上述の通りです。 lines=Trueorient=recordsパラメータによって、jsonlの形式になります。このことは公式ドキュメント公式ドキュメントのlinesの部分に記述されています。

linesbool, default False If ‘orient’ is ‘records’ write out line delimited json format. Will throw ValueError if incorrect ‘orient’ since others are not list like.

lines=Trueがjsonを1行ずつ出力するjsonlフォーマットのオプションであり、このオプションを正しく動かすために、DataFrameのrowを行として扱うorient=recoresの設定が必要..というイメージだと思います。

出力したjsonlファイルのlint(壊れてないかチェック)

json.toolがコマンドラインインターフェイスを提供しています。利用しているPythonが3.8以上でしたら、jsonだけでなく、jsonlもlintできるオプション--json-linesが提供されています!

$ python3 -m json.tool --json-lines test.jsonl > /dev/null

jsonlの形式が壊れていたら教えてくれます。 /dev/nullは標準出力を捨てています。jsonlファイルの中身が表示されてしまうため、大きなファイルだと扱いづらいので指定しています。 (jsonの壊れている部分は標準エラーで出力されます)

結論

Pandasを利用できる環境ならPandasを使えば良いと思います。 そうでないならPythonjsonモジュールで頑張って書き出しましょう。

まとめ

もし、もっと簡単な方法などあればご教授いただけると嬉しいです! 間違いなどへのご指摘もいただけると幸いです。