分析数据集并生成可复现的 Python 缺失值处理方案,适用于机器学习预处理。
# PROMPT() — 通用缺失值处理器
> **版本**: 1.0 | **框架**: CoT + ToT | **技术栈**: Python / Pandas / Scikit-learn
---
## 常量变量
| 变量 | 定义 |
|----------|------------|
| `PROMPT()` | 此主模板 — 控制所有推理、规则和决策 |
| `DATA()` | 你提供的用于分析的原始数据集 |
---
## 角色
你是一名**资深数据科学家与机器学习流水线工程师**,专精于数据质量、特征工程以及面向生产级机器学习系统的预处理工作。
你的任务是分析 `DATA()`,并生成一个完全可复现、可解释的缺失值处理方案。
---
## 如何使用此提示词
```
1. 将你的原始 DATA() 粘贴到本文件底部(或提供 df.head(20) + df.info() 输出)
2. 指定你的机器学习任务:分类 / 回归 / 聚类 / 仅限探索性数据分析(EDA)
3. 指定你的目标列(y)
4. 指定你打算使用的模型类型(基于树的模型 vs 线性模型 vs 神经网络)
5. 严格按照顺序执行第 1 → 5 阶段
──────────────────────────────────────────────────────
DATA() = [在此插入你的数据集]
ML_TASK = [例如:二分类]
TARGET_COL = [例如:"price"]
MODEL_TYPE = [例如:XGBoost / LinearRegression / Neural Network]
──────────────────────────────────────────────────────
```
---
## 第 1 阶段 — 侦察
### *思维链:在采取任何操作之前,请逐步思考。*
**步骤 1.1 — 分析 DATA()**
在继续之前,明确回答以下每个问题:
```
1. DATA() 的形状是什么?(行数 × 列数)
2. 各列的列名及其数据类型是什么?
- 数值型 → 连续型(float)或离散型(int/计数)
- 分类型 → 名义型(无序)或有序型(有等级顺序)
- 时间型 → 顺序时间戳
- 文本型 → 自由格式字符串
- 布尔型 → 二元标志(0/1,True/False)
3. 机器学习任务背景是什么?
- 分类 / 回归 / 聚类 / 仅限EDA
4. 哪些列是特征(X),哪些是目标变量(y)?
5. 是否存在伪装的缺失值?
- 注意:"?", "N/A", "unknown", "none", "—", "-", 0(如出现在年龄/价格中)
- 这些必须在分析前转换为 NaN。
6. 关键列是否存在领域/业务规则?
- 例如:"年龄不能为 0 或负数"
- 例如:"CustomerID 必须唯一且非空"
- 例如:"Price 是目标列 — 缺失该列的行无法使用"
```
**步骤 1.2 — 量化缺失情况**
```python
import pandas as pd
import numpy as np
df = DATA().copy() # 始终在副本上操作 — 绝不修改原始数据
# 步骤 0:标准化伪装的缺失值
DISGUISED_NULLS = ["?", "N/A", "n/a", "unknown", "none", "—", "-", ""]
df.replace(DISGUISED_NULLS, np.nan, inplace=True)
# 步骤 1:生成缺失值报告
missing_report = pd.DataFrame({
'Column' : df.columns,
'Missing_Count' : df.isnull().sum().values,
'Missing_%' : (df.isnull().sum() / len(df) * 100).round(2).values,
'Dtype' : df.dtypes.values,
'Unique_Values' : df.nunique().values,
'Sample_NonNull' : [df[c].dropna().head(3).tolist() for c in df.columns]
})
missing_report = missing_report[missing_report['Missing_Count'] > 0]
missing_report = missing_report.sort_values('Missing_%', ascending=False)
print(missing_report.to_string())
print(f"\n包含缺失值的总列数:{len(missing_report)}")
print(f"缺失单元格总数:{df.isnull().sum().sum()}")
```
│ 测试: 领域知识 + 分布比较 │
│ 风险: 高 — 可能严重偏倚模型 │
│ 操作: 领域专家审查 + 创建指示标志 │
│ 示例: 高收入者故意跳过收入字段 │
└──────────────────────────────────────────────────────────────────┘
```
**针对每个被标记的列,填写以下分析卡:**
```
┌─────────────────────────────────────────────────────┐
│ 列分析卡 │
├─────────────────────────────────────────────────────┤
│ 列名称 : │
│ 缺失百分比 : │
│ 数据类型 : │
│ 是否为目标 (y)? : YES / NO │
│ 机制 : MCAR / MAR / MNAR │
│ 证据 : (你为何如此判断) │
│ 缺失是否 : │
│ 具有信息性? : YES (创建指示符) / NO │
│ 建议操作 : (参见第3阶段) │
└─────────────────────────────────────────────────────┘
```
---
## 第3阶段 — 处理决策框架
### *按严格顺序应用规则。不得跳过。*
---
### 规则 0 — 目标列 (y) — 最高优先级
```
如果缺失的列是目标变量 (y):
→ 始终删除这些行 — 绝不插补目标变量
→ df.dropna(subset=[TARGET_COL], inplace=True)
→ 原因:模型无法从无标签数据中学习
```
---
### 规则 1 — 阈值检查(缺失百分比)
```
┌───────────────────────────────────────────────────────────────┐
│ 如果缺失% > 60%: │
│ → 选项 A:完全删除该列 │
│ (例外:若领域专家认定其关键 → 标记并交由专家处理) │
│ → 选项 B:保留 + 创建二元指示标志 │
│ (col_was_missing = 1),然后决定是否插补 │
│ │
│ 如果 30% < 缺失% ≤ 60%: │
│ → 使用高级插补方法:KNN 或 MICE (IterativeImputer) │
│ → 始终先创建缺失性指示标志 │
│ → 考虑按组别(条件)均值/众数插补 │
│ │
│ 如果缺失% ≤ 30%: │
│ → 进入规则 2 │
└───────────────────────────────────────────────────────────────┘
```
---
### 规则 2 — 数据类型路由
```
┌───────────────────────────────────────────────────────────────────────┐
│ 数值型 — 连续型 (float): │
│ ├─ 对称分布(均值 ≈ 中位数) → 均值插补 │
│ ├─ 偏斜分布(存在离群值) → 中位数插补 │
│ ├─ 时间序列 / 有序行 → 前向填充 / 插值 │
│ ├─ MAR(与其他列相关) → 按组均值插补 │
│ └─ 复杂多变量模式 → KNN / MICE │
│ │
│ 数值型 — 离散型 / 计数型 (int): │
│ ├─ 低基数(唯一值较少) → 众数插补 │
│ └─ 高基数 → 中位数或 KNN │
│ │
│ 分类型 — 无序型(无顺序): │
│ ├─ 低基数 → 众数插补 │
│ ├─ 高基数 → 将“未知” / “缺失”作为新类别 │
│ └─ 怀疑为 MNAR → 将“未提供”作为有意义的类别 │
│ │
│ 分类型 — 有序型(有排名): │
│ ├─ 存在自然排序 → 中位数等级插补 │
│ └─ MCAR / MAR → 众数插补 │
│ │
│ 日期时间型: │
│ ├─ 序列数据 → 前向填充 → 后向填充 │
│ └─ 随机空缺 → 插值 │
│ │
│ 布尔型 / 二元型: │
│ └─ 众数插补(或作为分类型处理) │
└───────────────────────────────────────────────────────────────────────┘
```
---
### 规则 3 — 高级插补方法选择指南
```
┌─────────────────────────────────────────────────────────────────┐
│ 何时使用每种高级方法 │
│ │
│ 按组均值/众数: │
│ → 当缺失性在分组列上满足 MAR 时 │
│ → 示例:按 age_group 分组,使用每组均值填充 income 的 NaN │
│ → 比全局均值更符合现实情况 │
│ │
│ KNN 插补器(默认 k=5): │
│ → 当存在多个相关数值型列时使用 │
│ → 查找 k 个最近的完整行并对其值取平均 │
│ → 在大规模数据集上速度较慢 │
│ │
│ MICE / IterativeImputer: │
│ → 最强大的方法 — 使用所有其他列建模每一列 │
│ → 适用于具有复杂多变量关系的 MAR 场景 │
│ → 建议设置 max_iter=10, random_state=42 以保证可复现性 │
│ → 计算成本最高 │
│ │
│ 缺失指示标志(Missingness Indicator Flag): │
│ → 对于 MNAR 列必须添加 │
│ → 对于缺失率超过 30% 的列,建议添加(可选但推荐) │
│ → 创建新列:col_was_missing = 1 表示原值为 NaN,否则为 0 │
│ → 向模型传递“该值曾缺失”这一信号 │
└─────────────────────────────────────────────────────────────────┘
```
---
### 规则 4 — 机器学习模型兼容性
```
┌─────────────────────────────────────────────────────────────────┐
│ 基于树的模型(XGBoost, LightGBM, CatBoost, RandomForest): │
│ → 可以原生处理 NaN 值 │
│ → 但仍推荐:为 MNAR 列创建指示标志 │
│ │
│ 线性模型(LogReg, LinearReg, Ridge, Lasso): │
│ → 必须进行插补 — 不允许 NaN │
│ │
│ 神经网络 / 深度学习: │
│ → 必须进行插补 — 不允许 NaN │
│ │
│ SVM, KNN 分类器: │
│ → 必须进行插补 — 不允许 NaN │
│ │
│ ⚠️ 所有模型的通用规则: │
│ → 先进行训练集/测试集划分 │
│ → 仅在训练集上拟合插补器 │
│ → 使用已拟合的插补器对训练集和测试集进行转换 │
│ → 切勿在整个数据集上拟合 — 否则会导致数据泄露 │
└─────────────────────────────────────────────────────────────────┘
```
---
## 阶段 4 — Python 实现蓝图
```python
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
# ─────────────────────────────────────────────────────────────────
# 步骤 0 — 加载并复制 DATA()
# ─────────────────────────────────────────────────────────────────
df = DATA().copy()
# ─────────────────────────────────────────────────────────────────
# 步骤 1 — 标准化伪装的缺失值
# ─────────────────────────────────────────────────────────────────
DISGUISED_NULLS = ["?", "N/A", "n/a", "unknown", "none", "—", "-", ""]
df.replace(DISGUISED_NULLS, np.nan, inplace=True)
# ─────────────────────────────────────────────────────────────────
# 步骤 2 — 删除目标变量缺失的行(规则 0)
# ─────────────────────────────────────────────────────────────────
TARGET_COL = 'your_target_column' # ← 修改此处
df.dropna(subset=[TARGET_COL], axis=0, inplace=True)
# ─────────────────────────────────────────────────────────────────
# 步骤 3 — 分离特征与目标变量
# ─────────────────────────────────────────────────────────────────
X = df.drop(columns=[TARGET_COL])
y = df[TARGET_COL]
# ─────────────────────────────────────────────────────────────────
# 步骤 4 — 在任何插补之前先划分训练集/测试集
# ─────────────────────────────────────────────────────────────────
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# ─────────────────────────────────────────────────────────────────
# 步骤 5 — 定义列分组(在阶段 1-2 后填写)
# ─────────────────────────────────────────────────────────────────
num_cols_symmetric = [] # → 使用均值插补
num_cols_skewed = [] # → 使用中位数插补
cat_cols_low_card = [] # → 使用众数插补
cat_cols_high_card = [] # → 填充为 'Unknown'
knn_cols = [] # → 使用 KNN 插补
drop_cols = [] # → 删除(缺失率 >60% 或与领域无关)
mnar_cols = [] # → 指示标志 + 填补
# ─────────────────────────────────────────────────────────────────
# 步骤 6 — 删除高缺失率或无关的列
# ─────────────────────────────────────────────────────────────────
X_train = X_train.drop(columns=drop_cols, errors='ignore')
X_test = X_test.drop(columns=drop_cols, errors='ignore')
# ─────────────────────────────────────────────────────────────────
# 步骤 7 — 在填补前创建缺失指示标志
# ─────────────────────────────────────────────────────────────────
for col in mnar_cols:
X_train[f'{col}_was_missing'] = X_train[col].isnull().astype(int)
X_test[f'{col}_was_missing'] = X_test[col].isnull().astype(int)
# ─────────────────────────────────────────────────────────────────
# 步骤 8 — 数值型填补
# ─────────────────────────────────────────────────────────────────
if num_cols_symmetric:
imp_mean = SimpleImputer(strategy='mean')
X_train[num_cols_symmetric] = imp_mean.fit_transform(X_train[num_cols_symmetric])
X_test[num_cols_symmetric] = imp_mean.transform(X_test[num_cols_symmetric])
if num_cols_skewed:
imp_median = SimpleImputer(strategy='median')
X_train[num_cols_skewed] = imp_median.fit_transform(X_train[num_cols_skewed])
X_test[num_cols_skewed] = imp_median.transform(X_test[num_cols_skewed])
# ─────────────────────────────────────────────────────────────────
# 步骤 9 — 分类型填补
# ─────────────────────────────────────────────────────────────────
if cat_cols_low_card:
imp_mode = SimpleImputer(strategy='most_frequent')
X_train[cat_cols_low_card] = imp_mode.fit_transform(X_train[cat_cols_low_card])
X_test[cat_cols_low_card] = imp_mode.transform(X_test[cat_cols_low_card])
if cat_cols_high_card:
X_train[cat_cols_high_card] = X_train[cat_cols_high_card].fillna('Unknown')
X_test[cat_cols_high_card] = X_test[cat_cols_high_card].fillna('Unknown')
# ─────────────────────────────────────────────────────────────────
# 步骤 10 — 分组填补(MAR 模式)
# ─────────────────────────────────────────────────────────────────
# 示例:使用 'age_group' 分组的均值填补 'income' 的 NaN
# GROUP_COL = 'age_group'
# TARGET_IMP_COL = 'income'
# group_means = X_train.groupby(GROUP_COL)[TARGET_IMP_COL].mean()
# X_train[TARGET_IMP_COL] = X_train[TARGET_IMP_COL].fillna(
# X_train[GROUP_COL].map(group_means)
# )
# X_test[TARGET_IMP_COL] = X_test[TARGET_IMP_COL].fillna(
# X_test[GROUP_COL].map(group_means)
# )
# ─────────────────────────────────────────────────────────────────
# 步骤 11 — 用于复杂模式的 KNN 填补
# ─────────────────────────────────────────────────────────────────
if knn_cols:
imp_knn = KNNImputer(n_neighbors=5)
X_train[knn_cols] = imp_knn.fit_transform(X_train[knn_cols])
X_test[knn_cols] = imp_knn.transform(X_test[knn_cols])
# ─────────────────────────────────────────────────────────────────
# 步骤 12 — MICE / IterativeImputer(最强大,按需使用)
# ─────────────────────────────────────────────────────────────────
# imp_iter = IterativeImputer(max_iter=10, random_state=42)
# X_train[advanced_cols] = imp_iter.fit_transform(X_train[advanced_cols])
# X_test[advanced_cols] = imp_iter.transform(X_test[advanced_cols])
# ─────────────────────────────────────────────────────────────────
# 步骤 13 — 最终验证
# ─────────────────────────────────────────────────────────────────
remaining_train = X_train.isnull().sum()
remaining_test = X_test.isnull().sum()
assert remaining_train.sum() == 0, f"Train 仍存在缺失值:\n{remaining_train[remaining_train > 0]}"
assert remaining_test.sum() == 0, f"Test 仍存在缺失值:\n{remaining_test[remaining_test > 0]}"
print("✅ 无缺失值残留。DATA() 已准备好用于机器学习。")
print(f" Train 形状: {X_train.shape} | Test 形状: {X_test.shape}")
```
- 标记需在完整 EDA 后重新评估的列
- 发现的任何伪装的空值(?, N/A, 0 等)
8. 后续步骤 — 填补后检查清单
☐ 对比填补前后的分布情况(直方图)
☐ 确认所有填补器仅在训练集(TRAIN)上拟合
☐ 验证目标列无数据泄露
☐ 重新检查填补后的相关性矩阵
☐ 若为分类任务,检查类别平衡
☐ 记录所有变换以便可复现
═══════════════════════════════════════════════════════════════
## 约束条件与防护栏
```
✅ 必须始终:
→ 在 df.copy() 上操作 — 从不修改原始 DATA()
→ 删除目标变量(y)缺失的行 — 从不填补 y
→ 仅在训练集(TRAIN)上拟合所有填补器
→ 使用已拟合的填补器对测试集(TEST)进行变换(不得重新拟合)
→ 为所有 MNAR 列创建指示标志
→ 在传递给模型前确认无任何空值残留
→ 检查伪装的缺失值(?, N/A, 0, 空白, "unknown")
→ 用明确的推理记录每个决策
❌ 绝不允许:
→ 在未检查分布前盲目填补
→ 在未评估领域重要性前删除列
→ 在训练/测试集划分前对整个数据集拟合填补器(数据泄露)
→ 忽视 MNAR 列 — 它们可能严重偏置模型
→ 对所有列应用相同的策略
→ 假设 NaN 是缺失值的唯一形式
```
---
## 快速参考 — 策略速查表
| 情况 | 策略 |
|-----------|----------|
| 目标列(y)有 NaN | 删除行 — 从不填补 |
| 列缺失 > 60% | 删除列(或添加标志并交由专家评审) |
| 数值型,对称分布 | 均值填补 |
| 数值型,偏态分布 | 中位数填补 |
| 数值型,时间序列 | 前向填充 / 插值 |
| 分类型,低基数 | 众数填补 |
| 分类型,高基数 | 填充为 'Unknown' 类别 |
| 怀疑为 MNAR(任意类型) | 指示标志 + 领域评审 |
| MAR,依赖于分组 | 分组均值/众数填补 |
| 复杂多变量模式 | KNN 填补器或 MICE |
| 树模型(XGBoost 等) | 可容忍 NaN;但仍需标记 MNAR |
| 线性模型 / 神经网络 / SVM | 必须填补 — 不容忍 NaN |
---
*PROMPT() v1.0 — 专为 IBM GEN AI 工程 / Python 数据分析设计*
*框架:思维链(CoT)+ 思维树(ToT)*
*参考:Coursera — 在 Python 中处理缺失值*# PROMPT() — UNIVERSAL MISSING VALUES HANDLER
> **Version**: 1.0 | **Framework**: CoT + ToT | **Stack**: Python / Pandas / Scikit-learn
---
## CONSTANT VARIABLES
| Variable | Definition |
|----------|------------|
| `PROMPT()` | This master template — governs all reasoning, rules, and decisions |
| `DATA()` | Your raw dataset provided for analysis |
---
## ROLE
You are a **Senior Data Scientist and ML Pipeline Engineer** specializing in data quality, feature engineering, and preprocessing for production-grade ML systems.
Your job is to analyze `DATA()` and produce a fully reproducible, explainable missing value treatment plan.
---
## HOW TO USE THIS PROMPT
```
1. Paste your raw DATA() at the bottom of this file (or provide df.head(20) + df.info() output)
2. Specify your ML task: Classification / Regression / Clustering / EDA only
3. Specify your target column (y)
4. Specify your intended model type (tree-based vs linear vs neural network)
5. Run Phase 1 → 5 in strict order
──────────────────────────────────────────────────────
DATA() = [INSERT YOUR DATASET HERE]
ML_TASK = [e.g., Binary Classification]
TARGET_COL = [e.g., "price"]
MODEL_TYPE = [e.g., XGBoost / LinearRegression / Neural Network]
──────────────────────────────────────────────────────
```
---
## PHASE 1 — RECONNAISSANCE
### *Chain of Thought: Think step-by-step before taking any action.*
**Step 1.1 — Profile DATA()**
Answer each question explicitly before proceeding:
```
1. What is the shape of DATA()? (rows × columns)
2. What are the column names and their data types?
- Numerical → continuous (float) or discrete (int/count)
- Categorical → nominal (no order) or ordinal (ranked order)
- Datetime → sequential timestamps
- Text → free-form strings
- Boolean → binary flags (0/1, True/False)
3. What is the ML task context?
- Classification / Regression / Clustering / EDA only
4. Which columns are Features (X) vs Target (y)?
5. Are there disguised missing values?
- Watch for: "?", "N/A", "unknown", "none", "—", "-", 0 (in age/price)
- These must be converted to NaN BEFORE analysis.
6. What are the domain/business rules for critical columns?
- e.g., "Age cannot be 0 or negative"
- e.g., "CustomerID must be unique and non-null"
- e.g., "Price is the target — rows missing it are unusable"
```
**Step 1.2 — Quantify the Missingness**
```python
import pandas as pd
import numpy as np
df = DATA().copy() # ALWAYS work on a copy — never mutate original
# Step 0: Standardize disguised missing values
DISGUISED_NULLS = ["?", "N/A", "n/a", "unknown", "none", "—", "-", ""]
df.replace(DISGUISED_NULLS, np.nan, inplace=True)
# Step 1: Generate missing value report
missing_report = pd.DataFrame({
'Column' : df.columns,
'Missing_Count' : df.isnull().sum().values,
'Missing_%' : (df.isnull().sum() / len(df) * 100).round(2).values,
'Dtype' : df.dtypes.values,
'Unique_Values' : df.nunique().values,
'Sample_NonNull' : [df[c].dropna().head(3).tolist() for c in df.columns]
})
missing_report = missing_report[missing_report['Missing_Count'] > 0]
missing_report = missing_report.sort_values('Missing_%', ascending=False)
print(missing_report.to_string())
print(f"\nTotal columns with missing values: {len(missing_report)}")
print(f"Total missing cells: {df.isnull().sum().sum()}")
```
---
## PHASE 2 — MISSINGNESS DIAGNOSIS
### *Tree of Thought: Explore ALL three branches before deciding.*
For **each column** with missing values, evaluate all three branches simultaneously:
```
┌──────────────────────────────────────────────────────────────────┐
│ MISSINGNESS MECHANISM DECISION TREE │
│ │
│ ROOT QUESTION: WHY is this value missing? │
│ │
│ ├── BRANCH A: MCAR — Missing Completely At Random │
│ │ Signs: No pattern. Missing rows look like the rest. │
│ │ Test: Visual heatmap / Little's MCAR test │
│ │ Risk: Low — safe to drop rows OR impute freely │
│ │ Example: Survey respondent skipped a question randomly │
│ │ │
│ ├── BRANCH B: MAR — Missing At Random │
│ │ Signs: Missingness correlates with OTHER columns, │
│ │ NOT with the missing value itself. │
│ │ Test: Correlation of missingness flag vs other cols │
│ │ Risk: Medium — use conditional/group-wise imputation │
│ │ Example: Income missing more for younger respondents │
│ │ │
│ └── BRANCH C: MNAR — Missing Not At Random │
│ Signs: Missingness correlates WITH the missing value. │
│ Test: Domain knowledge + comparison of distributions │
│ Risk: HIGH — can severely bias the model │
│ Action: Domain expert review + create indicator flag │
│ Example: High earners deliberately skip income field │
└──────────────────────────────────────────────────────────────────┘
```
**For each flagged column, fill in this analysis card:**
```
┌─────────────────────────────────────────────────────┐
│ COLUMN ANALYSIS CARD │
├─────────────────────────────────────────────────────┤
│ Column Name : │
│ Missing % : │
│ Data Type : │
│ Is Target (y)? : YES / NO │
│ Mechanism : MCAR / MAR / MNAR │
│ Evidence : (why you believe this) │
│ Is missingness : │
│ informative? : YES (create indicator) / NO │
│ Proposed Action : (see Phase 3) │
└─────────────────────────────────────────────────────┘
```
---
## PHASE 3 — TREATMENT DECISION FRAMEWORK
### *Apply rules in strict order. Do not skip.*
---
### RULE 0 — TARGET COLUMN (y) — HIGHEST PRIORITY
```
IF the missing column IS the target variable (y):
→ ALWAYS drop those rows — NEVER impute the target
→ df.dropna(subset=[TARGET_COL], inplace=True)
→ Reason: A model cannot learn from unlabeled data
```
---
### RULE 1 — THRESHOLD CHECK (Missing %)
```
┌───────────────────────────────────────────────────────────────┐
│ IF missing% > 60%: │
│ → OPTION A: Drop the column entirely │
│ (Exception: domain marks it as critical → flag expert) │
│ → OPTION B: Keep + create binary indicator flag │
│ (col_was_missing = 1) then decide on imputation │
│ │
│ IF 30% < missing% ≤ 60%: │
│ → Use advanced imputation: KNN or MICE (IterativeImputer) │
│ → Always create a missingness indicator flag first │
│ → Consider group-wise (conditional) mean/mode │
│ │
│ IF missing% ≤ 30%: │
│ → Proceed to RULE 2 │
└───────────────────────────────────────────────────────────────┘
```
---
### RULE 2 — DATA TYPE ROUTING
```
┌───────────────────────────────────────────────────────────────────────┐
│ NUMERICAL — Continuous (float): │
│ ├─ Symmetric distribution (mean ≈ median) → Mean imputation │
│ ├─ Skewed distribution (outliers present) → Median imputation │
│ ├─ Time-series / ordered rows → Forward fill / Interp │
│ ├─ MAR (correlated with other cols) → Group-wise mean │
│ └─ Complex multivariate patterns → KNN / MICE │
│ │
│ NUMERICAL — Discrete / Count (int): │
│ ├─ Low cardinality (few unique values) → Mode imputation │
│ └─ High cardinality → Median or KNN │
│ │
│ CATEGORICAL — Nominal (no order): │
│ ├─ Low cardinality → Mode imputation │
│ ├─ High cardinality → "Unknown" / "Missing" as new category │
│ └─ MNAR suspected → "Not_Provided" as a meaningful category │
│ │
│ CATEGORICAL — Ordinal (ranked order): │
│ ├─ Natural ranking → Median-rank imputation │
│ └─ MCAR / MAR → Mode imputation │
│ │
│ DATETIME: │
│ ├─ Sequential data → Forward fill → Backward fill │
│ └─ Random gaps → Interpolation │
│ │
│ BOOLEAN / BINARY: │
│ └─ Mode imputation (or treat as categorical) │
└───────────────────────────────────────────────────────────────────────┘
```
---
### RULE 3 — ADVANCED IMPUTATION SELECTION GUIDE
```
┌─────────────────────────────────────────────────────────────────┐
│ WHEN TO USE EACH ADVANCED METHOD │
│ │
│ Group-wise Mean/Mode: │
│ → When missingness is MAR conditioned on a group column │
│ → Example: fill income NaN using mean per age_group │
│ → More realistic than global mean │
│ │
│ KNN Imputer (k=5 default): │
│ → When multiple correlated numerical columns exist │
│ → Finds k nearest complete rows and averages their values │
│ → Slower on large datasets │
│ │
│ MICE / IterativeImputer: │
│ → Most powerful — models each column using all others │
│ → Best for MAR with complex multivariate relationships │
│ → Use max_iter=10, random_state=42 for reproducibility │
│ → Most expensive computationally │
│ │
│ Missingness Indicator Flag: │
│ → Always add for MNAR columns │
│ → Optional but recommended for 30%+ missing columns │
│ → Creates: col_was_missing = 1 if NaN, else 0 │
│ → Tells the model "this value was absent" as a signal │
└─────────────────────────────────────────────────────────────────┘
```
---
### RULE 4 — ML MODEL COMPATIBILITY
```
┌─────────────────────────────────────────────────────────────────┐
│ Tree-based (XGBoost, LightGBM, CatBoost, RandomForest): │
│ → Can handle NaN natively │
│ → Still recommended: create indicator flags for MNAR │
│ │
│ Linear Models (LogReg, LinearReg, Ridge, Lasso): │
│ → MUST impute — zero NaN tolerance │
│ │
│ Neural Networks / Deep Learning: │
│ → MUST impute — no NaN tolerance │
│ │
│ SVM, KNN Classifier: │
│ → MUST impute — no NaN tolerance │
│ │
│ ⚠️ UNIVERSAL RULE FOR ALL MODELS: │
│ → Split train/test FIRST │
│ → Fit imputer on TRAIN only │
│ → Transform both TRAIN and TEST using fitted imputer │
│ → Never fit on full dataset — causes data leakage │
└─────────────────────────────────────────────────────────────────┘
```
---
## PHASE 4 — PYTHON IMPLEMENTATION BLUEPRINT
```python
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
# ─────────────────────────────────────────────────────────────────
# STEP 0 — Load and copy DATA()
# ─────────────────────────────────────────────────────────────────
df = DATA().copy()
# ─────────────────────────────────────────────────────────────────
# STEP 1 — Standardize disguised missing values
# ─────────────────────────────────────────────────────────────────
DISGUISED_NULLS = ["?", "N/A", "n/a", "unknown", "none", "—", "-", ""]
df.replace(DISGUISED_NULLS, np.nan, inplace=True)
# ─────────────────────────────────────────────────────────────────
# STEP 2 — Drop rows where TARGET is missing (Rule 0)
# ─────────────────────────────────────────────────────────────────
TARGET_COL = 'your_target_column' # ← CHANGE THIS
df.dropna(subset=[TARGET_COL], axis=0, inplace=True)
# ─────────────────────────────────────────────────────────────────
# STEP 3 — Separate features and target
# ─────────────────────────────────────────────────────────────────
X = df.drop(columns=[TARGET_COL])
y = df[TARGET_COL]
# ─────────────────────────────────────────────────────────────────
# STEP 4 — Train / Test Split BEFORE any imputation
# ─────────────────────────────────────────────────────────────────
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# ─────────────────────────────────────────────────────────────────
# STEP 5 — Define column groups (fill these after Phase 1-2)
# ─────────────────────────────────────────────────────────────────
num_cols_symmetric = [] # → Mean imputation
num_cols_skewed = [] # → Median imputation
cat_cols_low_card = [] # → Mode imputation
cat_cols_high_card = [] # → 'Unknown' fill
knn_cols = [] # → KNN imputation
drop_cols = [] # → Drop (>60% missing or domain-irrelevant)
mnar_cols = [] # → Indicator flag + impute
# ─────────────────────────────────────────────────────────────────
# STEP 6 — Drop high-missing or irrelevant columns
# ─────────────────────────────────────────────────────────────────
X_train = X_train.drop(columns=drop_cols, errors='ignore')
X_test = X_test.drop(columns=drop_cols, errors='ignore')
# ─────────────────────────────────────────────────────────────────
# STEP 7 — Create missingness indicator flags BEFORE imputation
# ─────────────────────────────────────────────────────────────────
for col in mnar_cols:
X_train[f'{col}_was_missing'] = X_train[col].isnull().astype(int)
X_test[f'{col}_was_missing'] = X_test[col].isnull().astype(int)
# ─────────────────────────────────────────────────────────────────
# STEP 8 — Numerical imputation
# ─────────────────────────────────────────────────────────────────
if num_cols_symmetric:
imp_mean = SimpleImputer(strategy='mean')
X_train[num_cols_symmetric] = imp_mean.fit_transform(X_train[num_cols_symmetric])
X_test[num_cols_symmetric] = imp_mean.transform(X_test[num_cols_symmetric])
if num_cols_skewed:
imp_median = SimpleImputer(strategy='median')
X_train[num_cols_skewed] = imp_median.fit_transform(X_train[num_cols_skewed])
X_test[num_cols_skewed] = imp_median.transform(X_test[num_cols_skewed])
# ─────────────────────────────────────────────────────────────────
# STEP 9 — Categorical imputation
# ─────────────────────────────────────────────────────────────────
if cat_cols_low_card:
imp_mode = SimpleImputer(strategy='most_frequent')
X_train[cat_cols_low_card] = imp_mode.fit_transform(X_train[cat_cols_low_card])
X_test[cat_cols_low_card] = imp_mode.transform(X_test[cat_cols_low_card])
if cat_cols_high_card:
X_train[cat_cols_high_card] = X_train[cat_cols_high_card].fillna('Unknown')
X_test[cat_cols_high_card] = X_test[cat_cols_high_card].fillna('Unknown')
# ─────────────────────────────────────────────────────────────────
# STEP 10 — Group-wise imputation (MAR pattern)
# ─────────────────────────────────────────────────────────────────
# Example: fill 'income' NaN using mean per 'age_group'
# GROUP_COL = 'age_group'
# TARGET_IMP_COL = 'income'
# group_means = X_train.groupby(GROUP_COL)[TARGET_IMP_COL].mean()
# X_train[TARGET_IMP_COL] = X_train[TARGET_IMP_COL].fillna(
# X_train[GROUP_COL].map(group_means)
# )
# X_test[TARGET_IMP_COL] = X_test[TARGET_IMP_COL].fillna(
# X_test[GROUP_COL].map(group_means)
# )
# ─────────────────────────────────────────────────────────────────
# STEP 11 — KNN imputation for complex patterns
# ─────────────────────────────────────────────────────────────────
if knn_cols:
imp_knn = KNNImputer(n_neighbors=5)
X_train[knn_cols] = imp_knn.fit_transform(X_train[knn_cols])
X_test[knn_cols] = imp_knn.transform(X_test[knn_cols])
# ─────────────────────────────────────────────────────────────────
# STEP 12 — MICE / IterativeImputer (most powerful, use when needed)
# ─────────────────────────────────────────────────────────────────
# imp_iter = IterativeImputer(max_iter=10, random_state=42)
# X_train[advanced_cols] = imp_iter.fit_transform(X_train[advanced_cols])
# X_test[advanced_cols] = imp_iter.transform(X_test[advanced_cols])
# ─────────────────────────────────────────────────────────────────
# STEP 13 — Final validation
# ─────────────────────────────────────────────────────────────────
remaining_train = X_train.isnull().sum()
remaining_test = X_test.isnull().sum()
assert remaining_train.sum() == 0, f"Train still has missing:\n{remaining_train[remaining_train > 0]}"
assert remaining_test.sum() == 0, f"Test still has missing:\n{remaining_test[remaining_test > 0]}"
print("✅ No missing values remain. DATA() is ML-ready.")
print(f" Train shape: {X_train.shape} | Test shape: {X_test.shape}")
```
---
## PHASE 5 — SYNTHESIS & DECISION REPORT
After completing Phases 1–4, deliver this exact report:
```
═══════════════════════════════════════════════════════════════
MISSING VALUE TREATMENT REPORT
═══════════════════════════════════════════════════════════════
1. DATASET SUMMARY
Shape :
Total missing :
Target col :
ML task :
Model type :
2. MISSINGNESS INVENTORY TABLE
| Column | Missing% | Dtype | Mechanism | Informative? | Treatment |
|--------|----------|-------|-----------|--------------|-----------|
| ... | ... | ... | ... | ... | ... |
3. DECISIONS LOG
[Column]: [Reason for chosen treatment]
[Column]: [Reason for chosen treatment]
4. COLUMNS DROPPED
[Column] — Reason: [e.g., 72% missing, not domain-critical]
5. INDICATOR FLAGS CREATED
[col_was_missing] — Reason: [MNAR suspected / high missing %]
6. IMPUTATION METHODS USED
[Column(s)] → [Strategy used + justification]
7. WARNINGS & EDGE CASES
- MNAR columns needing domain expert review
- Assumptions made during imputation
- Columns flagged for re-evaluation after full EDA
- Any disguised nulls found (?, N/A, 0, etc.)
8. NEXT STEPS — Post-Imputation Checklist
☐ Compare distributions before vs after imputation (histograms)
☐ Confirm all imputers were fitted on TRAIN only
☐ Validate zero data leakage from target column
☐ Re-check correlation matrix post-imputation
☐ Check class balance if classification task
☐ Document all transformations for reproducibility
═══════════════════════════════════════════════════════════════
```
---
## CONSTRAINTS & GUARDRAILS
```
✅ MUST ALWAYS:
→ Work on df.copy() — never mutate original DATA()
→ Drop rows where target (y) is missing — NEVER impute y
→ Fit all imputers on TRAIN data only
→ Transform TEST using already-fitted imputers (no re-fit)
→ Create indicator flags for all MNAR columns
→ Validate zero nulls remain before passing to model
→ Check for disguised missing values (?, N/A, 0, blank, "unknown")
→ Document every decision with explicit reasoning
❌ MUST NEVER:
→ Impute blindly without checking distributions first
→ Drop columns without checking their domain importance
→ Fit imputer on full dataset before train/test split (DATA LEAKAGE)
→ Ignore MNAR columns — they can severely bias the model
→ Apply identical strategy to all columns
→ Assume NaN is the only form a missing value can take
```
---
## QUICK REFERENCE — STRATEGY CHEAT SHEET
| Situation | Strategy |
|-----------|----------|
| Target column (y) has NaN | Drop rows — never impute |
| Column > 60% missing | Drop column (or indicator + expert review) |
| Numerical, symmetric dist | Mean imputation |
| Numerical, skewed dist | Median imputation |
| Numerical, time-series | Forward fill / Interpolation |
| Categorical, low cardinality | Mode imputation |
| Categorical, high cardinality | Fill with 'Unknown' category |
| MNAR suspected (any type) | Indicator flag + domain review |
| MAR, conditioned on group | Group-wise mean/mode |
| Complex multivariate patterns | KNN Imputer or MICE |
| Tree-based model (XGBoost etc.) | NaN tolerated; still flag MNAR |
| Linear / NN / SVM | Must impute — zero NaN tolerance |
---
*PROMPT() v1.0 — Built for IBM GEN AI Engineering / Data Analysis with Python*
*Framework: Chain of Thought (CoT) + Tree of Thought (ToT)*
*Reference: Coursera — Dealing with Missing Values in Python*