← 返回提示詞庫
通用 #簡短 難度:入門

缺失值处理器

MISSING VALUES HANDLER

通用缺失值处理提示,采用思维链和思维树框架,支持Python/Pandas/Scikit-learn。

適用平台: ChatGPTClaudeGemini
# 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 / 神经网络]
──────────────────────────────────────────────────────
```

---

## 阶段 1 — 侦察
### *思维链:在采取任何行动之前,请逐步思考。*

**步骤 1.1 — 分析 DATA()**

在继续之前,请明确回答每个问题:

```
1. DATA() 的形状是什么?(行 × 列)
2. 列名及其数据类型是什么?
   - 数值型    → 连续型(浮点数)或离散型(整数/计数)
   - 分类型    → 名义型(无序)或序数型(有等级顺序)
   - 日期时间型 → 顺序时间戳
   - 文本型    → 自由格式字符串
   - 布尔型    → 二进制标志(0/1, True/False)
3. 机器学习任务的背景是什么?
   - 仅限分类 / 回归 / 聚类 / 探索性数据分析
4. 哪些列是特征(X),哪些是目标(y)?
5. 是否存在伪装的缺失值?
   - 注意以下值:"?", "N/A", "unknown", "none", "—", "-", 0 (在年龄/价格中)
   - 这些值在分析前必须转换为 NaN。
6. 关键列的领域/业务规则是什么?
   - 例如:"年龄不能为 0 或负数"
   - 例如:"CustomerID 必须唯一且非空"
   - 例如:"价格是目标 — 缺少此值的行无法使用"
```

**步骤 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]
})

```

```python
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()}")
```

---

## 阶段 2 — 缺失值诊断
### *思维树:在做决定之前,探索所有三个分支。*

对于**每个含有缺失值的列**,同时评估所有三个分支:

```
┌──────────────────────────────────────────────────────────────────┐
│           缺失机制决策树                                         │
│                                                                  │
│  根问题:为什么这个值会缺失?                                    │
│                                                                  │
│  ├── 分支 A: MCAR — 完全随机缺失                                 │
│  │     迹象:   无规律。缺失行看起来与其他行无异。               │
│  │     测试:    可视化热图 / Little's MCAR 测试                  │
│  │     风险:    低 — 可安全删除行或自由插补                     │
│  │     示例: 调查受访者随机跳过一个问题                         │
│  │                                                               │
│  ├── 分支 B: MAR — 随机缺失                                      │
│  │     迹象:   缺失与其他列相关,而不是与缺失值本身相关。       │
│  │     测试:    缺失标志与其它列的相关性                         │
│  │     风险:    中 — 使用条件/分组插补                           │
│  │     示例: 年轻受访者收入缺失更多                             │
│  │                                                               │
│  └── 分支 C: MNAR — 非随机缺失                                   │
│        迹象:   缺失与缺失值本身相关。                           │
│        测试:    领域知识 + 分布比较                              │
│        风险:    高 — 可能严重偏倚模型                            │
│        行动:  领域专家审查 + 创建指示标志                        │
│        示例: 高收入者故意跳过收入字段                           │
└──────────────────────────────────────────────────────────────────┘
```

**对于每个标记的列,填写此分析卡:**

```
┌─────────────────────────────────────────────────────┐
│  列分析卡片                                         │
├─────────────────────────────────────────────────────┤
│  列名             :                                 │
│  缺失百分比       :                                 │
│  数据类型         :                                 │
│  是否为目标变量(y)?: 是 / 否                       │
│  机制             : MCAR / MAR / MNAR               │
│  证据             : (你为什么这么认为)              │
│  缺失值是否       :                                 │
│    具有信息性?    : 是 (创建指示器) / 否            │
│  建议操作         : (参见阶段 3)                    │
└─────────────────────────────────────────────────────┘
```

---

## 阶段 3 — 处理决策框架
### *严格按顺序应用规则。不要跳过。*

---

### 规则 0 — 目标列 (y) — 最高优先级

