STEP 8:Pandasによるデータクリーニング②

🔍 STEP 8: Pandasによるデータクリーニング②

異常値・外れ値を検出して処理し、データを正規化する方法を学びます

📋 このステップで学ぶこと

  • 異常値と外れ値の違い
  • 異常値の検出方法(範囲チェック、論理チェック)
  • 異常値の対処方法(削除、置換、補完、クリッピング)
  • 外れ値の検出方法(IQR、Zスコア、箱ひげ図)
  • 外れ値の処理方法
  • データの正規化(Min-Max正規化、標準化、ログ変換)
  • 実践演習:総合的なデータクリーニング

🎯 1. 異常値の検出と対処

1-1. STEP 7の復習

STEP 7では、欠損値(NaN)の処理と重複データの削除を学びました。
このSTEP 8では、さらに異常値・外れ値の検出と処理、データの正規化を学びます。

1-2. 異常値とは?

異常値(Anomaly)とは、明らかにおかしい値です。
入力ミス、システムエラー、不正データなどが原因で発生します。

🏥 例え話:健康診断の結果

健康診断で「身長:-170cm」という結果が出たら、明らかにおかしいですよね。
これが異常値です。機械の故障か、入力ミスか、何か問題があったはずです。

⚠️ 異常値の具体例
  • 年齢が-5歳や150歳 → 人間としてありえない値
  • 価格が0円や-1000円 → 通常の商品ではありえない
  • 日付が未来(2050年) → データ入力ミス
  • 郵便番号が12345678 → 桁数が違う
  • メールアドレスに@がない → フォーマットエラー

1-3. 範囲チェックで異常値を検出

最も基本的な方法は、値の範囲をチェックすることです。

# ===== 基本的な異常値検出 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘name’: [‘山田’, ‘佐藤’, ‘鈴木’, ‘田中’, ‘高橋’, ‘渡辺’], ‘age’: [25, 30, -5, 28, 150, 35], ‘price’: [1000, 1500, -500, 2000, 0, 1800] }) print(“=== 元のデータ ===”) print(df) print() # 1. 年齢の異常値を検出(0未満または120より大きい) print(“=== 異常な年齢(0未満 or 120超)===”) invalid_age = df[(df[‘age’] < 0) | (df['age'] > 120)] print(invalid_age) print() # 2. 価格の異常値を検出(0以下) print(“=== 異常な価格(0以下)===”) invalid_price = df[df[‘price’] <= 0] print(invalid_price) print() # 3. 正常なデータだけを抽出 print("=== 正常なデータ ===") valid_df = df[ (df['age'] >= 0) & (df[‘age’] <= 120) & (df['price'] > 0) ] print(valid_df)
【実行結果】 === 異常な年齢(0未満 or 120超)=== name age price 2 鈴木 -5 -500 4 高橋 150 0 === 異常な価格(0以下)=== name age price 2 鈴木 -5 -500 4 高橋 150 0 === 正常なデータ === name age price 0 山田 25 1000 1 佐藤 30 1500 3 田中 28 2000 5 渡辺 35 1800

1-4. 複数条件での異常値チェック

# ===== 複数条件での異常値検出 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘product_name’: [‘ノートPC’, ”, ‘マウス’, ‘キーボード’, ‘モニター’], ‘price’: [89800, 1500, 1980, -100, 25000], ‘stock’: [10, 50, 5, 30, -5], ‘category’: [‘PC’, ‘PC’, ‘アクセサリ’, ‘アクセサリ’, ‘PC’] }) print(“=== 元のデータ ===”) print(df) # 異常値を検出するフラグを作成 df[‘is_invalid’] = False # 条件1: 商品名が空 df.loc[df[‘product_name’] == ”, ‘is_invalid’] = True # 条件2: 価格が異常(100円未満または100万円超) df.loc[(df[‘price’] < 100) | (df['price'] > 1000000), ‘is_invalid’] = True # 条件3: 在庫が負 df.loc[df[‘stock’] < 0, 'is_invalid'] = True # 異常データを表示 print("\n=== 異常なデータ ===") print(df[df['is_invalid']]) # 正常なデータだけを抽出 df_clean = df[~df['is_invalid']].drop('is_invalid', axis=1) print("\n=== 正常なデータ ===") print(df_clean)

