Pandasで複数列をキーにしたデータ結合(merge)完全ガイド

はじめに:Pandasのmergeとは

Pandasは、Pythonでデータ分析を行う上で欠かせないライブラリです。その中でもmerge関数は、異なるデータフレーム(表形式のデータ構造)を、共通のキーとなる列に基づいて結合するための非常に強力なツールです。

SQLにおけるJOIN操作と同様に、Pandasのmerge関数を使うことで、複数のデータセットを効率的に統合し、より包括的な分析を行うことができます。例えば、顧客情報と購買履歴のデータフレームを、顧客IDをキーにして結合することで、顧客ごとの購買傾向を分析したり、製品情報と販売実績のデータフレームを製品IDで結合して、製品別の売れ筋を把握したりすることが可能です。

この記事では、特に「複数列をキーにしたmerge」に焦点を当て、その基本的な使い方から応用的なテクニックまで、具体例を交えながらわかりやすく解説していきます。複数列をキーに指定することで、より複雑な条件でのデータ結合が可能となり、より高度なデータ分析を実現できます。

複数列をキーにしたmergeの基本

Pandasのmerge関数で複数列をキーとして指定するには、on引数にキーとなる列名のリストを渡します。基本的な構文は以下のようになります。

import pandas as pd

# データフレームの準備 (例)
df1 = pd.DataFrame({'ID': [1, 2, 3, 4], '日付': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02'], '値1': [10, 20, 30, 40]})
df2 = pd.DataFrame({'ID': [1, 2, 3, 4], '日付': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02'], '値2': [100, 200, 300, 400]})

# 複数列をキーにしてmerge
merged_df = pd.merge(df1, df2, on=['ID', '日付'])

print(merged_df)

この例では、df1df2という2つのデータフレームを、ID日付の2つの列をキーにして結合しています。on=['ID', '日付']とすることで、ID日付の両方の値が一致する行同士が結合されます。

ポイント:

  • on引数には、結合キーとして使用する列名のリストを指定します。
  • on引数に指定した列名は、両方のデータフレームに存在する必要があります。
  • on引数に指定した列の値が完全に一致する場合にのみ、行が結合されます。

複数列をキーにすることで、より詳細な条件でデータを結合することが可能になり、データ分析の幅が広がります。例えば、上記の例では、同じIDでも日付が異なれば別の行として扱われます。

次のセクションでは、具体的なサンプルデータを使って、複数列をキーにしたmergeの動作をより詳しく見ていきましょう。

サンプルデータの準備

実際にmerge関数を試すために、まずはサンプルとなるデータフレームを2つ作成します。ここでは、ある商品の販売データと在庫データを想定したデータフレームを用意します。

販売データ (sales_data)

import pandas as pd

sales_data = pd.DataFrame({
    '商品ID': [101, 102, 101, 103, 102, 104],
    '販売日': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03'],
    '販売数量': [5, 10, 3, 7, 2, 8],
    '店舗ID': ['A', 'B', 'A', 'B', 'A', 'B']
})

print("販売データ:")
print(sales_data)

在庫データ (inventory_data)

inventory_data = pd.DataFrame({
    '商品ID': [101, 102, 103, 104, 101, 102],
    '在庫日': ['2023-01-01', '2023-01-01', '2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02'],
    '在庫数': [100, 50, 75, 25, 97, 48],
    '店舗ID': ['A', 'B', 'B', 'B', 'A', 'B']
})

print("\n在庫データ:")
print(inventory_data)

これらのデータフレームは、商品ID販売日 (または在庫日)販売数量 (または在庫数)店舗IDの4つの列で構成されています。sales_dataは商品の販売に関する情報、inventory_dataは商品の在庫に関する情報を持っています。

商品ID日付店舗IDをキーにしてこれらのデータフレームを結合することで、特定の商品が特定の日付に特定の店舗でどれだけ販売され、どれだけ在庫があったかといった情報を、一つのデータフレームで確認できるようになります。

次のセクションでは、これらのサンプルデータを使って、実際にmerge関数を実行し、複数列をキーにしたデータ結合を体験してみましょう。

複数列指定のmergeを実行する

前のセクションで準備したサンプルデータを使って、merge関数を実際に実行してみましょう。ここでは、sales_datainventory_dataを、商品ID販売日 (または在庫日)、店舗IDの3つの列をキーにして結合します。

import pandas as pd

# 前のセクションで定義したsales_dataとinventory_dataをここに記述

merged_data = pd.merge(sales_data, inventory_data,
                      left_on=['商品ID', '販売日', '店舗ID'],
                      right_on=['商品ID', '在庫日', '店舗ID'],
                      how='inner')

print("\n結合後のデータ:")
print(merged_data)

このコードでは、pd.merge関数を使用しています。

  • sales_dataが左側のデータフレーム、inventory_dataが右側のデータフレームとして指定されています。
  • left_on引数とright_on引数には、結合キーとして使用する列名のリストをそれぞれ指定します。ここでは、左側のデータフレーム(sales_data)の商品ID, 販売日, 店舗IDと、右側のデータフレーム(inventory_data)の商品ID, 在庫日, 店舗IDが対応するように指定しています。
  • how='inner'は、内部結合 (inner join) を行うことを指定しています。これは、両方のデータフレームに共通するキーを持つ行のみを結合することを意味します。

実行結果の解釈:

実行結果として得られるmerged_dataには、sales_datainventory_dataの両方に存在する商品ID日付店舗IDの組み合わせを持つ行の情報が含まれます。販売数量在庫数を同じ行で確認できるため、販売状況と在庫状況を組み合わせて分析することが可能になります。

補足:

  • left_onright_onに異なる列名を指定することで、列名が異なる場合でも結合できます。
  • how引数には、'inner'以外にも'left', 'right', 'outer'などの値を指定できます。これらの結合の種類については、次のセクションで詳しく解説します。

この例では、内部結合を使用しましたが、データ分析の目的によっては、他の種類の結合を使用する必要がある場合があります。

mergeの種類:inner, left, right, outer

pd.merge関数におけるhow引数は、データフレームの結合方法を指定する上で非常に重要な役割を果たします。how引数に指定できる主な値は、inner, left, right, outerの4種類で、それぞれ異なる結合の挙動を示します。

以下に、それぞれの結合方法について詳しく解説します。

1. inner (内部結合)

  • how='inner'を指定すると、両方のデータフレームに共通するキーを持つ行のみが結合されます。
  • SQLのINNER JOINと同様の動作です。
  • 片方のデータフレームにしか存在しないキーを持つ行は、結合結果から除外されます。
  • 前のセクションで使用した例が、このinner結合です。

例:

merged_data_inner = pd.merge(sales_data, inventory_data,
                             left_on=['商品ID', '販売日', '店舗ID'],
                             right_on=['商品ID', '在庫日', '店舗ID'],
                             how='inner')

2. left (左結合)

  • how='left'を指定すると、左側のデータフレームのすべての行が結合結果に含まれます。
  • 右側のデータフレームに一致するキーを持つ行が存在しない場合、右側のデータフレームの列はNaN (欠損値) で埋められます。
  • SQLのLEFT JOINまたはLEFT OUTER JOINと同様の動作です。

例:

merged_data_left = pd.merge(sales_data, inventory_data,
                            left_on=['商品ID', '販売日', '店舗ID'],
                            right_on=['商品ID', '在庫日', '店舗ID'],
                            how='left')

3. right (右結合)

  • how='right'を指定すると、右側のデータフレームのすべての行が結合結果に含まれます。
  • 左側のデータフレームに一致するキーを持つ行が存在しない場合、左側のデータフレームの列はNaNで埋められます。
  • SQLのRIGHT JOINまたはRIGHT OUTER JOINと同様の動作です。

例:

merged_data_right = pd.merge(sales_data, inventory_data,
                             left_on=['商品ID', '販売日', '店舗ID'],
                             right_on=['商品ID', '在庫日', '店舗ID'],
                             how='right')

4. outer (完全外部結合)

  • how='outer'を指定すると、両方のデータフレームのすべての行が結合結果に含まれます。
  • 片方のデータフレームにしか存在しないキーを持つ行については、存在しない側のデータフレームの列はNaNで埋められます。
  • SQLのFULL OUTER JOINと同様の動作です。

例:

merged_data_outer = pd.merge(sales_data, inventory_data,
                             left_on=['商品ID', '販売日', '店舗ID'],
                             right_on=['商品ID', '在庫日', '店舗ID'],
                             how='outer')

どの結合方法を選ぶべきか?

どの結合方法を選ぶかは、分析の目的によって異なります。

  • 共通のキーを持つデータのみに興味がある場合: inner
  • 左側のデータフレームのすべての情報を保持したい場合: left
  • 右側のデータフレームのすべての情報を保持したい場合: right
  • 両方のデータフレームのすべての情報を保持したい場合: outer

これらの結合方法を理解し、適切に使い分けることで、より柔軟なデータ分析が可能になります。

異なる列名でmergeする場合

データ分析の現場では、結合したい2つのデータフレームが、意味は同じでも列名が異なるという状況が頻繁に発生します。 Pandasのmerge関数は、このような場合にも柔軟に対応できます。

on引数ではなく、left_on引数とright_on引数を使用することで、異なる列名を指定してmergeを実行できます。

例:

import pandas as pd

# サンプルデータの作成
df1 = pd.DataFrame({
    'CustomerID': [1, 2, 3, 4],
    'Name': ['Alice', 'Bob', 'Charlie', 'David']
})

df2 = pd.DataFrame({
    '顧客ID': [1, 2, 5],
    'PurchaseAmount': [100, 200, 300]
})

# 異なる列名でmerge
merged_df = pd.merge(df1, df2,
                     left_on='CustomerID',
                     right_on='顧客ID',
                     how='left')

print(merged_df)

この例では、df1の顧客IDを表す列がCustomerIDdf2の顧客IDを表す列が顧客IDと異なっています。

left_on='CustomerID'right_on='顧客ID' を指定することで、それぞれのデータフレームでどの列を結合キーとして使用するかを明示的に指定できます。

how='left' を指定しているため、左側のデータフレーム (df1) のすべての行が含まれ、右側のデータフレーム (df2) に一致する顧客IDが存在しない場合は、PurchaseAmount の値が NaN となります。

ポイント:

  • left_on 引数には、左側のデータフレームで使用する列名を指定します。
  • right_on 引数には、右側のデータフレームで使用する列名を指定します。
  • left_onright_on に指定する列のデータ型は、一致している必要があります。
  • merge後のデータフレームには、結合に使用したキー列が両方残ります。片方だけ残したい場合は、不要な列を削除する必要があります。(例: merged_df = merged_df.drop('顧客ID', axis=1))

異なる列名を持つデータフレームをmergeする際は、left_onright_on を活用することで、柔軟なデータ結合を実現できます。

merge時の注意点:データ型と欠損値

Pandasのmerge関数を使用する際には、データ型と欠損値(NaN)に注意することが重要です。これらを適切に処理しないと、期待通りの結果が得られなかったり、エラーが発生したりする可能性があります。

1. データ型の一致

  • 結合キーとして使用する列のデータ型が、両方のデータフレームで一致している必要があります。
  • データ型が異なると、意図せず結合に失敗したり、誤った結果が出力されたりすることがあります。

例:

一方のデータフレームの商品ID列が数値型(int64)で、もう一方のデータフレームの商品ID列が文字列型(object)の場合、merge関数はこれらの値を異なるものとして扱い、期待通りに結合されないことがあります。

対処法:

  • astype()メソッドを使用して、データ型を統一します。

    df1['商品ID'] = df1['商品ID'].astype(str)
    df2['商品ID'] = df2['商品ID'].astype(str)
  • pd.to_numeric()関数を使用して、数値に変換できる列を数値型に変換します。

2. 欠損値 (NaN) の扱い

  • 結合キーとなる列に欠損値(NaN)が含まれている場合、merge関数の挙動に注意が必要です。
  • デフォルトでは、欠損値は他の欠損値とも一致しないと見なされるため、欠損値を持つ行同士は結合されません。

例:

df1df2の両方の顧客ID列に欠損値が含まれている場合、on='顧客ID'でmergeしても、これらの欠損値を持つ行同士は結合されません。

対処法:

  • 欠損値を何らかの適切な値で埋める(例:0, -1, ‘Unknown’など)。fillna()メソッドを使用します。
    df1['顧客ID'] = df1['顧客ID'].fillna(0)
    df2['顧客ID'] = df2['顧客ID'].fillna(0)
  • dropna()メソッドを使用して、欠損値を含む行を削除します。

    df1 = df1.dropna(subset=['顧客ID'])
    df2 = df2.dropna(subset=['顧客ID'])
  • 欠損値を明示的に結合キーとして扱いたい場合は、事前に欠損値を特定の文字列などに置換することを検討します。

3. 複合キーにおける欠損値

複数列をキーとしてmergeする場合、キーのいずれかの列に欠損値が含まれていると、意図しない結果になる可能性があります。 複合キーを構成するすべての列で欠損値の扱いを統一する必要があります。

まとめ:

merge関数を使用する際は、以下の点に注意してデータ型と欠損値を適切に処理しましょう。

  • 結合キーとなる列のデータ型が一致しているか確認する。
  • 欠損値の有無とその影響を考慮し、必要に応じて欠損値を処理する。
  • 複合キーの場合は、キーを構成するすべての列で欠損値の扱いを統一する。

これらの注意点を守ることで、より正確で信頼性の高いデータ結合を行うことができます。

実践的な応用例

Pandasのmerge関数は、様々なデータ分析の場面で活用できます。ここでは、より実践的な応用例をいくつか紹介します。

1. 顧客属性と購買履歴の結合

ECサイトの分析において、顧客の属性情報(年齢、性別、居住地など)と購買履歴データを結合することで、顧客セグメントごとの購買傾向を分析できます。

  • データフレーム:

    • customers (顧客属性): CustomerID, Age, Gender, Region
    • orders (購買履歴): OrderID, CustomerID, ProductID, OrderDate, Amount
  • 結合キー: CustomerID
  • 分析: 顧客セグメント(例:20代女性、東京都居住)ごとの購買金額の平均値、特定の商品を購入する顧客セグメントの特定など。

2. Webサイトのアクセスログと会員情報の結合

Webサイトのアクセスログと会員情報を結合することで、会員の行動履歴を詳細に分析できます。

  • データフレーム:

    • access_logs (アクセスログ): SessionID, UserID, PageURL, Timestamp
    • users (会員情報): UserID, RegistrationDate, MembershipLevel
  • 結合キー: UserID
  • 分析: 会員レベルごとのページ閲覧数、特定のページを閲覧した会員の属性、会員登録後の行動の変化など。

3. 複数ソースからのデータを集約

複数のシステムやデータベースからデータを取得し、それらをmerge関数で統合することで、より包括的な分析が可能になります。

  • 例:

    • POSシステムの売上データ
    • 顧客管理システムの顧客データ
    • Webサイトのアクセスログ
  • 結合キー: 顧客ID、商品ID、日付など、共通するキーを複数組み合わせる。
  • 分析: 顧客の購買行動とWebサイトの閲覧履歴を組み合わせた分析、店舗ごとの売上と顧客属性を組み合わせた分析など。

4. 時系列データの結合

異なる頻度で収集された時系列データを結合することで、より詳細な分析を行えます。例えば、日次の売上データと月次の広告費用データを結合し、広告費用と売上の相関関係を分析できます。

  • データフレーム:

    • daily_sales (日次売上): Date, ProductID, Sales
    • monthly_ad_spend (月次広告費用): Month, AdSpend
  • 結合キー: DateMonthを、それぞれのデータフレームで扱いやすい形に変換し、結合キーとして使用する。
  • 分析: 広告費用が売上に与える影響、季節変動による売上の変化など。

これらの例は、merge関数がデータ分析において非常に強力なツールであることを示しています。データの種類や分析の目的に応じて、適切な結合方法とキーを選択することで、様々なインサイトを得ることができます。

パフォーマンス改善のヒント

Pandasのmerge関数は非常に便利ですが、大規模なデータフレームを結合する場合、処理に時間がかかることがあります。ここでは、merge関数のパフォーマンスを改善するためのヒントをいくつか紹介します。

1. 結合キーのインデックス化

結合キーとして使用する列にインデックスを設定することで、検索速度が向上し、merge処理を高速化できます。特に大規模なデータフレームの場合に効果的です。

import pandas as pd

# データフレームの準備(例)

# 結合キーとなる列にインデックスを設定
df1 = df1.set_index('CustomerID')
df2 = df2.set_index('CustomerID')

# インデックスを使用してmerge
merged_df = pd.merge(df1, df2, left_index=True, right_index=True, how='left')

# 必要に応じてインデックスをリセット
merged_df = merged_df.reset_index()

ポイント:

  • set_index()メソッドでインデックスを設定します。
  • pd.merge()関数のleft_index=Trueright_index=True引数を使用することで、インデックスを結合キーとして指定します。
  • merge後にインデックスをリセットしたい場合は、reset_index()メソッドを使用します。

2. データ型の最適化

メモリ使用量を削減し、処理速度を向上させるために、データ型を最適化します。特に、object型(文字列型)の列は、メモリを消費しやすいので、category型や適切な数値型に変換することを検討します。

# 例: object型をcategory型に変換
df1['Region'] = df1['Region'].astype('category')

3. 不要な列の削除

merge処理の前に、不要な列を削除することで、メモリ使用量を削減し、処理速度を向上させることができます。

4. データのフィルタリング

merge処理の前に、必要なデータのみを抽出することで、処理対象のデータ量を減らすことができます。

5. Chunk処理

大規模なデータフレームを分割して、小さなチャンクごとにmerge処理を行うことで、メモリ使用量を抑え、処理を安定させることができます。

# 例: Chunk処理
chunk_size = 10000
for i in range(0, len(df1), chunk_size):
    df1_chunk = df1[i:i+chunk_size]
    merged_chunk = pd.merge(df1_chunk, df2, on='CustomerID')
    # 処理を行う

6. DaskやSparkなどの分散処理フレームワークの活用

Pandasだけでは処理しきれないほど大規模なデータフレームの場合は、DaskやSparkなどの分散処理フレームワークの利用を検討します。これらのフレームワークは、データを複数のノードに分散して処理することで、処理速度を大幅に向上させることができます。

7. mergeの種類を検討する

必要な情報のみを取得できる結合方法を選択することで、無駄な処理を減らすことができます。 例えば、inner結合で済む場合は、outer結合よりも効率的です。

これらのヒントを参考に、データ量や処理内容に合わせて、適切なパフォーマンス改善策を講じることで、merge関数をより効率的に活用できます。

まとめ:Pandas mergeの活用

この記事では、Pandasのmerge関数における複数列をキーにしたデータ結合について、基本的な使い方から応用的なテクニック、パフォーマンス改善まで幅広く解説しました。

主要なポイント:

  • merge関数は、SQLのJOIN操作と同様に、複数のデータフレームを効率的に結合するための強力なツールです。
  • on引数、left_on引数、right_on引数を使用することで、柔軟なキー指定が可能です。
  • how引数によって、inner, left, right, outerなどの様々な結合方法を選択できます。
  • データ型や欠損値の扱いに注意することで、正確な結合結果を得ることができます。
  • インデックスの活用、データ型の最適化、不要な列の削除などのテクニックにより、パフォーマンスを改善できます。

Pandas mergeの活用例:

  • 顧客属性と購買履歴を結合して、顧客セグメントごとの購買傾向を分析する。
  • Webサイトのアクセスログと会員情報を結合して、会員の行動履歴を詳細に分析する。
  • 複数のシステムからデータを集約して、より包括的な分析を行う。
  • 異なる頻度の時系列データを結合して、詳細な時系列分析を行う。

merge関数を使いこなすことで、様々なデータ分析の課題を解決し、ビジネス上の意思決定に役立つインサイトを得ることができます。

今後の学習:

  • より複雑な条件でのデータ結合
  • 複数のデータフレームの連続的な結合
  • pd.concat関数など、他のデータ結合手法との組み合わせ

merge関数は、Pandasのデータ分析機能の中でも特に重要な機能の一つです。この記事を参考に、merge関数を積極的に活用し、データ分析スキルを向上させてください。

投稿者 karaza

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です