json_normalizeとは?Python・pandasでJSONデータを表形式に変換する方法を徹底解説

「JSONデータをpandasのDataFrameに変換したい」
「ネストしたJSONを表形式にしたい」
「pandasのjson_normalizeの使い方がわからない」

このような悩みを解決するのが、pandasのjson_normalize()です。

json_normalize()を使うと、APIなどから取得した入れ子構造のJSON(ネストしたJSON)を、分析しやすいDataFrameへ変換できます。

本記事では、json_normalize()の基本的な使い方から、ネストしたJSON、record_pathmetasepなどの便利なオプション、エラー対策まで詳しく解説します。


json_normalizeとは?

json_normalize()は、Pythonのpandasに用意されている関数で、JSON形式の階層構造をフラットな表形式に変換するための機能です。

基本的には、次のように利用します。

import pandas as pd

df = pd.json_normalize(data)

JSONデータが次のような構造だったとします。

data = [
    {
        "id": 1,
        "name": "田中",
        "age": 30
    },
    {
        "id": 2,
        "name": "佐藤",
        "age": 25
    }
]

json_normalize()を使うと、

df = pd.json_normalize(data)

print(df)

以下のようなDataFrameになります。

   id name  age
0   1   田中   30
1   2   佐藤   25

つまり、JSONの各要素をDataFrameの行・列に変換できます。


json_normalizeの基本的な使い方

まずpandasをインポートします。

import pandas as pd

次にJSON形式のデータを用意します。

data = [
    {
        "id": 1,
        "name": "田中",
        "age": 30
    },
    {
        "id": 2,
        "name": "佐藤",
        "age": 25
    }
]

pd.json_normalize()に渡します。

df = pd.json_normalize(data)

print(df)

結果は次のようになります。

   id name  age
0   1   田中   30
1   2   佐藤   25

これだけでJSONからDataFrameへの変換ができます。


json_normalizeが便利な理由

通常のJSONであれば、pd.DataFrame()でもDataFrameに変換できます。

df = pd.DataFrame(data)

しかし、JSONが複雑な階層構造になっている場合、json_normalize()が便利です。

例えば次のJSONを考えてみましょう。

data = [
    {
        "id": 1,
        "name": "田中",
        "address": {
            "city": "東京",
            "zipcode": "100-0001"
        }
    }
]

このデータに対して、

df = pd.json_normalize(data)

とすると、次のようなDataFrameになります。

   id name address.city address.zipcode
0   1   田中     東京        100-0001

addressの中にあったcityzipcodeも展開されています。


json_normalizeでネストしたJSONを展開する

APIから取得するJSONでは、データが階層化されていることがよくあります。

例えば、

data = [
    {
        "id": 1,
        "name": "田中",
        "profile": {
            "age": 30,
            "gender": "男性"
        }
    },
    {
        "id": 2,
        "name": "佐藤",
        "profile": {
            "age": 25,
            "gender": "女性"
        }
    }
]

これを、

df = pd.json_normalize(data)

print(df)

とすると、

   id name  profile.age profile.gender
0   1   田中           30            男性
1   2   佐藤           25            女性

となります。

このように、json_normalize()はネストされた辞書を自動的に展開できます。


sepで列名の区切り文字を変更する

デフォルトでは、ネストされた項目の区切りに.が使われます。

例えば、

profile.age
profile.gender

という列名になります。

区切り文字はsepで変更できます。

df = pd.json_normalize(
    data,
    sep="_"
)

すると、

profile_age
profile_gender

のようになります。

例えば、

df = pd.json_normalize(data, sep="_")

print(df.columns)

結果:

Index(['id', 'name', 'profile_age', 'profile_gender'], dtype='object')

CSVなどに出力する場合にも、sep="_"は便利です。


深い階層のJSONをjson_normalizeで処理する

さらに複雑なJSONも考えてみましょう。

data = [
    {
        "id": 1,
        "name": "田中",
        "company": {
            "name": "ABC株式会社",
            "address": {
                "city": "東京",
                "country": "日本"
            }
        }
    }
]

json_normalize()を使うと、

df = pd.json_normalize(data)

以下のようになります。

   id name company.name company.address.city company.address.country
0   1   田中 ABC株式会社             東京                  日本

階層に応じて、

company.name
company.address.city
company.address.country

という列名が作られます。


record_pathとは?

json_normalize()で特に重要なのがrecord_pathです。

record_pathは、JSONの中にあるリストを指定して、DataFrameの行として展開するための引数です。

例えば、次のJSONを考えます。

data = [
    {
        "id": 1,
        "name": "田中",
        "orders": [
            {
                "product": "Python入門",
                "price": 3000
            },
            {
                "product": "Python応用",
                "price": 4000
            }
        ]
    }
]

ordersを表形式にしたい場合、

df = pd.json_normalize(
    data,
    record_path="orders"
)

print(df)

結果:

    product  price
0  Python入門   3000
1  Python応用   4000

このようにordersの配列をDataFrameの行に変換できます。


record_pathとmetaを組み合わせる

record_pathだけでは、親データの情報が失われることがあります。

例えば、

data = [
    {
        "id": 1,
        "name": "田中",
        "orders": [
            {
                "product": "Python入門",
                "price": 3000
            },
            {
                "product": "Python応用",
                "price": 4000
            }
        ]
    }
]

ここで、

record_path="orders"

とすると、idnameはDataFrameに含まれません。

そこでmetaを使用します。

df = pd.json_normalize(
    data,
    record_path="orders",
    meta=["id", "name"]
)

print(df)

結果:

    product  price id name