1-5. 異常値の対処方法

1️⃣ 削除

異常値を含む行を削除
使いどころ:異常値が少ない場合

2️⃣ 置換

正しい値に修正
使いどころ:明らかな入力ミス

3️⃣ 補完

平均値や中央値で置換
使いどころ:データを残したい場合

4️⃣ クリッピング

上限・下限で切り詰め
使いどころ:極端な値を緩和

# ===== 異常値の対処方法 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘age’: [25, 30, -5, 28, 150, 35], ‘price’: [1000, 1500, -500, 2000, 0, 1800] }) print(“=== 元のデータ ===”) print(df) # 方法1: 削除 df1 = df.copy() df1 = df1[(df1[‘age’] >= 0) & (df1[‘age’] <= 120)] print("\n=== 方法1: 削除 ===") print(df1) # 方法2: NaNに置換してから補完 df2 = df.copy() df2.loc[(df2['age'] < 0) | (df2['age'] > 120), ‘age’] = np.nan df2[‘age’] = df2[‘age’].fillna(df2[‘age’].median()) print(“\n=== 方法2: 中央値で補完 ===”) print(df2) # 方法3: クリッピング(上限・下限で切り詰め) df3 = df.copy() df3[‘age’] = df3[‘age’].clip(lower=0, upper=120) print(“\n=== 方法3: クリッピング(0〜120)===”) print(df3)

1-6. 実践例:ECサイトの注文データクリーニング

# ===== 実践的な異常値処理 ===== import pandas as pd import numpy as np # 注文データ(異常値あり) df = pd.DataFrame({ ‘order_id’: [1, 2, 3, 4, 5, 6], ‘product_name’: [‘ノートPC’, ”, ‘マウス’, ‘キーボード’, ‘モニター’, ‘ヘッドホン’], ‘quantity’: [2, 5, -1, 3, 1000, 1], ‘price’: [89800, 1500, 1980, -500, 25000, 8900], ‘order_date’: [‘2024-01-01’, ‘2050-12-31’, ‘2024-01-03’, ‘2024-01-04’, ‘2024-01-05’, ‘2024-01-06’] }) print(“=== 元のデータ ===”) print(df) # クリーニング処理 df_clean = df.copy() # 1. 商品名が空の行を削除 print(“\n【処理1】商品名が空の行を削除”) before = len(df_clean) df_clean = df_clean[df_clean[‘product_name’] != ”] print(f” 削除数: {before – len(df_clean)}件”) # 2. 数量が異常(0以下または100超)な行を削除 print(“\n【処理2】数量が異常な行を削除”) before = len(df_clean) df_clean = df_clean[(df_clean[‘quantity’] > 0) & (df_clean[‘quantity’] <= 100)] print(f" 削除数: {before - len(df_clean)}件") # 3. 価格が異常(0以下)なものを中央値で置換 print("\n【処理3】価格が異常なものを中央値で置換") median_price = df_clean[df_clean['price'] > 0][‘price’].median() invalid_price_count = (df_clean[‘price’] <= 0).sum() df_clean.loc[df_clean['price'] <= 0, 'price'] = median_price print(f" 置換数: {invalid_price_count}件(中央値: {median_price}円)") # 4. 日付が未来のものを削除 print("\n【処理4】日付が未来の行を削除") df_clean['order_date'] = pd.to_datetime(df_clean['order_date']) before = len(df_clean) df_clean = df_clean[df_clean['order_date'] <= pd.Timestamp.now()] print(f" 削除数: {before - len(df_clean)}件") # インデックスをリセット df_clean = df_clean.reset_index(drop=True) print("\n=== クリーニング後 ===") print(df_clean)

📊 2. 外れ値の処理

2-1. 外れ値とは?

外れ値(Outlier)とは、他のデータから大きく離れた値です。
異常値とは違い、正しいデータだが極端な値を指します。

💰 例え話:クラスの年収

同窓会で年収を聞いたとき、ほとんどの人が400〜600万円なのに、一人だけ年収1億円の人がいた。
これは間違いではないけど極端な値=外れ値です。
平均を計算すると、この一人のせいで全体の平均が大きく歪んでしまいます。

2-2. 異常値と外れ値の違い