```
如果缺失的列是目标变量 (y):
  → 总是删除这些行 — 绝不插补目标变量
  → df.dropna(subset=[TARGET_COL], inplace=True)
  → 原因:模型无法从未标记的数据中学习
```

---

### 规则 1 — 阈值检查 (缺失百分比)

```
┌───────────────────────────────────────────────────────────────┐
│  如果 missing% > 60%:                                         │
│    → 选项 A: 完全删除该列                                     │
│      (例外: 领域将其标记为关键 → 标记专家)                  │
│    → 选项 B: 保留 + 创建二元指示符标记                        │
│      (col_was_missing = 1) 然后决定如何插补                  │
│                                                               │
│  如果 30% < missing% ≤ 60%:                                   │
│    → 使用高级插补: KNN 或 MICE (IterativeImputer)             │
│    → 始终首先创建缺失指示符标记                               │
│    → 考虑按组 (条件) 平均值/众数                              │
│                                                               │
│  如果 missing% ≤ 30%:                                         │
│    → 继续执行规则 2                                           │
└───────────────────────────────────────────────────────────────┘
```

---

### 规则 2 — 数据类型路由

```
┌───────────────────────────────────────────────────────────────────────┐
│  数值型 — 连续(浮点数):                                            │
│    ├─ 对称分布(均值 ≈ 中位数)→ 均值填充                              │
│    ├─ 偏态分布(存在异常值)→ 中位数填充                              │
│    ├─ 时间序列 / 有序行             → 前向填充 / 插值                │
│    ├─ MAR(与其他列相关)       → 分组均值                            │
│    └─ 复杂多变量模式          → KNN / MICE                           │
│                                                                       │
│  数值型 — 离散 / 计数(整数):                                       │
│    ├─ 低基数(少量唯一值)    → 众数填充                              │
│    └─ 高基数                       → 中位数或 KNN                     │
│                                                                       │
│  类别型 — 名义(无序):                                              │
│    ├─ 低基数  → 众数填充                                              │
│    ├─ 高基数 → “未知” / “缺失” 作为新类别                            │
│    └─ 怀疑 MNAR   → “未提供” 作为有意义的类别                        │
│                                                                       │
│  类别型 — 有序(有排名顺序):                                        │
│    ├─ 自然排名  → 中位数排名填充                                      │
│    └─ MCAR / MAR       → 众数填充                                    │
│                                                                       │
│  日期时间:                                                           │
│    ├─ 顺序数据  → 前向填充 → 后向填充                                 │
│    └─ 随机间隔      → 插值                                            │
```

│                                                                       │
│  布尔型 / 二进制型:                                                   │
│    └─ 众数填充(或视为分类变量)                                       │
└───────────────────────────────────────────────────────────────────────┘
```

---

### 规则 3 — 高级填充选择指南

```
┌─────────────────────────────────────────────────────────────────┐
│  何时使用每种高级方法                                           │
│                                                                 │
│  按组均值/众数:                                                │
│    → 当缺失值在给定分组列的条件下是 MAR 时                     │
│    → 示例:使用每个 age_group 的平均值填充收入 NaN             │
│    → 比全局平均值更真实                                        │
│                                                                 │
│  KNN Imputer (默认 k=5):                                       │
│    → 当存在多个相关数值列时                                    │
│    → 找到 k 个最近的完整行并平均它们的值                       │
│    → 在大型数据集上速度较慢                                    │
│                                                                 │
│  MICE / IterativeImputer:                                      │
│    → 最强大 — 使用所有其他列对每列进行建模                     │
│    → 最适合具有复杂多变量关系的 MAR                            │
│    → 使用 max_iter=10, random_state=42 以实现可复现性          │
│    → 计算成本最高                                              │
│                                                                 │
│  缺失值指示器标志:                                             │
│    → 始终为 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 — 删除 TARGET 缺失的行(规则 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])

如果 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 — 类别特征填充
# ─────────────────────────────────────────────────────────────────
如果 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])

如果 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 填充
# ─────────────────────────────────────────────────────────────────
如果 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 / 迭代插补器 (最强大,按需使用)
# ─────────────────────────────────────────────────────────────────
# 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"训练集仍有缺失值:\n{remaining_train[remaining_train > 0]}"
assert remaining_test.sum()  == 0, f"测试集仍有缺失值:\n{remaining_test[remaining_test > 0]}"

