複数表の結合と欠損値の処理。実データ分析で必ず必要になる前処理スキル。
複数のCSVを組み合わせるのって、SQLのJOINみたいなことPandasでもできるの?
もちろんできるわよ。 pd.mergeとpd.concatが代表ね。
import pandas as pd
customers = pd.DataFrame({
'customer_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
})
orders = pd.DataFrame({
'order_id': [101, 102, 103, 104, 105],
'customer_id': [1, 2, 1, 3, 5], # 5は customers に存在しない
'amount': [3000, 5000, 2000, 8000, 1500],
})
# (1) merge: SQLのJOINに相当
inner = pd.merge(customers, orders, on='customer_id', how='inner')
left = pd.merge(customers, orders, on='customer_id', how='left')
outer = pd.merge(customers, orders, on='customer_id', how='outer')
# (2) concat: 縦/横方向の結合
df1 = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
df2 = pd.DataFrame({'a': [5, 6], 'b': [7, 8]})
stacked = pd.concat([df1, df2], axis=0, ignore_index=True) # 縦結合
つまりpd.mergeはSQLのJOIN相当で、how='inner/left/right/outer'で種類を選ぶ。 pd.concatは単純に縦か横にスタックする、という理解で合っていますか?
欠損値ってどうやって扱えばいいの?
import numpy as np
# 欠損値検出
df.isna().sum() # 列ごとの欠損数
df.isna().any(axis=1) # 各行に欠損があるか
# 欠損値処理
df_dropped = df.dropna() # 欠損行を削除
df_filled = df.fillna(0) # 0で埋める
df_filled = df.fillna(df.mean()) # 列ごとの平均で埋める
df_filled = df.fillna(method='ffill') # 前の値で埋める(時系列向け)
# 列ごとに違う埋め方
df['age'] = df['age'].fillna(df['age'].median())
df['gender'] = df['gender'].fillna('不明')
欠損値って、どれで埋めるか難しいですぅ…
数値列なら平均か中央値、カテゴリ列なら最頻値や『不明』、時系列なら前後の値を使う、というのが定石ね。 大事なのは『何で埋めたかをきちんとドキュメント化する』ということね。
補足すると、単純な平均埋めには注意が必要。 列の平均で全行を埋めるとデータの分散が下がってしまって、その後の分析にバイアスが入る。 多重代入法みたいな高度な手法もある。
どれくらい欠損があったら、もう諦めて行ごと削除しちゃう?
目安として『1行に欠損が大半ある』『列の50%以上が欠損』なら削除を検討。 ただしデータ量が少ない場合は埋めることも検討。 ケースバイケース。
DS検定★では merge の how の違い、isna/fillna/dropna の使い分けが頻出。
確認クイズ
pd.merge(df1, df2, on='id', how='left') の結果として正しいのはどれか。
- 両方に存在する id のみが結果に残る
- df1 の全行が結果に残り、df2 にない場合は NaN になる
- df2 の全行が結果に残る
- 両方の和集合(両方にあれば残る)
こたえを見る
正解: 2. df1 の全行が結果に残り、df2 にない場合は NaN になる
df1 の全行が結果に残り、df2 にない場合は NaN になる。 how='left' は SQL の LEFT JOIN 相当で、左側(df1)の全行を保持し、右側にマッチしない場合は欠損値で埋められます。