pandasのDataFrameを結合する方法について解説します。
Contents
DataFrameの結合
データを分析する際にはデータソースとして、DBやcsvなどいろいろなデータソースからデータを取得して分析することがほとんどです。この時、各データを同じ意味を表すキー列をもとに結合してから分析することで様々な意味ある分析をすることができるようになるため、データをうまく結合していくことは非常に重要なデータ操作です。
pandasでは、DataFrameの結合方法としてmerge関数やjoinメソッドが用意されています。
以降では以下図に示したように製品一覧と関連する情報を結合する例を用いてDataFrameのmerge, joinを使用した結合例を紹介します。

この例では、各製品シリアル一覧と関連する品質情報実績、顧客情報、製品特徴のデータがあるものとします。
製造メーカーなどでは生産管理システムのマスタや製造実績、品質管理システムの品質実績など、いろいろなシステムのデータをつなげて分析したいといったことがよくあります。実際の現場で使われているデータはもっと複雑なデータが多いかと思いますが、上記のような簡単データで結合のイメージをつかんでもらおうと思います。
merge関数によるDataFrameの結合の基本
データベースの扱いに慣れている人であればわかる人が多いかと思いますが、データの関係性として「1対1」「多対1」「多対多」という関係性があります。以降ではそれぞれの関係性のデータを例に使いながら、merge関数を使用方法や動作を説明します。
1対1の結合

まずは、「1対1」の関係からです。上記の例で「serial_no」をキー列として見てみると、各レコード(各行)は1対1に対応していることがわかります。この例では製品一覧と品質情報をserial_noで結合することで、各製品シリアルの品質情報を一つの表として確認することができるようになります。
具体的にそれぞれのDataFrameを用意して、merge関数で結合する例を見てみましょう。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # 品質情報 quality_result = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'quality': [100, 200, 300, 400, 500] } ) print(f'製品一覧(product_list):\n{product_list}\n') print(f'品質実績(quality_result):\n{quality_result}\n') # 製品一覧と品質情報をマージ(1対1) result_df = pd.merge(product_list, quality_result) print(f'製品一覧と品質情報をマージ(1対1):\n{result_df}')
【実行結果】 製品一覧(product_list): serial_no customer_id product_code 0 A001 cid_1 p_a 1 B001 cid_1 p_b 2 C001 cid_2 p_c 3 C002 cid_2 p_c 4 D001 cid_3 p_d 品質実績(quality_result): serial_no quality 0 A001 100 1 B001 200 2 C001 300 3 C002 400 4 D001 500 製品一覧と品質情報をマージ(1対1): serial_no customer_id product_code quality 0 A001 cid_1 p_a 100 1 B001 cid_1 p_b 200 2 C001 cid_2 p_c 300 3 C002 cid_2 p_c 400 4 D001 cid_3 p_d 500
merge関数は、引数として結合するDataFrameを指定します。結果としては、結合されたDataFrameが返ってきます。merge関数は、自動的に1つ以上の同じ名前の列名を探してキーにして結合をしてくれます。今回の例では「serial_no」がキーになります。
多対1の結合

次に、「多対1」の関係性です。上記の例で「customer_id」をキー列として見てみると、製品一覧の複数のレコード(行)に1つの顧客情報のレコード(行)が対応しているものがあります。このような関係を「多対1」といいます。この例では製品一覧と顧客情報をcustomer_idで結合することで、製品一覧の各製品がどこの顧客の製品なのかを確認することができるようになります。
具体的にそれぞれのDataFrameを用意して、merge関数で結合する例を見てみましょう。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # 顧客情報 customer_info = pd.DataFrame( {'customer_id': ['cid_1', 'cid_2', 'cid_3'], 'customer_name': ['顧客1', '顧客2', '顧客3'] } ) print(f'製品一覧(product_list):\n{product_list}\n') print(f'顧客情報(customer_info):\n{customer_info}\n') # 製品一覧と顧客情報をマージ(多対1) result_df = pd.merge(product_list, customer_info) print(f'製品一覧と顧客情報をマージ(多対1):\n{result_df}')
製品一覧(product_list): serial_no customer_id product_code 0 A001 cid_1 p_a 1 B001 cid_1 p_b 2 C001 cid_2 p_c 3 C002 cid_2 p_c 4 D001 cid_3 p_d 顧客情報(customer_info): customer_id customer_name 0 cid_1 顧客1 1 cid_2 顧客2 2 cid_3 顧客3 製品一覧と顧客情報をマージ(多対1): serial_no customer_id product_code customer_name 0 A001 cid_1 p_a 顧客1 1 B001 cid_1 p_b 顧客1 2 C001 cid_2 p_c 顧客2 3 C002 cid_2 p_c 顧客2 4 D001 cid_3 p_d 顧客3
結果を見てみるとcustomer_idをキーとして、customer_name列を結合したDataFrameを取得できていることがわかるかと思います。
多対多の結合