print("✅ 没有缺失值。数据已准备好用于机器学习。")
print(f"   训练集形状: {X_train.shape} | 测试集形状: {X_test.shape}")
```

---

## 阶段 5 — 综合与决策报告

完成阶段 1-4 后,提交此确切报告:

```
═══════════════════════════════════════════════════════════════
  缺失值处理报告
═══════════════════════════════════════════════════════════════

1. 数据集摘要
   形状         :
   总缺失值     :
   目标列       :
   机器学习任务 :
   模型类型     :

2. 缺失值清单表
   | 列     | 缺失百分比 | 数据类型 | 机制      | 是否提供信息? | 处理方法 |
   |--------|------------|----------|-----------|----------------|----------|
   | ...    | ...        | ...      | ...       | ...            | ...      |

3. 决策日志
   [列名]: [选择该处理方法的原因]
   [列名]: [选择该处理方法的原因]

4. 已删除列
   [列] — 原因:[例如,72% 缺失,非领域关键]

5. 已创建指标旗标
   [col_was_missing] — 原因:[怀疑 MNAR / 缺失百分比高]

6. 使用的插补方法
   [列] → [使用的策略 + 理由]

7. 警告与边缘情况
   - 需要领域专家审查的 MNAR 列
   - 插补过程中做出的假设
   - 在完整 EDA 后标记为需要重新评估的列
   - 发现的任何伪装空值(?、N/A、0 等)

8. 后续步骤 — 插补后检查清单
   ☐ 比较插补前后的分布(直方图)
   ☐ 确认所有插补器仅在训练集上拟合
   ☐ 验证目标列没有数据泄露
   ☐ 插补后重新检查相关矩阵
   ☐ 如果是分类任务,检查类别平衡
   ☐ 记录所有转换以实现可复现性

═══════════════════════════════════════════════════════════════
```

---

## 约束与防护措施

```
✅ 必须始终:
   → 在 df.copy() 上操作 — 绝不修改原始 DATA()
   → 删除目标 (y) 缺失的行 — 绝不插补 y
   → 仅在训练数据上拟合所有插补器
   → 使用已拟合的插补器转换测试集(不重新拟合)
   → 为所有 MNAR 列创建指标旗标
   → 在传递给模型之前验证没有空值残留
   → 检查伪装的缺失值(?、N/A、0、空白、“unknown”)
   → 记录每个决策并给出明确理由

❌ 绝不能:
   → 在未首先检查分布的情况下盲目插补
   → 在未检查列的领域重要性之前删除列
   → 在训练/测试拆分之前在完整数据集上拟合插补器(数据泄露)
   → 忽略 MNAR 列 — 它们可能严重偏倚模型
   → 对所有列应用相同的策略
   → 假设 NaN 是缺失值的唯一形式
```

---

## 快速参考 — 策略速查表

| 情况 | 策略 |
|---|---|
| 目标列 (y) 包含 NaN | 删除行 — 绝不插补 |
| 列缺失值超过 60% | 删除列(或指示器 + 专家审查) |
| 数值型,对称分布 | 均值插补 |
| 数值型,偏态分布 | 中位数插补 |
| 数值型,时间序列 | 前向填充 / 插值 |
| 分类型,低基数 | 众数插补 |
| 分类型,高基数 | 填充为“未知”类别 |
| 怀疑 MNAR(任何类型) | 指示器标志 + 领域审查 |
| MAR,基于组条件 | 按组均值/众数 |
| 复杂多变量模式 | KNN Imputer 或 MICE |
| 基于树的模型 (XGBoost 等) | 允许 NaN;仍需标记 MNAR |
| 线性 / NN / SVM | 必须插补 — 零 NaN 容忍度 |

---

*PROMPT() v1.0 — 专为 IBM GEN AI 工程 / Python 数据分析构建*
*框架:思维链 (CoT) + 思维树 (ToT)*
*参考:Coursera — 使用 Python 处理缺失值*