項目 異常値(Anomaly) 外れ値(Outlier)
定義 明らかにおかしい値 極端だが正しい値
年齢が-5歳、150歳 年収が1億円
原因 入力ミス、システムエラー 自然なばらつき、特殊なケース
対処 削除または修正 慎重に判断(残すことも多い)

2-3. 外れ値の検出方法①:IQR(四分位範囲)

IQR(Interquartile Range)は、データの散らばりを測る指標です。
第1四分位数(Q1)と第3四分位数(Q3)の差を使って外れ値を検出します。

📐 IQRの計算方法
  • Q1:データを小さい順に並べたとき、下から25%の位置の値
  • Q3:データを小さい順に並べたとき、下から75%の位置の値
  • IQR = Q3 – Q1
  • 外れ値の範囲:Q1 – 1.5×IQR より小さい、または Q3 + 1.5×IQR より大きい
# ===== IQRで外れ値を検出 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘price’: [1000, 1200, 1100, 1300, 1150, 1250, 5000, 1180, 950, 1050] }) print(“=== 元のデータ ===”) print(df[‘price’].values) # IQR(四分位範囲)を計算 Q1 = df[‘price’].quantile(0.25) # 第1四分位数(25%) Q3 = df[‘price’].quantile(0.75) # 第3四分位数(75%) IQR = Q3 – Q1 # 外れ値の範囲を計算 lower_bound = Q1 – 1.5 * IQR # 下限 upper_bound = Q3 + 1.5 * IQR # 上限 print(f”\n=== IQRの計算 ===”) print(f”Q1(25%): {Q1}”) print(f”Q3(75%): {Q3}”) print(f”IQR: {IQR}”) print(f”外れ値の下限: {lower_bound}”) print(f”外れ値の上限: {upper_bound}”) # 外れ値を検出 outliers = df[(df[‘price’] < lower_bound) | (df['price'] > upper_bound)] print(f”\n=== 外れ値 ===”) print(outliers) # 外れ値を除外 df_clean = df[(df[‘price’] >= lower_bound) & (df[‘price’] <= upper_bound)] print(f"\n=== 外れ値除外後 ===") print(df_clean['price'].values)
【実行結果】 === 元のデータ === [1000 1200 1100 1300 1150 1250 5000 1180 950 1050] === IQRの計算 === Q1(25%): 1037.5 Q3(75%): 1237.5 IQR: 200.0 外れ値の下限: 737.5 外れ値の上限: 1537.5 === 外れ値 === price 6 5000 === 外れ値除外後 === [1000 1200 1100 1300 1150 1250 1180 950 1050]

2-4. 外れ値の検出方法②:Zスコア(標準偏差)

Zスコアは、データが平均からどれだけ離れているかを標準偏差で測る指標です。

📐 Zスコアの計算方法
  • Zスコア = (値 – 平均) / 標準偏差
  • |Z| > 2:約95%の範囲外 → 外れ値の可能性
  • |Z| > 3:約99.7%の範囲外 → ほぼ確実に外れ値
# ===== Zスコアで外れ値を検出 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘price’: [1000, 1200, 1100, 1300, 1150, 1250, 5000, 1180] }) print(“=== 元のデータ ===”) print(df[‘price’].values) # Zスコアを計算 mean = df[‘price’].mean() std = df[‘price’].std() df[‘z_score’] = (df[‘price’] – mean) / std print(f”\n平均: {mean:.2f}”) print(f”標準偏差: {std:.2f}”) print(“\n=== Zスコア ===”) print(df) # |Z| > 2 を外れ値とする outliers_2 = df[np.abs(df[‘z_score’]) > 2] print(f”\n=== 外れ値(|Z| > 2)===”) print(outliers_2) # |Z| > 3 を外れ値とする outliers_3 = df[np.abs(df[‘z_score’]) > 3] print(f”\n=== 外れ値(|Z| > 3)===”) print(outliers_3 if len(outliers_3) > 0 else “なし”)

2-5. IQR vs Zスコア

項目 IQR Zスコア
前提 分布を仮定しない 正規分布を仮定
外れ値の影響 受けにくい(頑健) 受けやすい
使いどころ 一般的なデータ 正規分布に近いデータ
推奨度 ★★★(よく使われる) ★★☆

2-6. 外れ値の処理方法

