pandasでJSON文字列・ファイルを読み込み(read_json)

Posted: | Tags: Python, pandas, JSON

pandas.read_json()関数を使うと、JSON形式の文字列(str型)やファイルをpandas.DataFrameとして読み込むことができる。JSON Lines(.jsonl)にも対応している。

pandas.DataFrameとして読み込んでしまえば、もろもろのデータ分析はもちろん、to_csv()メソッドでcsvファイルとして保存したりもできるので、pandas.DataFrameを介してJSONファイルをCSVファイルに簡単に変換できて便利。

ここでは以下の内容について説明する。そのほかの引数については上記の公式ドキュメントを参照。

  • pandas.read_json()の基本的な使い方
    • JSON形式の文字列を読み込み
    • JSON形式のファイルを読み込み
      • 圧縮ファイルを読み込み: 引数compression
  • 形式を指定: 引数orient
  • JSON Lines(.jsonl)を読み込み
  • JSON文字列・ファイルの一部を読み込み

Python標準ライブラリのjsonモジュールでJSONファイルや文字列を辞書として読み込む方法は以下の記事を参照。

辞書やリストからなるオブジェクトをpandas.DataFrameに変換するにはpandas.io.json.json_normalize()を使う。

そのほかpandasでのcsvファイル、Excelファイルの読み書き(入出力)については以下の記事を参照。

pandas.read_jsonの基本的な使い方

例として使う文字列、ファイルは以下の記事で作成したもの。

JSON形式の文字列を読み込み

pandas.read_json()関数の第一引数にJSON形式の文字列を渡すと、文字列がpandas.DataFrameに変換される。

import pandas as pd
import json

s = '{"col1":{"row1":1,"row2":2,"row3":3},"col2":{"row1":"a","row2":"x","row3":"\u3042"}}'

df_s = pd.read_json(s)

print(df_s)
#       col1 col2
# row1     1    a
# row2     2    x
# row3     3    あ

元の文字列のUnicodeエスケープシーケンス\uXXXXは対応する文字に変換される。

なお、JSON文字列内の引用符はダブルクォート"でなければならない。シングルクォート'の場合はエラー(ValueError)となる。

s_single_quote = "{'col1':{'row1':1,'row2':2,'row3':3},'col2':{'row1':'a','row2':'x','row3':'\u3042'}}"

# df_s_single_quote = pd.read_json(s_single_quote)
# ValueError: Expected object or value

シングルクォート'が使われた文字列の場合、文字列メソッドreplace()でシングルクォート'をダブルクォート"に置換すればOK。

print(pd.read_json(s_single_quote.replace("'", '"')))
#       col1 col2
# row1     1    a
# row2     2    x
# row3     3    あ

JSON形式のファイルを読み込み

pandas.read_json()関数の第一引数にJSON形式のファイルのパスを渡すと、ファイルがpandas.DataFrameとして読み込まれる。

df_f = pd.read_json('data/src/sample_from_pandas_columns.json')

print(df_f)
#       col1 col2
# row1     1    a
# row2     2    x
# row3     3    あ

元のファイルのUnicodeエスケープシーケンス\uXXXXは対応する文字に変換される。

文字列の場合と同様、JSONファイル内で使われる引用符はダブルクォート"でなければならない。

圧縮ファイルを読み込み: 引数compression

pandasのバージョン0.21.0から引数compressionが追加され、'gzip', 'bz2', 'zip', 'xz'を指定することで圧縮ファイルを直接読み込めるようになった。

拡張子が.gz, .bz2, .zip, .xzの場合はcompression='infer'とすると対応する圧縮方式が自動的に選ばれる。

df_gzip = pd.read_json('data/src/sample_from_pandas_columns.gz', compression='infer')

print(df_gzip)
#       col1 col2
# row1     1    a
# row2     2    x
# row3     3    あ

なお、単独のファイルが圧縮されたもののみが対象で、複数ファイルがまとめられたzipは読み込めない。

形式を指定: 引数orient

JSONの中身をどのようにpandas.DataFrameの行ラベルindex、列ラベルcolumns、値valuesに割り当てるかという形式には以下の種類がある。

  • 'split'
    • {index -> [index], columns -> [columns], data -> [values]}
  • 'records'
    • [{column -> value}, ... , {column -> value}]
  • 'index'
    • {index -> {column -> value}}
  • 'columns'(デフォルト)
    • {column -> {index -> value}}
  • 'values'
    • [values]