次に、「多対多」の関係性です。上記の例で「product_code」をキーにして見てみると、製品一覧の複数のレコード(行)に複数の製品特徴のレコード(行)が対応しているものがあります。このような関係を「多対多」といいます。この例では製品一覧と製品特徴をproduct_codeで結合することで、製品一覧の各製品がどういった製品特徴を持っているかを確認できるようになります。
具体的にそれぞれのDataFrameを用意して、merge関数で結合する例を見てみましょう。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # 製品特徴 product_feature = pd.DataFrame( {'product_code': ['p_a', 'p_a', 'p_b', 'p_b', 'p_c', 'p_c', 'p_d', 'p_d', 'p_d'], 'feature': ['a-1', 'a-2', 'b-1', 'b-2', 'c-1', 'c-2', 'd-1', 'd-2', 'd-3'] } ) print(f'製品一覧(product_list):\n{product_list}\n') print(f'製品特徴(product_feature):\n{product_feature}\n') # 製品一覧と製品特徴をマージ(多対多) result_df = pd.merge(product_list, product_feature) print(f'製品一覧と製品特徴をマージ(多対多):\n{result_df}')
【実行結果】 製品一覧(product_list): serial_no customer_id product_code 0 A001 cid_1 p_a 1 B001 cid_1 p_b 2 C001 cid_2 p_c 3 C002 cid_2 p_c 4 D001 cid_3 p_d 製品特徴(product_feature): product_code feature 0 p_a a-1 1 p_a a-2 2 p_b b-1 3 p_b b-2 4 p_c c-1 5 p_c c-2 6 p_d d-1 7 p_d d-2 8 p_d d-3 製品一覧と製品特徴をマージ(多対多): serial_no customer_id product_code feature 0 A001 cid_1 p_a a-1 1 A001 cid_1 p_a a-2 2 B001 cid_1 p_b b-1 3 B001 cid_1 p_b b-2 4 C001 cid_2 p_c c-1 5 C001 cid_2 p_c c-2 6 C002 cid_2 p_c c-1 7 C002 cid_2 p_c c-2 8 D001 cid_3 p_d d-1 9 D001 cid_3 p_d d-2 10 D001 cid_3 p_d d-3
結果を見るとわかりますが、多対多の関係ではある製品シリアルのレコードが複数行に増えることがわかると思います。例えば、serial_no=D001の場合は、製品特徴が3レコード(行)あるので、結果も3行になります。
merge関数でキーを指定したDataFrameの結合
merge関数は、自動的に1つ以上の同じ名前の列名を探してキーとして結合をしてくれますが、実際の場面では結合したいキーは明確になっていることが多く、明示的に結合するキーを指定して結合することが多いかと思います。
以降では、結合するキー列を指定してDataFrameを結合する方法を説明します。
onでキーを指定して結合
merge関数でキー列を指定する場合は、以下の例のように「on」でキーの列名を指定します。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # 品質情報 quality_result = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'quality': [100, 200, 300, 400, 500] } ) print(f'製品一覧(product_list):\n{product_list}\n') print(f'品質実績(quality_result):\n{quality_result}\n') # onでキーを指定してマージ result_df = pd.merge(product_list, quality_result, on='serial_no') print(f'onでキー(serial_no)を指定してマージ\n{result_df}')
【実行結果】 製品一覧(product_list): serial_no customer_id product_code 0 A001 cid_1 p_a 1 B001 cid_1 p_b 2 C001 cid_2 p_c 3 C002 cid_2 p_c 4 D001 cid_3 p_d 品質実績(quality_result): serial_no quality 0 A001 100 1 B001 200 2 C001 300 3 C002 400 4 D001 500 onでキー(serial_no)を指定してマージ serial_no customer_id product_code quality 0 A001 cid_1 p_a 100 1 B001 cid_1 p_b 200 2 C001 cid_2 p_c 300 3 C002 cid_2 p_c 400 4 D001 cid_3 p_d 500
left_on, right_onでそれぞれのDataFrameのキーを指定して結合
データを結合する際には、結合する元のテーブルで意味は同じなのに異なる列名となっている場合がよくあります。このような場合には「left_on」「right_on」を使ってそれぞれのDataFrameにおけるキーの列名を指定します。
ここで左(left)と言っているのは、merge関数の第1引数に指定するDataFrameで、右(right)と言っているのはmerge関数の第2引数に指定するDataFrameです。
以下の例で見てみましょう。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # 品質情報 quality_result = pd.DataFrame( {'serial': ['A001', 'B001', 'C001', 'C002', 'D001'], 'quality': [100, 200, 300, 400, 500] } ) print(f'製品一覧(product_list):\n{product_list}\n') print(f'品質実績(quality_result):\n{quality_result}\n') # 左右のDataFrameのキーをそれぞれ指定してマージ result_df = pd.merge(product_list, quality_result, left_on='serial_no', right_on='serial') print(f'左右のDataFrameのキーをそれぞれ指定してマージ:\n{result_df}')
【実行結果】 製品一覧(product_list): serial_no customer_id product_code 0 A001 cid_1 p_a 1 B001 cid_1 p_b 2 C001 cid_2 p_c 3 C002 cid_2 p_c 4 D001 cid_3 p_d 品質実績(quality_result): serial quality 0 A001 100 1 B001 200 2 C001 300 3 C002 400 4 D001 500 左右のDataFrameのキーをそれぞれ指定してマージ: serial_no customer_id product_code serial quality 0 A001 cid_1 p_a A001 100 1 B001 cid_1 p_b B001 200 2 C001 cid_2 p_c C001 300 3 C002 cid_2 p_c C002 400 4 D001 cid_3 p_d D001 500
上記の例では、製品一覧ではシリアルナンバーは「serial_no」であるのに対して、品質情報では、「serial」となっており名前が異なっています。この場合は、merge関数を使用しても一致するキーがなく、自動ではうまく結合できません。
このような場合には「left_on=’serial_no’」「right_on=’serial’」としてそれぞれ結合するキー名称を指定することで指定列をキーにして結合できます。結果では、キー列は一つにまとめられるわけではなく複数列出てくる点も覚えておきましょう。
left_index, right_indexを指定してDataFrameのインデックスで結合
これまでの例では、DataFrameの列名を使ったmerge例を見てきました。しかし、場合によってはDataFrameのインデックスを用いて結合する場合もあると思います。このような場合には「left_index」「right_index」を指定することでDataFrameのインデックスを使用して結合することができます。
以下の例でみてみましょう。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # serial_no列をインデックスに設定 product_list = product_list.set_index('serial_no') # 品質情報 quality_result = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'quality': [100, 200, 300, 400, 500] } ) # serial_no列をインデックスに設定 quality_result = quality_result.set_index('serial_no') print(f'製品一覧(product_list):\n{product_list}\n') print(f'品質実績(quality_result):\n{quality_result}\n') # インデックスを使ってマージ result_df = pd.merge(product_list, quality_result, left_index=True, right_index=True) print(f'インデックスを使ってマージ:\n{result_df}')
【実行結果】 製品一覧(product_list): customer_id product_code serial_no A001 cid_1 p_a B001 cid_1 p_b C001 cid_2 p_c C002 cid_2 p_c D001 cid_3 p_d 品質実績(quality_result): quality serial_no A001 100 B001 200 C001 300 C002 400 D001 500 インデックスを使ってマージ: customer_id product_code quality serial_no A001 cid_1 p_a 100 B001 cid_1 p_b 200 C001 cid_2 p_c 300 C002 cid_2 p_c 400 D001 cid_3 p_d 500
この例で、上記までの例と違うのは、set_indexで’serial_no’列をインデックスとして設定しているところです。
インデックスを使用してマージする場合は、merge関数で「left_index=True」「right_index=True」と指定します。Trueを指定することでインデックスを使ってマージすることを明示しています。
joinメソッドを用いたインデックスでの結合
DataFrameでは、インデックスをキーとして結合を実行するためのjoinメソッドがデフォルトで用意されています。joinメソッドは以下のように使用します。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # serial_no列をインデックスに設定 product_list = product_list.set_index('serial_no') # 品質情報 quality_result = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'quality': [100, 200, 300, 400, 500] } ) # serial_no列をインデックスに設定 quality_result = quality_result.set_index('serial_no') print(f'製品一覧(product_list):\n{product_list}\n') print(f'品質実績(quality_result):\n{quality_result}\n') # joinメソッドを使ってインデックスで結合 result_df = product_list.join(quality_result) print(f'joinメソッドを使ってインデックスで結合:\n{result_df}')
【実行結果】 製品一覧(product_list): customer_id product_code serial_no A001 cid_1 p_a B001 cid_1 p_b C001 cid_2 p_c C002 cid_2 p_c D001 cid_3 p_d 品質実績(quality_result): quality serial_no A001 100 B001 200 C001 300 C002 400 D001 500 joinメソッドを使ってインデックスで結合: customer_id product_code quality serial_no A001 cid_1 p_a 100 B001 cid_1 p_b 200 C001 cid_2 p_c 300 C002 cid_2 p_c 400 D001 cid_3 p_d 500
joinメソッドは左側となるDataFrameのメソッドで呼び出して、結合する右側となるDataFrameを引数に指定します。結果はmerge関数で「left_index=True」「right_index=True」とした場合と同じになります。
mergeはpandasの関数ですので、pd.merge()というように呼び出すことができますが、joinはDataFrameのメソッドのためDataFrameのオブジェクトを使って実行します。そのため、pd.join()というような呼び出し方はできませんので注意してください。
left_on, right_on, left_index, right_indexの混在での結合
列を指定する「left_on」「right_on」、インデックスでの結合を指定する「left_index」「right_index」の使い方を見てきましたが、これらは同時に使用することができ、列とインデックスを混在させて結合することができます。
以下の例で見てみましょう。
import pandas as pd # 製品一覧 product_list = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'customer_id': ['cid_1', 'cid_1', 'cid_2', 'cid_2', 'cid_3'], 'product_code': ['p_a', 'p_b', 'p_c', 'p_c', 'p_d'] } ) # serial_no列をインデックスに設定 product_list = product_list.set_index('serial_no') # 品質情報 quality_result = pd.DataFrame( {'serial_no': ['A001', 'B001', 'C001', 'C002', 'D001'], 'quality': [100, 200, 300, 400, 500] } ) print(f'製品一覧(product_list):\n{product_list}\n') print(f'品質実績(quality_result):\n{quality_result}\n') # インデックスを使ってマージ(左:インデックスを使用、右:列名を指定) result_df = pd.merge(product_list, quality_result, left_index=True, right_on='serial_no') print(f'インデックスを使ってマージ(左:インデックス、右:列名)\n{result_df}')
【実行結果】 製品一覧(product_list): customer_id product_code serial_no A001 cid_1 p_a B001 cid_1 p_b C001 cid_2 p_c C002 cid_2 p_c D001 cid_3 p_d 品質実績(quality_result): serial_no quality 0 A001 100 1 B001 200 2 C001 300 3 C002 400 4 D001 500 インデックスを使ってマージ(左:インデックス、右:列名) customer_id product_code serial_no quality 0 cid_1 p_a A001 100 1 cid_1 p_b B001 200 2 cid_2 p_c C001 300 3 cid_2 p_c C002 400 4 cid_3 p_d D001 500
この例では、左側となる製品一覧はDataFrameのインデックスを、右側となる品質情報は、列名をキーにして結合しています。このようにインデックスと列を混在させて結合することも可能です。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。