pandasのDataFrameを結合する方法について解説します。
Contents
DataFrameの結合
データ分析の際には、データベースやCSVファイル等、様々なデータソースからデータを取得して分析することがほとんどです。この時、各データで同じ意味を表すキー列をもとに結合して分析できるようにすることは、データに対する深い知見を得るために必要不可欠です。
Pythonのデータ分析で使用されるpandasでは、DataFrameの結合方法としてmerge
関数やjoin
メソッドといった非常に便利な機能が提供されています。
以降では、以下図に示したような製品一覧と関連する情報を結合する例でmerge
関数やjoin
メソッドを用いたDataFrameの結合方法を紹介していきます。
上記例では、各製品シリアル一覧と関連する品質情報実績、顧客情報、製品特徴のデータがあるものとします。製造メーカー等では生産管理システムのマスタや製造実績、品質管理システムの品質実績など、様々なシステムのデータをつなげて分析したいといったことがよくあります。実際の現場で使われているデータはもっと複雑なデータが多いと思いますが、上記のような簡単データで結合のイメージをつかんでもらえればと思います。
同じ構造をもつデータを単純に連結する場合には、concat
を使うことができます。以下を参考にしていただければと思います。
merge関数によるDataFrameの結合の基本
データを結合する場合には、データの関係性をよく考慮することが重要です。データの関係に応じて「1対1」「多対1」「多対多」という関係性があります。以降では、それぞれの関係性のデータを使いながらmerge
関数を使用してデータを結合する方法を紹介します。
1対1の結合
上記図の例は「1対1」の関係があるデータです。「serial_no
」をキー列として見てみると、各レコード(各行)は1対1に対応していることがわかります。ここでは、製品一覧と品質情報をserial_no
をキーに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つの顧客情報のレコードが対応しています。ここでは、製品一覧と顧客情報をcustomer_id
をキーにして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
をキーにして製品一覧に顧客情報が結合されていることが分かります。製品一覧の方が多であるため、顧客情報は複製されて各行に結合されていることが分かるかと思います。
多対多の結合
上記図の例は「多対多」の関係があるデータです。「product_code
」をキー列としてみてみると、製品一覧の複数レコードに、複数の製品特徴のレコードが対応しているということが分かります。ここでは、製品一覧と製品特徴をproduct_code
をキーにして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
上記結果を見てみると、多対多の関係であるため、ある製品シリアルに関するレコードが複数行に増えていることが分かるかと思います。
上記で見てきた通り、merge
関数は自動的に1つ以上のキーを探して結合してくれます。データの関係性による結合の違いを見てもらうために自動でのキー結合をで結果を見てもらいましたが、実際のデータ分析の場面では、結合したいキーを明確に指定して結合することがほとんどです。以降では、merge
関数で、キーを指定してDataFrameを結合する方法を紹介していきます。
merge関数でキーを指定したDataFrameの結合
ここでは、merge
関数でキー列を指定して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
上記例では、明示的に"serial_no"
をキーとして結合しています。
この例では、それぞれのDataFrameが同一名の列を持っているため、簡単に結合できます。しかし、データ分析では同じ意味でも異なる列名であることがよくあります。この場合には、以降で説明するleft_on
やright_on
を使用します。
left_onとright_onでそれぞれのDataFrameのキーを指定する
データを結合する際には、それぞれの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"
」としてそれぞれ結合するキー名称を指定することで指定列をキーにして結合することができます。
上記結果を見ても分かるように、キー列は一つにまとめられるわけではなく複数列出てくる点も覚えておきましょう。もし、キー列が複数出てこないようにしたい場合は、事前にDataFrameの列名を揃えておく必要があります。
left_indexとright_indexを指定してDataFrameのインデックスで結合
これまでの例では、DataFrameの列名を使用したmerge
関数の実行例を見てきました。pandasのDataFrameでは、インデックスを持っているためインデックスを用いての結合をすることも可能です。
インデックスを使用して結合する場合には、以下の例のように「left_index=True
」「right_index=True
」を指定することで、インデックスを使用しての結合が可能です。
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"
列をインデックスとして登録していることに注意してください。
結果を見ると分かりますが「left_index=True
」「right_index=True
」を指定することで、インデックスを使った結合ができていることが分かるかと思います。
joinメソッドを用いたインデックスでの結合
merge
関数でインデックスを用いた結合の例を紹介しましたが、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
merge
は関数でしたが、join
についてはDataFrameのメソッドです。そのため、join
メソッドは、左側となるDataFrameでメソッドとして呼び出し、右側となるDataFrameを引数として指定します。結果としては、merge関数で「left_index=True
」「right_index=True
」とした場合と同じです。
left_on, right_on, left_index, right_indexの混在での結合
上記では、列を指定する「left_on
」「right_on
」、インデックスでの結合を指定する「left_index=True
」「right_index=True
」の使い方を見てきました。もちろん、これらは同時に使用することができ、列とインデックスを混在させて結合することができます。
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のインデックスを使用し、右側となる品質情報は、列名をキーとして結合しています。このようにインデックスや列名を混在させて、柔軟に結合することが可能です。
まとめ
pandasのDataFrameを結合する方法について解説しました。
pandasでは、DataFrameの結合方法としてmerge
関数やjoin
メソッドが用意されています。結合時には、1対1、多対1、多対多といったことを考慮して結合をすることが適切な結合のためには重要です。それぞれのパターンに関する結合を例を使って紹介しました。
また、merge
関数で結合する際のキーの指定は非常に重要です。列名を指定するon
、left_on
、right_on
やインデックスで結合するleft_index=True
、right_index=True
といった指定方法について説明しました。
データ分析では、異なるデータソースのデータを適切に結合できることが、データから深い知見を得るために必要不可欠です。是非、merge
関数やjoin
メソッドを適切に使用できるようになってデータ分析に活かしてください。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。