# ===== 外れ値の処理方法 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘price’: [1000, 1200, 1100, 1300, 1150, 1250, 5000, 1180] }) # IQRで外れ値の範囲を計算 Q1 = df[‘price’].quantile(0.25) Q3 = df[‘price’].quantile(0.75) IQR = Q3 – Q1 lower = Q1 – 1.5 * IQR upper = Q3 + 1.5 * IQR print(f”外れ値の範囲: {lower:.0f} 〜 {upper:.0f}”) print(f”元のデータ: {df[‘price’].values}”) # 方法1: 削除 df1 = df[(df[‘price’] >= lower) & (df[‘price’] <= upper)].copy() print(f"\n【方法1】削除後: {df1['price'].values}") # 方法2: クリッピング(上限・下限で切り詰め) df2 = df.copy() df2['price'] = df2['price'].clip(lower=lower, upper=upper) print(f"【方法2】クリッピング後: {df2['price'].values}") # 方法3: 中央値で置換 df3 = df.copy() median = df3[(df3['price'] >= lower) & (df3[‘price’] <= upper)]['price'].median() df3.loc[(df3['price'] < lower) | (df3['price'] > upper), ‘price’] = median print(f”【方法3】中央値({median:.0f})で置換後: {df3[‘price’].values}”)
⚠️ 外れ値処理の注意点
  • 削除は慎重に:重要な情報が失われる可能性がある
  • ビジネス知識が重要:年収1億円は外れ値だが、削除すべきではない場合もある
  • 可視化してから判断:箱ひげ図やヒストグラムで確認してから処理
  • 外れ値の原因を調査:本当に外れ値なのか、異常値なのかを確認

📏 3. データの正規化

3-1. 正規化とは?

正規化(Normalization)とは、データの範囲を揃える処理です。
機械学習や統計分析の前処理としてよく使われます。

📊 なぜ正規化が必要?

例:「年齢(20〜80)」と「年収(200万〜2000万)」を一緒に分析する場合

  • 年齢:最大60の差(80 – 20)
  • 年収:最大1800万の差(2000万 – 200万)

このまま機械学習にかけると、年収の影響が大きすぎて、年齢の影響が無視されてしまいます。
正規化で0〜1の範囲に揃えれば、公平に比較できる!

3-2. Min-Max正規化(0〜1に変換)

データを0〜1の範囲に変換する方法です。

📐 Min-Max正規化の公式

正規化された値 = (元の値 – 最小値) / (最大値 – 最小値)

# ===== Min-Max正規化 ===== import pandas as pd df = pd.DataFrame({ ‘age’: [25, 30, 35, 40, 45], ‘salary’: [3000000, 4000000, 5000000, 6000000, 7000000] }) print(“=== 元のデータ ===”) print(df) # 手動でMin-Max正規化 df[‘age_normalized’] = (df[‘age’] – df[‘age’].min()) / (df[‘age’].max() – df[‘age’].min()) df[‘salary_normalized’] = (df[‘salary’] – df[‘salary’].min()) / (df[‘salary’].max() – df[‘salary’].min()) print(“\n=== 手動で正規化 ===”) print(df) # scikit-learnを使う方法 from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() df[[‘age_sklearn’, ‘salary_sklearn’]] = scaler.fit_transform(df[[‘age’, ‘salary’]]) print(“\n=== scikit-learnで正規化 ===”) print(df[[‘age’, ‘salary’, ‘age_sklearn’, ‘salary_sklearn’]])
【実行結果】 === 手動で正規化 === age salary age_normalized salary_normalized 0 25 3000000 0.00 0.00 1 30 4000000 0.25 0.25 2 35 5000000 0.50 0.50 3 40 6000000 0.75 0.75 4 45 7000000 1.00 1.00

3-3. 標準化(Standardization)

データを平均0、標準偏差1に変換する方法です。

📐 標準化の公式

標準化された値 = (元の値 – 平均) / 標準偏差