0  Python入門   3000  1  田中
1  Python応用   4000  1  田中

metaを使うことで、親階層にある情報を一緒に取得できます。


metaでネストした項目を指定する

metaにはネストした項目も指定できます。

例えば、

data = [
    {
        "id": 1,
        "user": {
            "name": "田中",
            "department": "営業部"
        },
        "orders": [
            {
                "product": "Python入門",
                "price": 3000
            }
        ]
    }
]

この場合、

df = pd.json_normalize(
    data,
    record_path="orders",
    meta=[
        "id",
        ["user", "name"],
        ["user", "department"]
    ]
)

とできます。


json_normalizeのerrors引数

JSONによっては、一部のデータに特定の項目が存在しない場合があります。

例えば、

data = [
    {
        "id": 1,
        "name": "田中"
    },
    {
        "id": 2
    }
]

このような場合です。

errors引数を使うと、欠損した項目がある場合の動作を指定できます。

df = pd.json_normalize(
    data,
    errors="ignore"
)

errors="ignore"を指定すると、存在しない項目があっても処理を継続できます。


json_normalizeでJSON APIのデータを処理する

json_normalize()は、Web APIから取得したJSONデータを処理するときにもよく利用されます。

例えばAPIから次のようなデータを取得したとします。

import requests
import pandas as pd

response = requests.get("https://example.com/api/users")

data = response.json()

df = pd.json_normalize(data)

print(df)

APIによっては、次のような複雑なJSONが返されます。

{
  "users": [
    {
      "id": 1,
      "name": "田中"
    },
    {
      "id": 2,
      "name": "佐藤"
    }
  ]
}

この場合は、

df = pd.json_normalize(data["users"])

とすることで、usersの配列をDataFrameに変換できます。


JSONファイルを読み込んでjson_normalizeを使う

JSONファイルをPythonで読み込む場合は、jsonモジュールを利用できます。

import json
import pandas as pd

with open("data.json", encoding="utf-8") as f:
    data = json.load(f)

df = pd.json_normalize(data)

print(df)

大量のJSONデータを分析するときにも便利な方法です。


json_normalizeとDataFrameの違い

単純なJSONであれば、pd.DataFrame()でも問題ありません。

df = pd.DataFrame(data)

一方、ネストしたJSONの場合は、

df = pd.json_normalize(data)

のほうが適しています。

方法 単純なJSON ネストしたJSON リストの展開
pd.DataFrame()
pd.json_normalize()

階層構造を持つJSONを扱うなら、まずjson_normalize()を検討するとよいでしょう。


json_normalizeでよくあるエラー

KeyErrorが発生する

record_pathに指定したキーが存在しないと、エラーになることがあります。

例えば、

df = pd.json_normalize(
    data,
    record_path="orders"
)

としているのに、JSONにordersが存在しないケースです。

まず、

print(data)

などで実際のJSON構造を確認しましょう。


JSONの構造がデータごとに違う

例えば、あるデータには、

{
  "name": "田中",
  "address": {
    "city": "東京"
  }
}

がある一方で、別のデータには、

{
  "name": "佐藤"
}

しかない場合があります。

このようなJSONを扱う場合は、欠損値が発生することを前提に処理する必要があります。

json_normalize()では、存在しない項目がNaNになる場合があります。


json_normalizeで作成したDataFrameをCSVにする

DataFrameに変換した後は、CSVファイルとして保存できます。

df.to_csv(
    "output.csv",
    index=False,
    encoding="utf-8-sig"
)

これにより、Excelなどでも扱いやすいCSVファイルを作成できます。


json_normalizeの主な引数

json_normalize()では、さまざまな引数を利用できます。

代表的なものは以下の通りです。

引数 内容
data 変換するJSONデータ
record_path 行として展開するリスト
meta 親階層から取得する項目
meta_prefix metaの列名に付ける接頭辞
record_prefix レコードの列名に付ける接頭辞
errors エラー処理方法
sep 階層を区切る文字
max_level 展開する階層の深さ

max_levelで展開する階層を制限する

JSONが非常に深い階層になっている場合、max_levelを利用できます。

例えば、

df = pd.json_normalize(
    data,
    max_level=1
)

とすると、展開する階層を制限できます。

JSONをすべてフラット化するのではなく、必要な階層だけ展開したい場合に便利です。


json_normalizeを使うときのポイント

json_normalize()を使うときは、次の3点を意識するとよいでしょう。

1. まずJSONの構造を確認する

print(data)

JSONのどの階層にデータがあるかを確認します。

2. リストを展開するときはrecord_pathを使う

pd.json_normalize(
    data,
    record_path="items"
)

3. 親データも必要ならmetaを使う

pd.json_normalize(
    data,
    record_path="items",
    meta=["id", "name"]
)

この3つを理解するだけでも、かなり複雑なJSONを処理できるようになります。


json_normalizeまとめ

pandas.json_normalize()は、JSONデータをDataFrameへ変換するときに非常に便利な関数です。

特に、APIなどから取得したネスト構造のJSONをデータ分析に利用するときに活躍します。

基本的な使い方は、

import pandas as pd

df = pd.json_normalize(data)

です。

さらに、

pd.json_normalize(
    data,
    record_path="items",
    meta=["id", "name"],
    sep="_"
)

のようにすれば、リストの展開、親データの取得、列名の変更なども行えます。

「JSONをDataFrameにしたい」ならjson_normalize()、「ネストしたJSONを表形式に整理したい」なら特にjson_normalize()を覚えておくと便利です。

PythonでAPI連携やデータ分析、スクレイピングなどを行う場合は、ぜひ使いこなしてみてください。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら