---
# System prepended metadata

title: Python Pandas DataFrame 基本：明細（1：N）のデータを `LEFT JOIN` する 2021-07-24
tags: [Python, LEFT JOIN, DataFrame, Pandas]

---

---
lang: ja-jp
breaks: true
---
# Python Pandas DataFrame 基本：明細（1：N）のデータを `LEFT JOIN` する 2021-07-24

基本のテーブルと明細のテーブルをSQLの`LEFT JOIN`と同様の動作となるように結合したい。
※pandasは`join`で結合した結果が明細の件数とならないため、かなり面倒くさい操作が必要となる。

明細の最大行数と同じになるように基本データの行数あらかじめ増やし、その後に結合し、結合結果から不要な行を削除するといった流れで処理を行っている。

```python=
import pandas as pd

path = "hoge.json"

df_json = pd.read_json(path);
if (df_json.search_all_count[0] == 0) :
    # ゼロ件のデータとなったのでこれ以降は読込不要なはず。
    break;

df_orders = pd.DataFrame(df_json.response.orders)
if df_orders.shape[0] > 0 :
    # データが取得出来た場合のみ
    df_order            = pd.DataFrame(df_orders.order.to_list())
    df_shipping         = pd.DataFrame(df_orders.shipping.to_list())
    
    ## 1:1 のデータは、単純に結合する。
    df = df_order .join(df_shipping         , how='left')
    
    # 明細を列に分割
    df_order_detail = pd.DataFrame(df_orders.order_detail.to_list())
    
    # 明細リストの最大数を取得
    lstCount = df_order_detail.shape[1]
    
    # 明細リストを行に変換
    df_order_detail = pd.DataFrame(df_order_detail.values.ravel())
    
    # pd.json_normalizeで正規化する為、nullをディクショナリに置換
    df_order_detail = df_order_detail.where(df_order_detail.isnull() == False, {})
    # 
    df_order_detail = pd.json_normalize(df_order_detail[0])

    # 明細リストの最大数を掛けて行を増やす
    if lstCount > 0 :
        df = pd.concat([df] * lstCount, ignore_index=False, sort=False)

    # インデックスを降り直し
    df = df.sort_index()
    df = df.reset_index()
    
    # 明細リストを結合
    df = df.join(df_order_detail)
    
    # 明細リスト部分がnullの行を削除
    if 'product_no' in df.columns :
        df = df[df["product_no"].isnull() == False]
    if 0 in df.columns :
        df = df[df[0].isnull() == False]
    
    # 元々のindexを削除
    df = df.drop(columns=['index'])
    if 0 in df.columns :
        df = df.drop(columns=[0])
    
    # 完成
    display(df)
```

###### tags: `Pandas` `Python` `DataFrame` `LEFT JOIN`
