Excelの vlookup関数やSQL文の join句のように、Python in Excel でも pandas のオブジェクト DataFrame を使用して、表・テーブルの結合が可能です。この記事では merge メソッドを使った結合方法について紹介します。
サンプルデータ
Anaconda の公式サイトにあるサンプルデータを使用して説明します。以下のページ内の「download here」のリンクからダウンロードできます。
以下のテーブルが定義されています。
- 「InternetSales」テーブル… 売上データ 60,398件
- 「ProductInfo」テーブル… 製品マスタ 197件
両テーブルに「ProductKey」列があり、結合のキーに使えそうです。但し「ProductInfo」テーブルには「InternetSales」テーブル内の一部の「ProductKey」は含まれていません。
merge メソッドの構文
DataFrame.merge(right [, 任意引数])
戻り値 | DataFrame |
引数 | 型 | 説明 |
---|---|---|
right (必須) | object | マージするDataFrameまたはSeries |
how | string | 選択肢: ・”left”(左外部結合) ・”right”(右外部結合) ・”outer”(完全外部結合) ・”inner”(内部結合) ・”cross”(交差結合) デフォルト:”inner” |
on | str or list | 結合する列またはインデックスのレベル名 指定しない場合は交差結合になる |
left_on | str or list | 左側のDataFrameで結合する列またはインデックスレベルの名前 |
right_on | str or list | 右側のDataFrameで結合する列またはインデックスレベルの名前 |
left_index | bool | 左側のDataFrameのインデックスを結合キーとして使用するか否か デフォルト:False |
right_index | bool | 右側のDataFrameのインデックスを結合キーとして使用するか否か デフォルト:False |
sort | bool | True … 結果のDataFrameで結合キーを辞書順に並べ替える False … 結合キーの順序は結合タイプ (how引数) によって決まる デフォルト:False |
suffixes | list | 左側と右側のDataFrameに重複する列名がある場合に 列名に追加する接尾辞 長さが2のシーケンス(タプルやリスト)を指定する デフォルト:(“_x”, “_y”) |
copy | bool | Falseの場合、可能であればコピーを避ける デフォルト:True |
indicator | bool or str | 各行の結合結果を表示する「_merge」列を出力するか否か 出力内容は以下の通り ・「left_only」…左のDataFrameのみに存在 ・「right_only」…右のDataFrameのみに存在 ・「both」…左右のDataFrameに存在 デフォルト:False ※文字列を渡した場合はその値が列名となる |
validate | str | マージキーが一意であるかチェックする (チェックに引っ掛かった場合はエラーが返る) 選択肢: ・「one_to_one」or「1:1」…マージキーが左右両方で一意 ・「one_to_many」or「1:m」…マージキーが左DataFrameで一意 ・「many_to_one」or「m:1」…マージキーが右DataFrameで一意 ・「many_to_many」or「m:m」…チェックは行われない |
サンプルコード
前準備
サンプルデータのブックにシートを追加し、A1セルに以下のコードを入力します。
# A1セル トランザクションテーブル(一部の列のみ取得)
sales = xl("InternetSales[#すべて]", headers = True)
sales = sales.reindex(columns=["SalesOrderNumber","SalesOrderLineNumber","OrderDate","ProductKey","OrderQuantity"])
# A2セル マスタ(一部の列のみ取得)
products = xl("ProductInfo[#すべて]", headers = True)
products = products.reindex(columns=["ProductKey", "ProductName"])
売上データ sales は 60398件、製品マスタ products は 197件です。列は適当に絞り込みました。
製品マスタは全ての ProductKey は網羅していません。また、製品マスタの197件中102件(製品)は売上データがありますが、残りの95件はありません。(売れてないってことです。)
Pythonの入力方法や出力種類、DataFrameの定義方法については、以下の記事で詳しく紹介しています。
結合の種類(how, on)
結合の種類とは、SQL文でいうところの join 句の種類と同じです。
以降の説明では、「左」はmergeメソッドを呼び出すオブジェクトを意味し、「右」はmergeメソッドの第1引数 right に指定するオブジェクトを意味します。
サンプルデータは以下のイメージです。
inner, left, right, outer
# A3セル
df1 = sales.merge(products, on="ProductKey", how="inner") # innerはデフォルト
# A4セル
df2 = sales.merge(products, on="ProductKey", how="left")
# A5セル
df3 = sales.merge(products, on="ProductKey", how="right")
# A6セル
df4 = sales.merge(products, on="ProductKey", how="outer")
結果 内部結合(左右のdataframeで結合キーが一致したデータ)54970件
結果 左外部結合(左の全てのデータと、左の結合キーと一致した右のデータ)60398件(=54970+5428)
結果 右外部結合(右の全てのデータと、右の結合キーと一致した左のデータ)55065件(=54970+95)
結果 完全外部結合(左右の全てのデータ)60493件(=5428+54970+95)
cross
# A7セル トランザクションテーブル(3397件)
sales2012 = xl("InternetSales[#すべて]", headers = True)
sales2012["OrderYear"] = sales2012["OrderDate"].dt.year
sales2012 = sales2012.query("OrderYear == 2012") # 2012年のデータのみ抽出
sales2012 = sales2012.reindex(columns=["SalesOrderNumber","SalesOrderLineNumber","OrderDate","ProductKey","OrderQuantity"])
# B7セル マスタ(197件)
products2012 = xl("ProductInfo[#すべて]", headers = True)
products2012 = products2012.reindex(columns=["ProductKey", "ProductName"])
# C7セル
df5 = sales2012.merge(products2012, how="cross")
結果 クロス結合 669,209件(=3397*197)
結合キーの指定
前セクションでは、引数 on に結合キーの列名を指定しました。左右のDataFrameで結合キーの名称が同じ場合は on を使用します。左右のDataFrameで結合キーの名称が異なる場合は、left_on, right_on, left_index, right_index を使用します。
left_on, right_on
# A9セル(製品マスタの「ProductKey」列を「ProductId」列にリネーム)
products2 = products.rename(columns={"ProductKey": "ProductId"})
# A10セル
df6 = sales.merge(products2, left_on="ProductKey", right_on="ProductId")
結果
left_index, right_index
# A12セル(ProductId列をインデックスに変更する)
products3 = products2.set_index("ProductId")
# A13セル
# right_index=True を指定すると、右側のdataframeのインデックスと結合される
df7 = sales.merge(products3, left_on="ProductKey", right_index=True)
# A14セル
# right_onにインデックス名を指定してもよい(結果は↑と同じ)
df8 = sales.merge(products3, left_on="ProductKey", right_on="ProductId")
結果
結合キーが複数ある場合
# A16セル
products4 = products3.copy()
products4.insert(1, "OrderQuantity", 1) # 列を追加
products4
# A17セル
# 結合キーが複数ある時はリストで渡す
df9 = sales.merge(products4, left_on=["ProductKey", "OrderQuantity"], right_on=["ProductId", "OrderQuantity"])
結果
並び順(sort)
# A19セル
df10 = sales.merge(products, on="ProductKey", sort=True)
結果 結合キーで並び変わる
重複する列名の接尾辞(suffixes)
# A21セル
products5 = products.reindex(columns=["ProductKey", "OrderQuantity", "ProductName"], fill_value=1)
# A22セル
# 引数suffixes未指定 重複する列名に「_x」「_y」のサフィックスが付く
df11 = sales.merge(products5, on="ProductKey")
# A23セル
# 引数suffixesを指定 重複する列名に「_left」「_right」のサフィックスが付く
df12 = sales.merge(products5, on="ProductKey", suffixes=['_left', '_right'])
結果
結合結果の表示(indicator)
# A27セル
df14 = sales.merge(products, on="ProductKey", how="outer", indicator=True)
# A28セル
df19 = sales.merge(products, on="ProductKey", how="outer", indicator="インジケータ")
結果 indicator を指定すると結合結果の列が表示される。True を指定すると「_merge」、文字列を指定するとその文字列が列名になる。
一意チェック(validate)
引数 validate を指定すると、結合キーが一意であるかをチェックし、チェックに引っ掛かった場合はエラーが返ってきます。
# A30セル(sales, productsの一意チェック エラーになるはず)
df15 = sales.merge(products, on="ProductKey", validate="1:1") # "one_to_one"でもOK
# A31セル(salesの一意チェック エラーになるはず)
df16 = sales.merge(products, on="ProductKey", validate="1:m") # "one_to_many"でもOK
# A32セル(productsの一意チェック エラーにならない)
df17 = sales.merge(products, on="ProductKey", validate="m:1") # "many_to_one"でもOK
# A33セル(左右どちらもチェックしない)
df18 = sales.merge(products, on="ProductKey", validate="m:m") # "many_to_many"でもOK
結果 A30、A31セルはエラーになる
結果 A32、A33セルはエラーにならず結合結果が返る
引数 copy
ちょっと謎な引数。
このメソッドでは、渡されたDataFrameをコピーしてから処理しているみたいです。copy=Falseと指定するとコピーしないで処理され、パフォーマンスやメモリ使用量が改善される可能性がある、とのこと。ただ、多くの場合はコピーを回避できないようです。
コメント