# ===== 標準化 ===== import pandas as pd from sklearn.preprocessing import StandardScaler df = pd.DataFrame({ ‘age’: [25, 30, 35, 40, 45], ‘salary’: [3000000, 4000000, 5000000, 6000000, 7000000] }) print(“=== 元のデータ ===”) print(df) # 手動で標準化 df[‘age_standardized’] = (df[‘age’] – df[‘age’].mean()) / df[‘age’].std() # scikit-learnで標準化 scaler = StandardScaler() df[[‘age_std’, ‘salary_std’]] = scaler.fit_transform(df[[‘age’, ‘salary’]]) print(“\n=== 標準化後 ===”) print(df) print(“\n=== 確認:平均と標準偏差 ===”) print(f”age_std の平均: {df[‘age_std’].mean():.10f}”) print(f”age_std の標準偏差: {df[‘age_std’].std():.10f}”)

3-4. Min-Max正規化 vs 標準化

項目 Min-Max正規化 標準化
変換後の範囲 0〜1 平均0、標準偏差1
外れ値の影響 大きい(最大・最小を使うため) 小さい
使いどころ 画像処理、ニューラルネットワーク 統計分析、機械学習(SVM、回帰)
元に戻す 可能 可能

3-5. ログ変換(対数変換)

ログ変換は、右に歪んだ分布を正規分布に近づける方法です。
年収や売上など、大きな値に偏ったデータに有効です。

# ===== ログ変換 ===== import pandas as pd import numpy as np df = pd.DataFrame({ ‘income’: [2000000, 3000000, 4000000, 8000000, 50000000] }) print(“=== 元のデータ ===”) print(df) print(f”平均: {df[‘income’].mean():,.0f}円”) print(f”中央値: {df[‘income’].median():,.0f}円”) # ログ変換(自然対数) df[‘income_log’] = np.log(df[‘income’]) # log1p(log(1+x)):0があってもOK df[‘income_log1p’] = np.log1p(df[‘income’]) print(“\n=== ログ変換後 ===”) print(df) # 元に戻す df[‘income_restored’] = np.exp(df[‘income_log’]) print(“\n=== 元に戻す(exp)===”) print(df[[‘income’, ‘income_restored’]])
💡 正規化の使い分け
  • Min-Max正規化:データを0〜1に収めたい場合、外れ値が少ない場合
  • 標準化:外れ値がある場合、平均周辺に集めたい場合
  • ログ変換:右に歪んだ分布(年収、売上など)を正規分布に近づけたい場合

🏋️ 4. 実践演習:総合的なデータクリーニング

4-1. 課題:顧客データの完全クリーニング

実際の現場を想定した汚いデータを、これまで学んだ技術を使って完全にクリーニングします。

# ===== 汚いデータの作成 ===== import pandas as pd import numpy as np # 汚いデータ(様々な問題を含む) df = pd.DataFrame({ ‘customer_id’: [1, 2, 3, 2, 4, 5, 6, 7, 8], ‘name’: [‘山田太郎’, ‘佐藤花子’, ”, ‘佐藤花子’, ‘鈴木一郎’, ‘田中美咲’, ‘高橋健太’, ‘渡辺由美’, ‘伊藤翔太’], ‘age’: [25, np.nan, 30, 28, -5, 150, 35, 40, 45], ‘email’: [‘yamada@ex.com’, ‘sato@ex.com’, ‘suzuki@ex.com’, ‘sato@ex.com’, ‘tanaka@ex.com’, ‘takahashi@ex.com’, ”, ‘watanabe@ex.com’, ‘ito@ex.com’], ‘purchase_amount’: [10000, ‘15000’, 20000, 18000, -5000, 50000, 8000, 12000, 0], ‘purchase_date’: [‘2024-01-01’, ‘2024-01-02’, ‘2024-01-03’, ‘2024-01-04’, ‘2050-12-31’, ‘2024-01-06’, ‘2024-01-07’, ‘2024-01-08’, ‘2024-01-09’] }) print(“=== 汚いデータ ===”) print(df) print(f”\nデータ型:\n{df.dtypes}”) print(f”\n欠損値:\n{df.isnull().sum()}”)

4-2. 完全なクリーニング処理