実際の例は以下の記事を参照されたい。

読み込む文字列やファイルの形式と引数orientで指定する形式が違っていると、行と列が入れ替わったり、エラーになったりするので注意。

df_s_index = pd.read_json(s, orient='index')

print(df_s_index)
#      row1 row2 row3
# col1    1    2    3
# col2    a    x    あ

# df_s_split = pd.read_json(s, orient='split')
# ValueError: JSON data had unexpected key(s): col2, col1

JSON Lines(.jsonl)を読み込み

JSON Lines(.jsonl)はJSONが改行で区切られたフォーマット。

引数orient='records'の場合に、さらに引数lines=Trueとすると、pandas.read_json()でJSON Lines(.jsonl)を読み込める。

s_jsonl = '''{"col1":1,"col2":"a"}
{"col1":2,"col2":"x"}
{"col1":3,"col2":"\u3042"}'''

print(s_jsonl)
# {"col1":1,"col2":"a"}
# {"col1":2,"col2":"x"}
# {"col1":3,"col2":"あ"}

df_s_jsonl = pd.read_json(s_jsonl, orient='records', lines=True)

print(df_s_jsonl)
#    col1 col2
# 0     1    a
# 1     2    x
# 2     3    あ

JSONLファイルを読み込む場合は、上の例と同様に、第一引数にファイルのパスを指定すればよい。

JSON文字列・ファイルの一部を読み込み

実際にWeb APIなどで取得できるJSONにはpandas.DataFrameとして読み込みたいデータ以外の情報も付加されているので、そのままpandas.read_json()を適用できない場合が多い。

そのような場合は、以下のような流れで読み込むことが可能。もっといいやり方があるかもしれない。

  1. JSON文字列・ファイルを標準ライブラリのjsonモジュールのjson.loads(), json.load()で辞書として読み込む
  2. 辞書から読み込みたい部分を抽出
  3. 抽出した部分をjson.dumps()で文字列に変換
  4. 文字列をpandas.read_json()に渡す

json.loads()json.dumps()についての詳細は以下の記事を参照。

以下のネストしたJSON文字列を例とする。

s_nested = '{"OTHER": "x", "DATA": {"col1":{"row1":1,"row2":2},"col2":{"row1":"a","row2":"x"}}}'

これをそのままpandas.read_json()に渡すと以下のようになってしまう。

print(pd.read_json(s_nested))
#                             DATA OTHER
# col1      {'row1': 1, 'row2': 2}     x
# col2  {'row1': 'a', 'row2': 'x'}     x

まずjson.loads()で辞書に変換する。ファイルを読み込む場合はjson.load()

d = json.loads(s_nested)

print(d)
# {'OTHER': 'x', 'DATA': {'col1': {'row1': 1, 'row2': 2}, 'col2': {'row1': 'a', 'row2': 'x'}}}

print(type(d))
# <class 'dict'>

辞書からpandas.DataFrameとして読み込みたい部分を抽出。ネストが深い場合は[キー名][キー名]のように繰り返す。

d_target = d['DATA']

print(d_target)
# {'col1': {'row1': 1, 'row2': 2}, 'col2': {'row1': 'a', 'row2': 'x'}}

print(type(d_target))
# <class 'dict'>

json.dumps()で文字列に変換。

s_target = json.dumps(d_target)

print(s_target)
# {"col1": {"row1": 1, "row2": 2}, "col2": {"row1": "a", "row2": "x"}}

print(type(s_target))
# <class 'str'>

pandas.read_json()に渡す。形式に応じた引数orientを指定する。例はデフォルト(orient='columns')。

df_target = pd.read_json(s_target)

print(df_target)
#       col1 col2
# row1     1    a
# row2     2    x

まとめて書いてもOK。

df_target2 = pd.read_json(json.dumps(json.loads(s_nested)['DATA']))

print(df_target2)
#       col1 col2
# row1     1    a
# row2     2    x

読み込みたい部分がorient='records'(辞書のリスト)形式の場合はpandas.io.json.json_normalize()を使って辞書のリストを直接pandas.DataFrameに変換できる。

詳細は以下の記事を参照。

関連カテゴリー

関連記事