機械学習のためのデータ前処理:複数のデータを結合・集計する方法【Pandas入門】
はじめに
機械学習モデルを構築する際、質の高いデータを用意することが非常に重要です。データ前処理は、生データを分析やモデリングに適した形に変換する工程であり、モデルの性能に大きく影響します。これまでの記事では、欠損値の処理やカテゴリ変数の扱いなど、データそのものの整形方法について解説してきました。
しかし、ビジネスの現場では、必要なデータが単一のファイルやデータベースにまとまっていることは稀です。顧客情報、購買履歴、Webサイトのアクセスログなど、複数のデータソースから情報を集め、これらを組み合わせて分析や機械学習モデルの学習に利用することが一般的です。また、大量のデータをそのまま扱うのではなく、目的に応じて合計や平均などを計算し、データの粒度を調整することも頻繁に行われます。
この記事では、このように複数のデータを一つにまとめたり(結合)、データを要約したり(集計)する基本的な手法について解説します。Pythonのデータ分析ライブラリであるpandasを使用し、具体的なコード例とともにこれらの操作方法を学んでいきましょう。データ前処理の幅を広げ、より実践的なデータ加工ができるようになることを目指します。
データの結合
データ分析や機械学習のために、複数のデータソースから情報を組み合わせる必要はよくあります。例えば、顧客の基本情報が記載されたデータと、その顧客の購買履歴が記載されたデータがあるとします。これらのデータを組み合わせることで、「特定の顧客が過去にどのような商品を購入したか」といった分析が可能になります。
pandasには、データを結合するための主な方法がいくつかあります。ここでは、pd.concat
とpd.merge
という二つの主要なメソッドを紹介します。
pd.concat
による結合
pd.concat
は、複数のデータフレームを単純に縦方向(行方向)または横方向(列方向)に連結する際に使用します。Excelでいうところの、複数のシートを単純に上下に貼り合わせたり、左右に並べたりするイメージに近いです。
例えば、2つの期間の売上データが別々のデータフレームに格納されている場合、これらを縦に連結して一つの売上データを作成することができます。
import pandas as pd
# 期間1の売上データ
df1 = pd.DataFrame({
'日付': ['2023-01-01', '2023-01-02', '2023-01-03'],
'商品名': ['A', 'B', 'A'],
'売上': [1000, 1500, 1200]
})
# 期間2の売上データ
df2 = pd.DataFrame({
'日付': ['2023-01-04', '2023-01-05', '2023-01-06'],
'商品名': ['C', 'A', 'B'],
'売上': [800, 2000, 1800]
})
# 縦方向(行方向)に連結
df_combined_rows = pd.concat([df1, df2])
print("--- df1 ---")
print(df1)
print("\n--- df2 ---")
print(df2)
print("\n--- 縦に連結したデータフレーム ---")
print(df_combined_rows)
上記の例では、df1
とdf2
が縦に連結され、6行の新しいデータフレームが作成されています。元のデータフレームのインデックスも引き継がれますが、ignore_index=True
を指定することで新しい連番のインデックスを振ることも可能です。
また、axis=1
を指定することで、横方向(列方向)に連結することもできます。ただし、単純な横連結は、行数が一致しない場合に欠損値(NaN)が発生することがあります。
# 横方向(列方向)に連結 (例としてカラム名が異なるデータを用意)
df3 = pd.DataFrame({
'商品ID': [1, 2, 3],
'価格': [100, 200, 300]
})
df4 = pd.DataFrame({
'在庫数': [10, 5],
}, index=[0, 2]) # 行数が異なり、インデックスも一部一致しない例
df_combined_cols = pd.concat([df3, df4], axis=1)
print("\n--- df3 ---")
print(df3)
print("\n--- df4 ---")
print(df4)
print("\n--- 横に連結したデータフレーム ---")
print(df_combined_cols)
このように、pd.concat
は複数のデータフレームを単純に結合したい場合に便利です。
pd.merge
による結合
pd.merge
は、一つ以上のキーとなる列に基づいて、2つのデータフレームを結合する際に使用します。これはデータベースのSQLにおけるJOIN操作に非常に似ています。例えば、「顧客ID」という共通の列を持つ顧客情報データと購買履歴データを、この「顧客ID」をキーとして結合するといったケースです。
pd.merge
では、どのように結合するか(結合の種類)を指定できます。代表的な結合の種類には以下があります。
- 内部結合 (inner join): 両方のデータフレームにキーが存在する行のみを残して結合します。
- 左外部結合 (left outer join): 左側のデータフレームの全ての行を残し、キーが一致する右側のデータを結合します。右側にキーが存在しない場合は、該当する列の値は欠損値(NaN)となります。
- 右外部結合 (right outer join): 右側のデータフレームの全ての行を残し、キーが一致する左側のデータを結合します。
- 完全外部結合 (outer join): どちらかのデータフレームにキーが存在する全ての行を残して結合します。キーが一致しない場合は、該当する列の値は欠損値(NaN)となります。
ここでは、顧客情報と購買データを使った内部結合と左外部結合の例を見てみましょう。
# 顧客情報データ
df_customers = pd.DataFrame({
'顧客ID': [1, 2, 3, 4],
'顧客名': ['山田', '佐藤', '田中', '高橋']
})
# 購買データ
df_purchases = pd.DataFrame({
'顧客ID': [1, 2, 1, 3, 5], # 顧客ID 5 は顧客情報に存在しない
'商品': ['リンゴ', 'バナナ', 'オレンジ', 'メロン', 'ブドウ'],
'金額': [100, 150, 120, 300, 250]
})
# 内部結合 (inner join): 顧客ID 1, 2, 3 の購買履歴のみが残る
df_inner_merge = pd.merge(df_customers, df_purchases, on='顧客ID', how='inner')
print("--- 顧客情報 ---")
print(df_customers)
print("\n--- 購買データ ---")
print(df_purchases)
print("\n--- 内部結合の結果 ---")
print(df_inner_merge)
# 左外部結合 (left outer join): 顧客情報全ての行が残る。高橋さん(ID 4)には購買履歴がないためNaN
df_left_merge = pd.merge(df_customers, df_purchases, on='顧客ID', how='left')
print("\n--- 左外部結合の結果 ---")
print(df_left_merge)
pd.merge
を使うことで、共通のキーを持つ異なるデータフレームから情報を正確に組み合わせることができます。on
引数で結合に使う列を指定し、how
引数で結合の種類を指定します。
どちらを使うべきか?
pd.concat
: 複数のデータフレームを単に上下または左右に連結したい場合。同じ構造のデータをまとめたいときなど。pd.merge
: 共通のキー列に基づいて、関連する情報を異なるデータフレームから組み合わせたい場合。顧客IDや商品IDなどで情報を紐付けたいときなど。
データの集計
データを結合して必要な情報を集めた後、分析や機械学習に適した形にデータを要約(集計)することがよくあります。例えば、顧客ごとの合計購入金額を計算したり、商品カテゴリごとの平均売上を算出したりといった操作です。これにより、データの全体像を把握しやすくなり、また、モデルの入力として使いやすい特徴量を作成することができます。
pandasでデータを集計する際に最も頻繁に使用されるのが、groupby()
メソッドです。これは、特定の列の値に基づいてデータをグループに分割し、その各グループに対して集計関数(合計、平均、個数など)を適用するという処理を行います。
groupby()
メソッドによる集計
groupby()
メソッドは、「Split-Apply-Combine」という3つのステップで処理を行います。
- Split (分割): 指定されたキー列の値に基づいてデータフレームをグループに分割します。
- Apply (適用): 各グループに対して、指定された集計関数(例:
sum()
,mean()
,count()
,max()
,min()
,size()
,nunique()
など)を適用します。 - Combine (結合): 各グループに適用された集計結果を一つにまとめ、新しいデータフレームまたはシリーズを作成します。
先ほどの購買データ(df_purchases
)を使って、顧客ごとの合計購入金額を計算してみましょう。
# 購買データ (再掲)
df_purchases = pd.DataFrame({
'顧客ID': [1, 2, 1, 3, 5],
'商品': ['リンゴ', 'バナナ', 'オレンジ', 'メロン', 'ブドウ'],
'金額': [100, 150, 120, 300, 250]
})
# '顧客ID'でグループ化し、'金額'の合計を計算
customer_total_金額 = df_purchases.groupby('顧客ID')['金額'].sum()
print("--- 購買データ ---")
print(df_purchases)
print("\n--- 顧客ごとの合計購入金額 ---")
print(customer_total_金額)
この例では、df_purchases.groupby('顧客ID')
でデータを顧客IDごとにグループ化し、['金額'].sum()
で各グループの金額列の合計を計算しています。結果は、顧客IDをインデックスとする新しいシリーズになります。
複数の列でグループ化することも可能です。例えば、顧客IDと商品名の組み合わせごとの合計金額を計算できます。
# '顧客ID'と'商品'でグループ化し、'金額'の合計を計算
customer_product_total_金額 = df_purchases.groupby(['顧客ID', '商品'])['金額'].sum()
print("\n--- 顧客と商品の組み合わせごとの合計購入金額 ---")
print(customer_product_total_金額)
また、複数の集計関数を一度に適用することもできます。
# '顧客ID'でグループ化し、'金額'の合計と平均、購入回数を計算
customer_aggregated_stats = df_purchases.groupby('顧客ID')['金額'].agg(['sum', 'mean', 'count'])
print("\n--- 顧客ごとの合計購入金額、平均購入金額、購入回数 ---")
print(customer_aggregated_stats)
groupby()
と様々な集計関数を組み合わせることで、データから多様な集計値を得ることができ、これが機械学習モデルの特徴量として非常に有効になることがあります。
まとめ
この記事では、機械学習のためのデータ前処理として非常に頻繁に行われる、データの結合と集計という二つの基本的な操作について解説しました。
- データの結合には、単純な連結を行う
pd.concat
と、キー列に基づいて関連する情報を紐付けるpd.merge
があることを学びました。 - データの集計には、
groupby()
メソッドを使ってデータをグループに分割し、合計や平均などの集計関数を適用する方法を学びました。
これらの操作は、実世界の様々なデータソースから必要な情報を集め、分析や機械学習モデルが扱える形にデータを加工するための基盤となります。ビジネス課題に応じて、どのようなデータをどのように結合・集計すれば、より良い分析やモデル構築につながるかを考えることが、データ前処理の重要なポイントです。
今回学んだ結合と集計のスキルは、データ前処理における強力なツールです。これらの基礎をしっかりと理解し、実際のデータに適用することで、データ分析や機械学習プロジェクトをより効果的に進めることができるでしょう。