# ===== 完全なクリーニング処理 ===== import pandas as pd import numpy as np from datetime import datetime # (上記の汚いデータを使用) print(“\n” + “=” * 60) print(“データクリーニング開始”) print(“=” * 60) # ステップ1: 重複を削除(emailで判定、最初を残す) print(“\n【ステップ1】重複削除(emailで判定)”) before_count = len(df) df = df.drop_duplicates(subset=[‘email’], keep=’first’) print(f” → 削除数: {before_count – len(df)}件”) # ステップ2: 空の名前を削除 print(“\n【ステップ2】空の名前を削除”) before_count = len(df) df = df[df[‘name’] != ”] print(f” → 削除数: {before_count – len(df)}件”) # ステップ3: 年齢の欠損値を中央値で補完 print(“\n【ステップ3】年齢の欠損値を中央値で補完”) # まず正常な範囲のデータだけで中央値を計算 valid_ages = df[(df[‘age’] >= 0) & (df[‘age’] <= 120)]['age'] median_age = valid_ages.median() missing_count = df['age'].isnull().sum() df['age'] = df['age'].fillna(median_age) print(f" → 補完数: {missing_count}件(中央値: {median_age}歳)") # ステップ4: 年齢の異常値を削除 print("\n【ステップ4】年齢の異常値を削除(0未満 or 120超)") before_count = len(df) df = df[(df['age'] >= 0) & (df[‘age’] <= 120)] print(f" → 削除数: {before_count - len(df)}件") # ステップ5: 購入金額のデータ型を変換 print("\n【ステップ5】購入金額のデータ型を数値に変換") df['purchase_amount'] = pd.to_numeric(df['purchase_amount'], errors='coerce') # ステップ6: 購入金額の異常値を削除(0以下) print("\n【ステップ6】購入金額の異常値を削除(0以下)") before_count = len(df) df = df[df['purchase_amount'] > 0] print(f” → 削除数: {before_count – len(df)}件”) # ステップ7: 購入日をdatetime型に変換 print(“\n【ステップ7】購入日をdatetime型に変換”) df[‘purchase_date’] = pd.to_datetime(df[‘purchase_date’]) # ステップ8: 未来の日付を削除 print(“\n【ステップ8】未来の日付を削除”) before_count = len(df) df = df[df[‘purchase_date’] <= pd.Timestamp.now()] print(f" → 削除数: {before_count - len(df)}件") # ステップ9: 空のメールアドレスを補完 print("\n【ステップ9】空のメールアドレスを補完") empty_email_count = (df['email'] == '').sum() df.loc[df['email'] == '', 'email'] = 'unknown@example.com' print(f" → 補完数: {empty_email_count}件") # ステップ10: 購入金額の外れ値処理(IQR) print("\n【ステップ10】購入金額の外れ値処理(IQR)") Q1 = df['purchase_amount'].quantile(0.25) Q3 = df['purchase_amount'].quantile(0.75) IQR = Q3 - Q1 lower = Q1 - 1.5 * IQR upper = Q3 + 1.5 * IQR before_count = len(df) df = df[(df['purchase_amount'] >= lower) & (df[‘purchase_amount’] <= upper)] print(f" → 削除数: {before_count - len(df)}件(範囲: {lower:.0f}〜{upper:.0f})") # ステップ11: インデックスをリセット df = df.reset_index(drop=True) # ステップ12: データ型を最適化 df['customer_id'] = df['customer_id'].astype('int32') df['age'] = df['age'].astype('int32') df['purchase_amount'] = df['purchase_amount'].astype('int32') print("\n" + "=" * 60) print("データクリーニング完了") print("=" * 60) print(f"\n=== クリーニング後のデータ ===") print(df) print(f"\n=== データ型 ===") print(df.dtypes) print(f"\n=== 基本統計量 ===") print(df.describe())
✅ クリーニングのポイント
  • 各ステップで何件処理したかを表示する
  • 処理の順番に注意(重複削除→欠損値処理→異常値処理)
  • 元のデータは保存しておく(いつでも戻れるように)
  • 最後にデータ型を最適化してメモリを節約

📝 STEP 8 のまとめ

✅ このステップで学んだこと
  • 異常値の検出:範囲チェック、複数条件での検証
  • 異常値の対処:削除、置換、補完、クリッピング
  • 外れ値の検出:IQR(四分位範囲)、Zスコア
  • 外れ値の処理:削除、クリッピング、中央値置換
  • Min-Max正規化:0〜1に変換
  • 標準化:平均0、標準偏差1に変換
  • ログ変換:歪んだ分布を正規分布に近づける
💡 データクリーニングのベストプラクティス
  1. 元データを保存:クリーニング前のデータは必ず残す
  2. 段階的に処理:一度にすべてやらず、1つずつ確認しながら
  3. 処理内容を記録:何をしたか必ずログに残す
  4. 削除は慎重に:本当に削除すべきか検討
  5. 可視化して確認:処理前後でグラフ確認
