(学習ログ)Python023:pandasで複数データを結合して集計・フィルタリングする
1. はじめに
複数のCSVファイルを読み込んで結合し、集計・フィルタリングを行う方法を学びます。
実務のデータ分析では、顧客情報やログデータが複数のファイルに分かれていることがよくあります。本記事では、pandasのmerge() を使ったデータ結合、groupby() による集計、そして日付データのフィルタリング方法を、実践的なコード例とともに解説します。
2. サンプルコード
コード全文
import pandas as pd
# 利用ログデータを読み込む
sample_usagelog = pd.read_csv('003_サンプル/usage_log.csv')
# 顧客マスターを読み込む
sample_customer = pd.read_csv('003_サンプル/customer_master.csv')
# クラスマスターを読み込む(会員ランク情報など)
sample_class_master = pd.read_csv('003_サンプル/class_master.csv')
# キャンペーンマスターを読み込む(キャンペーン名など)
sample_campaign_master = pd.read_csv('003_サンプル/campaign_master.csv')
# 顧客マスターとクラスマスターを結合(class列をキーに左結合)
sample_customer_join = pd.merge(sample_customer, sample_class_master, on="class", how="left")
# さらにキャンペーンマスターを結合(campaign_id列をキーに左結合)
sample_customer_join = pd.merge(sample_customer_join, sample_campaign_master, on="campaign_id", how="left")
# クラス名ごとの顧客数を集計
sample_customer_join.groupby("class_name").count()["customer_id"]
# キャンペーン名ごとの顧客数を集計
sample_customer_join.groupby("campaign_name").count()["customer_id"]
# 性別ごとの顧客数を集計
sample_customer_join.groupby("gender").count()["customer_id"]
# 削除フラグごとの顧客数を集計
sample_customer_join.groupby("is_deleted").count()["customer_id"]
# start_date列を文字列から日付型に変換
sample_customer_join["start_date"] = pd.to_datetime(sample_customer_join["start_date"])
# 2018年4月1日より後に開始した顧客のみを抽出
sample_customer_start = sample_customer_join.loc[sample_customer_join["start_date"]>pd.to_datetime("20180401")]
# 抽出された顧客数を表示
print(len(sample_customer_start))
コード解説
ブロック1:データの読み込み
sample_usagelog = pd.read_csv('003_サンプル/usage_log.csv')
sample_customer = pd.read_csv('003_サンプル/customer_master.csv')
sample_class_master = pd.read_csv('003_サンプル/class_master.csv')
sample_campaign_master = pd.read_csv('003_サンプル/campaign_master.csv')
何をしているか
4つのCSVファイルをそれぞれDataFrameとして読み込んでいます。
なぜそうするか
実務では、データが用途別に複数ファイルに分かれていることが一般的です。まず個別に読み込んでから、必要に応じて結合していきます。
初心者ポイント
ファイルパスは実行環境によって異なります。相対パスで指定する場合、Pythonスクリプトの実行位置に注意しましょう。
ブロック2:データの結合
sample_customer_join = pd.merge(sample_customer, sample_class_master, on="class", how="left")
sample_customer_join = pd.merge(sample_customer_join, sample_campaign_master, on="campaign_id", how="left")
何をしているか
merge() 関数を使って、複数のDataFrameを順番に結合しています。
なぜそうするか
顧客マスターにはclassやcampaign_idというIDしかありません。実際の分析では、それらに対応する「名前」や「詳細情報」が必要になるため、マスターデータと結合します。
how="left"の意味
左側のDataFrame(ここではsample_customer)のすべての行を保持し、右側のDataFrameから一致する情報を追加します。SQLのLEFT JOINと同じ考え方です。
初心者ポイント
-
on="列名"で結合キーを指定します - 左右で列名が異なる場合は
left_onとright_onを使います
ブロック3:グループごとの集計
sample_customer_join.groupby("class_name").count()["customer_id"]
sample_customer_join.groupby("campaign_name").count()["customer_id"]
sample_customer_join.groupby("gender").count()["customer_id"]
sample_customer_join.groupby("is_deleted").count()["customer_id"]
何をしているか
groupby() を使って、特定の列(カテゴリ)ごとに顧客数をカウントしています。
なぜそうするか
「クラスごとに何人いるか」「キャンペーン別の参加者数は」といった集計は、データ分析の基本です。
["customer_id"]の意味
count()だけだとすべての列がカウントされて見づらいので、代表的な列(ここでは顧客ID)のみを表示しています。
ブロック4:日付フィルタリング
sample_customer_join["start_date"] = pd.to_datetime(sample_customer_join["start_date"])
sample_customer_start = sample_customer_join.loc[sample_customer_join["start_date"]>pd.to_datetime("20180401")]
print(len(sample_customer_start))
何をしているか
- 文字列として保存されている日付データを日付型に変換
- 2018年4月1日より後のデータだけを抽出
- 抽出された行数を表示
なぜそうするか
CSVから読み込んだ日付は文字列型です。日付として比較・計算するには、pd.to_datetime() で明示的に変換する必要があります。
初心者ポイント
-
.loc[]は条件に一致する行を抽出する方法です -
len()でDataFrameの行数(レコード数)を取得できます
実行結果例
データが以下のような構成だと仮定します
customer_master.csv (5件):
- customer_id, class, campaign_id, gender, start_date, is_deleted
class_master.csv (3件):
- class, class_name (例: 1→"ゴールド", 2→"シルバー", 3→"ブロンズ")
campaign_master.csv (2件):
- campaign_id, campaign_name (例: 101→"春キャンペーン", 102→"夏キャンペーン")
実行結果
# クラス名ごとの顧客数
class_name
ゴールド 2
シルバー 2
ブロンズ 1
Name: customer_id, dtype: int64
# キャンペーン名ごとの顧客数
campaign_name
春キャンペーン 3
夏キャンペーン 2
Name: customer_id, dtype: int64
# 性別ごとの顧客数
gender
F 2
M 3
Name: customer_id, dtype: int64
# 削除フラグごとの顧客数
is_deleted
0 4
1 1
Name: customer_id, dtype: int64
# 2018年4月1日以降に開始した顧客数
3
このように、各カテゴリごとの内訳と、条件に合う顧客数が確認できます。
3. エラーと確認のポイント
代表的なエラー
エラー1:KeyError
KeyError: 'class'
原因
結合キーに指定した列名が、DataFrameに存在しない場合に発生します。
対処法
# 列名を確認
print(sample_customer.columns)
print(sample_class_master.columns)
列名のスペルミスや全角・半角の違いをチェックしましょう。
エラー2:FileNotFoundError
FileNotFoundError: [Errno 2] No such file or directory: '003_サンプル/usage_log.csv'
原因
指定したファイルパスが間違っているか、ファイルが存在しません。
対処法
import os
# 現在のディレクトリを確認
print(os.getcwd())
# ファイルの存在確認
print(os.path.exists('003_サンプル/usage_log.csv'))
相対パスではなく絶対パスで指定するのも一つの方法です。
エラー3:TypeError(日付比較時)
TypeError: '>' not supported between instances of 'str' and 'Timestamp'
原因
日付列を**pd.to_datetime()**で変換せずに、文字列のまま比較しようとした場合に発生します。
対処法
# 必ず日付型に変換してから比較
sample_customer_join["start_date"] = pd.to_datetime(sample_customer_join["start_date"])
よく使う確認メソッド
1. head():先頭データの確認
sample_customer_join.head()
出力例
customer_id class campaign_id gender start_date class_name campaign_name
0 1001 1 101 M 2018-05-10 ゴールド 春キャンペーン
1 1002 2 101 F 2018-03-15 シルバー 春キャンペーン
...
結合後のデータ構造を視覚的に確認できます。
2. info():データ型とnull値の確認
sample_customer_join.info()
出力例
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 7 columns):
customer_id 5 non-null int64
class 5 non-null int64
start_date 5 non-null datetime64[ns]
...
各列のデータ型と欠損値の有無が一目でわかります。
3. isnull().sum():欠損値の数
sample_customer_join.isnull().sum()
出力例
customer_id 0
class_name 1
campaign_name 0
...
結合時に対応するデータがなかった場合、NaN(欠損値)になります。
4. value_counts():値の分布確認
sample_customer_join["class_name"].value_counts()
出力例
ゴールド 2
シルバー 2
ブロンズ 1
groupby().count()と似ていますが、より簡潔に分布を確認できます。
4. まとめ
- 複数のCSVファイルをpandasで読み込む方法
- merge()を使った複数DataFrameの結合テクニック(LEFT JOINの概念)
- groupby()による集計とカテゴリごとの分析方法
- 日付データの型変換とフィルタリング手法
Discussion