【Python】Pandasで入れ子データを扱う方法!実践的な使い方を解説

はじめに

Pandasは、Pythonでデータ解析を行うためのライブラリの一つで、データを操作する上で非常に便利です。しかし、Pandasでは入れ子データを扱う場合に若干扱いにくい点があります。

本記事では、入れ子データを扱う方法について解説し、実際のプログラム例を交えて理解を深めていきます。

入れ子データの特徴と扱いについて

入れ子データとは、配列やオブジェクトの中に別の配列やオブジェクトが入っている形式のデータです。JSON形式のデータなどが代表的な入れ子データの例です。

入れ子データを扱う場合、Pandasのデータフレームに格納することができます。ただし、入れ子データをそのままデータフレームに格納すると、入れ子構造が維持されず、フラットな形式に変換されてしまいます。そのため、入れ子データを扱う場合は、適宜変換する必要があります。

入れ子データをフラットに変換する方法

入れ子データをフラットに変換する方法はいくつかありますが、ここでは以下のような方法を紹介します。

1. json_normalize関数を使う方法

json_normalize関数を使うと、JSON形式の入れ子データをフラットな形式に変換することができます。以下は、例としてJSON形式の入れ子データをフラットな形式に変換するコード例です。

import pandas as pd
import json
# JSON形式の入れ子データ
data = [
{"name": "Alice", "age": 20, "hobbies": ["reading", "traveling"]},
{"name": "Bob", "age": 25, "hobbies": ["sports", "cooking", "reading"]}
]
# JSON形式の入れ子データを文字列に変換
json_str = json.dumps(data)
# json_normalize関数を使ってフラットな形式に変換
df = pd.json_normalize(json.loads(json_str), sep="_")
print(df)

このコードでは、json.dumps関数を使ってJSON形式の入れ子データを文字列に変換し、json.loads関数を使って文字列をJSON形式のデータに変換しています。そして、pd.json_normalize関数を使ってJSON形式のデータをフラットな形式に変換しています。このとき、sep引数を指定することで、列名の区切り文字を指定することができます。

2. explode関数を使う方法

explode関数を使うと、リストやシリーズなどの要素を展開して新しい行を作ることができます。以下は、例として入れ子データをフラットな形式に変換するコード例です。

import pandas as pd
# 入れ子データを含むデータフレーム
df = pd.DataFrame({
"name": ["Alice", "Bob"],
"age": [20, 25],
"hobbies": [["reading", "traveling"], ["sports", "cooking", "reading"]]
})
# hobbies列を展開して新しい行を作る
df = df.explode("hobbies").reset_index(drop=True)
df["hobby_id"] = df.groupby(["name", "age"]).cumcount()
df = df.pivot(index=["name", "age"], columns="hobby_id", values="hobbies")
df.columns = [f"hobby_{i}" for i in df.columns]
df = df.reset_index()
print(df)

このコードでは、pandasのDataFrameにより入れ子データを含むデータフレームを作成し、explode関数を使ってhobbies列を展開して新しい行を作っています。その後、cumcount関数を使ってhobby_id列を作成し、pivot関数を使ってhobby_idを列名にしてデータフレームを再構築しています。

JSON形式の入れ子データをデータフレームに変換

JSON形式の入れ子データをデータフレームに変換する方法については、先程紹介したjson_normalize関数を使う方法があります。以下は、例としてJSON形式の入れ子データをデータフレームに変換するコード例です。

import pandas as pd
import json
# JSON形式の入れ子データ
json_data = """
[{
    "name": "Alice",
    "age": 20,
    "address": {
        "prefecture": "Tokyo",
        "city": "Shinjuku",
        "town": "Nishishinjuku"
        }
    },
{
    "name": "Bob",
    "age": 25,
    "address": {
    "prefecture": "Osaka",
    "city": "Chuo",
    "town": "Honmachi"
    }
}]
"""

# JSON形式の入れ子デ
# ータをデータフレームに変換
df = pd.json_normalize(json.loads(json_data), sep="_")
print(df)

このコードでは、json.loads関数を使ってJSON形式の入れ子データをPythonの辞書型に変換し、pd.json_normalize関数を使ってデータフレームに変換しています。

apply関数を用いた入れ子データの操作

入れ子データを扱う場合、apply関数を使って入れ子構造を保ったまま操作することができます。以下は、例として入れ子データを操作するコード例です。

import pandas as pd
# 入れ子データを含むデータフレーム
df = pd.DataFrame({
"name": ["Alice", "Bob"],
"age": [20, 25],
"hobbies": [["reading", "traveling"], ["sports", "cooking", "reading"]]
})

# apply関数を使って各行に対して操作を実行する
def concat_hobbies(row):
    return ", ".join(row["hobbies"])

df["hobbies_concatenated"] = df.apply(concat_hobbies, axis=1)
print(df)

このコードでは、pandasのDataFrameにより入れ子データを含むデータフレームを作成し、apply関数を使って各行に対してhobbies列の要素を結合してhobbies_concatenated列を作成しています。axis引数を指定することで、apply関数の適用方向を指定することができます。axis=1を指定することで、各行に対して操作を行うようにしています。

入れ子データを含むデータフレームの結合・操作方法

入れ子データを含むデータフレームの結合や操作方法については、通常のデータフレームと同様に扱うことができます。ただし、入れ子データを含む列を結合する場合は、列名が重複しないように気を付ける必要があります。

以下は、例として入れ子データを含むデータフレームを結合するコード例です。

import pandas as pd
# 入れ子データを含むデータフレーム1
df1 = pd.DataFrame({
"name": ["Alice", "Bob"],
"age": [20, 25],
"hobbies": [["reading", "traveling"], ["sports", "cooking", "reading"]]
})
# 入れ子データを含むデータフレーム2
df2 = pd.DataFrame({
    "name": ["Alice", "Bob"],
    "address": [{"prefecture": "Tokyo", "city": "Shinjuku", "town": "Nishishinjuku"},
    {"prefecture": "Osaka", "city": "Chuo", "town": "Honmachi"}]
})
# データフレームを結合する
df = pd.concat([df1, df2], axis=1)
print(df)

このコードでは、pandasのDataFrameにより入れ子データを含む2つのデータフレームを作成し、pd.concat関数を使って結合しています。axis引数を1にすることで、列方向に結合しています。

まとめ

Pandasを使って入れ子データを扱う場合、適切な方法を使ってフラットな形式に変換したり、apply関数を使って入れ子構造を保ったまま操作することができます。また、入れ子データを含むデータフレームを結合する場合は、列名が重複しないように注意が必要です。