🎯 次のステップの予告

次のSTEP 9では、「データ変換の基本パターン」を学びます。

  • カラムの追加・削除・変更
  • 文字列操作(split、strip、replace)
  • 日付データの処理
  • カテゴリデータの変換

実務で頻出する変換技術を習得しましょう!

📝 練習問題

問題 1 基礎

ageカラムが0未満または120を超える行を抽出するコードを書いてください。

【解答例】
invalid_age = df[(df[‘age’] < 0) | (df['age'] > 120)] print(invalid_age)
問題 2 基礎

priceカラムの値を0〜1000の範囲でクリッピングするコードを書いてください。

【解答例】
df[‘price’] = df[‘price’].clip(lower=0, upper=1000)
問題 3 基礎

IQRを使った外れ値の上限と下限を計算するコードを書いてください。

【解答例】
Q1 = df[‘price’].quantile(0.25) Q3 = df[‘price’].quantile(0.75) IQR = Q3 – Q1 lower_bound = Q1 – 1.5 * IQR upper_bound = Q3 + 1.5 * IQR print(f”下限: {lower_bound}, 上限: {upper_bound}”)
問題 4 基礎

Min-Max正規化を手動で実装するコードを書いてください。

【解答例】
df[‘price_normalized’] = (df[‘price’] – df[‘price’].min()) / (df[‘price’].max() – df[‘price’].min())
問題 5 応用

Zスコアを計算し、|Z| > 2 の外れ値を抽出するコードを書いてください。

【解答例】
import numpy as np mean = df[‘price’].mean() std = df[‘price’].std() df[‘z_score’] = (df[‘price’] – mean) / std outliers = df[np.abs(df[‘z_score’]) > 2] print(outliers)
問題 6 応用

scikit-learnのStandardScalerを使って標準化するコードを書いてください。

【解答例】
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() df[[‘price_std’, ‘quantity_std’]] = scaler.fit_transform(df[[‘price’, ‘quantity’]])
問題 7 応用

ログ変換を行い、元に戻すコードを書いてください。

【解答例】
import numpy as np # ログ変換 df[‘price_log’] = np.log(df[‘price’]) # 元に戻す df[‘price_restored’] = np.exp(df[‘price_log’])
問題 8 応用

IQRで外れ値を検出し、削除するのではなく上限・下限でクリッピングするコードを書いてください。

【解答例】
Q1 = df[‘price’].quantile(0.25) Q3 = df[‘price’].quantile(0.75) IQR = Q3 – Q1 lower = Q1 – 1.5 * IQR upper = Q3 + 1.5 * IQR df[‘price_clipped’] = df[‘price’].clip(lower=lower, upper=upper)
問題 9 発展

異常値を検出し、NaNに置換してから中央値で補完するコードを書いてください。(条件:priceが0以下または100000以上)

【解答例】
import numpy as np # 異常値をNaNに置換 df.loc[(df[‘price’] <= 0) | (df['price'] >= 100000), ‘price’] = np.nan # 中央値で補完 median_price = df[‘price’].median() df[‘price’] = df[‘price’].fillna(median_price) print(df)
問題 10 発展

以下の条件でデータクリーニングを行うコードを書いてください。
① ageの異常値(0未満 or 120超)をクリッピング
② priceの外れ値(IQR)を削除
③ 残ったデータをMin-Max正規化

【解答例】
import pandas as pd from sklearn.preprocessing import MinMaxScaler # ① ageの異常値をクリッピング df[‘age’] = df[‘age’].clip(lower=0, upper=120) # ② priceの外れ値を削除(IQR) Q1 = df[‘price’].quantile(0.25) Q3 = df[‘price’].quantile(0.75) IQR = Q3 – Q1 lower = Q1 – 1.5 * IQR upper = Q3 + 1.5 * IQR df = df[(df[‘price’] >= lower) & (df[‘price’] <= upper)] # ③ Min-Max正規化 scaler = MinMaxScaler() df[['age_norm', 'price_norm']] = scaler.fit_transform(df[['age', 'price']]) print(df)
📝

学習メモ

データベース設計・データモデリング - Step 8

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE