- name
- data-preprocessing
- description
- 数据清洗、EDA、缺失值处理、异常值检测、相关性分析。触发词: 数据预处理、数据清洗、EDA、缺失值、异常值、data preprocessing、数据探索。
- argument-hint
- ["data-file-or-data-directory"]
- allowed-tools
- Bash(*), Read, Write, Edit, Grep, Glob, Agent, mcp__codex__codex, mcp__codex__codex-reply
# 数据预处理与探索性分析
执行描述: $ARGUMENTS
## Constants
- **OUTPUT_REPORT = `DATA_PREPROCESSING_REPORT.md`** — 主输出文件,供 `model-creator`、`solve-plan` 使用。
- **CLEAN_DATA_DIR = `data/cleaned/`** — 清洗后数据的存放目录。
- **EDA_FIGURE_DIR = `figures/eda/`** — EDA 图表存放目录。
- **MAX_COLUMNS_FULL_PROFILE = `50`** — 超过 50 列时只对高相关列做完整画像,其余做摘要。
- **MAX_ROWS_FULL_SCAN = `100000`** — 超过 10 万行时先采样分析,再对全量数据做统计。
- **MISSING_THRESHOLD_WARN = `0.05`** — 缺失率超过 5% 的列必须在报告中标红。
- **MISSING_THRESHOLD_DROP = `0.50`** — 缺失率超过 50% 的列建议删除,除非有充分理由保留。
- **OUTLIER_Z_THRESHOLD = `3.0`** — Z-score 阈值,超过即标记为疑似异常值。
- **OUTLIER_IQR_MULTIPLIER = `1.5`** — IQR 方法乘数,与 Z-score 交叉验证。
- **CORRELATION_THRESHOLD = `0.85`** — 相关系数绝对值超过 0.85 的变量对需要标注多重共线性风险。
- **REVIEWER_MODEL = `gpt-5.4`** — Codex MCP 交叉验证模型。
- **REASONING_EFFORT = `high`** — 数据预处理验证使用高推理强度。
- **ARTIFACT_DIR = `artifacts/`** — 存放中间结构化文件。
## Workflow
### Phase 1: 定位与加载数据
Input: `$ARGUMENTS`(文件路径或目录路径)。
Output: 数据成功加载,基础元信息记录到 `artifacts/data_profile_raw.json`。
1. 如果 `$ARGUMENTS` 是文件路径,直接读取。
2. 如果 `$ARGUMENTS` 是目录路径,扫描目录下所有 `.csv`、`.xlsx`、`.xls`、`.json`、`.txt` 文件。
3. 如果无参数,按优先级搜索:`data/`、`dataset/`、`附件/`、项目根目录。
4. 对每个文件尝试自动检测编码(UTF-8、GBK、GB2312)和分隔符。
5. 记录文件名、行数、列数、文件大小、编码格式。
6. 如果数据分散在多个文件中,尝试识别它们之间的关联字段。
7. 将加载结果保存到 `artifacts/data_profile_raw.json`。
```python
import pandas as pd
from pathlib import Path
import json
import chardet
def detect_encoding(filepath: str) -> str:
with open(filepath, "rb") as f:
raw = f.read(min(100000, Path(filepath).stat().st_size))
result = chardet.detect(raw)
return result.get("encoding", "utf-8") or "utf-8"
def load_data(filepath: str) -> pd.DataFrame:
enc = detect_encoding(filepath)
suffix = Path(filepath).suffix.lower()
if suffix in [".csv", ".txt"]:
for sep in [",", "\t", ";", "|"]:
try:
df = pd.read_csv(filepath, encoding=enc, sep=sep, nrows=5)
if len(df.columns) > 1:
return pd.read_csv(filepath, encoding=enc, sep=sep)
except Exception:
continue
return pd.read_csv(filepath, encoding=enc)
elif suffix in [".xlsx", ".xls"]:
return pd.read_excel(filepath)
elif suffix == ".json":
return pd.read_json(filepath, encoding=enc)
else:
raise ValueError(f"Unsupported file type: {suffix}")
# Example usage
data_files = list(Path("data").glob("*.*"))
profiles = []
for fp in data_files:
try:
df = load_data(str(fp))
profiles.append({
"file": fp.name,
"rows": len(df),
"cols": len(df.columns),
"columns": list(df.columns),
"dtypes": {c: str(df[c].dtype) for c in df.columns},
"size_kb": round(fp.stat().st_size / 1024, 1),
})
except Exception as e:
profiles.append({"file": fp.name, "error": str(e)})
Path("artifacts").mkdir(exist_ok=True)
Path("artifacts/data_profile_raw.json").write_text(
json.dumps(profiles, ensure_ascii=False, indent=2), encoding="utf-8"
)
```
### Phase 2: 缺失值分析
Input: 已加载的 DataFrame。
Output: `artifacts/missing_value_report.json` 与缺失值热力图。
1. 对每列统计缺失率,按缺失率降序排列。
2. 缺失率 > `MISSING_THRESHOLD_WARN` (5%) 的列标记为"需要关注"。
3. 缺失率 > `MISSING_THRESHOLD_DROP` (50%) 的列标记为"建议删除"。
4. 分析缺失模式:完全随机缺失 (MCAR)、随机缺失 (MAR)、非随机缺失 (MNAR)。
5. 检查行级缺失:是否存在整行大面积缺失的情况。
6. 生成缺失值热力图,保存到 `figures/eda/missing_heatmap.png`。
7. 为每种缺失列推荐处理策略:
- 数值型:均值/中位数/KNN/多重插补
- 分类型:众数/单独类别/"Unknown"
- 时序型:前向/后向填充/线性插值
8. 如果某列缺失率为 0,也要记录(证明数据完整性)。
9. 在报告中单列"缺失值处理决策表"。
10. 如果存在明显的非随机缺失模式,必须在报告中警告可能影响建模结果。
```python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
def analyze_missing(df: pd.DataFrame, fig_dir: str = "figures/eda") -> dict:
missing = df.isnull().sum()
missing_pct = (missing / len(df)).round(4)
report = {
"total_rows": len(df),
"total_cols": len(df.columns),
"columns": {}
}
for col in df.columns:
pct = float(missing_pct[col])
status = "ok"
if pct > 0.50:
status = "suggest_drop"
elif pct > 0.05:
status = "needs_attention"
report["columns"][col] = {
"missing_count": int(missing[col]),
"missing_pct": pct,
"status": status,
"dtype": str(df[col].dtype),
}
Path(fig_dir).mkdir(parents=True, exist_ok=True)
fig, ax = plt.subplots(figsize=(12, max(4, len(df.columns) * 0.3)))
sns.heatmap(df.isnull().T, cbar=True, yticklabels=True, ax=ax, cmap="YlOrRd")
ax.set_title("Missing Value Heatmap")
fig.tight_layout()
fig.savefig(f"{fig_dir}/missing_heatmap.png", dpi=150)
plt.close(fig)
return report
```
### Phase 3: 异常值检测
Input: 已加载的 DataFrame(数值列)。
Output: `artifacts/outlier_report.json` 与箱线图。
1. 对每个数值列同时使用 Z-score 和 IQR 方法检测异常值。
2. 两种方法都标记为异常的数据点定义为"强异常"。
3. 只有一种方法标记的定义为"弱异常"。
4. 统计每列的强异常和弱异常数量与占比。
5. 对异常值进行分类:
- 录入错误(值域不合理,如负年龄、超范围百分比)
- 极端但合理(如收入分布的极右尾部)
- 结构性异常(特定分组中的系统性偏移)
6. 生成箱线图矩阵,保存到 `figures/eda/boxplots.png`。
7. 为每种异常值推荐处理策略:
- 录入错误:修正或删除
- 极端合理值:Winsorize 或保留并标注
- 结构性异常:分组处理或单独建模
8. 不要自动删除异常值。所有删除操作必须写进决策表供用户确认。
9. 如果某列无异常,也要记录(证明数据质量良好)。
10. 异常值对数模竞赛的影响通常比对 ML 竞赛大,因为评委关注模型假设合理性。
```python
import numpy as np
def detect_outliers(df: pd.DataFrame, z_thresh: float = 3.0, iqr_mult: float = 1.5) -> dict:
report = {}
numeric_cols = df.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
series = df[col].dropna()
if len(series) == 0:
continue
z_scores = np.abs((series - series.mean()) / series.std())
z_outliers = set(series[z_scores > z_thresh].index)
q1, q3 = series.quantile(0.25), series.quantile(0.75)
iqr = q3 - q1
iqr_outliers = set(series[(series < q1 - iqr_mult * iqr) | (series > q3 + iqr_mult * iqr)].index)
strong = z_outliers & iqr_outliers
weak = (z_outliers | iqr_outliers) - strong
report[col] = {
"strong_outliers": len(strong),
"weak_outliers": len(weak),
"strong_pct": round(len(strong) / len(series), 4),
"min": float(series.min()),
"max": float(series.max()),
"mean": float(series.mean()),
"std": float(series.std()),
}
return report
```
### Phase 4: 分布与相关性分析
Input: 已加载的 DataFrame。
Output: `artifacts/correlation_report.json`、分布图、相关性热力图。
1. 对每个数值列绘制直方图+KDE,观察分布形态。
2. 计算偏度和峰度,判断是否需要变换(对数、Box-Cox)。
3. 对分类列绘制频率条形图,标注类别数量和各类别占比。
4. 计算数值列之间的 Pearson 相关系数矩阵。
5. 对非线性关系补充 Spearman 秩相关系数。
6. 相关系数绝对值 > `CORRELATION_THRESHOLD` (0.85) 的变量对标注"多重共线性风险"。
7. 生成相关性热力图,保存到 `figures/eda/correlation_heatmap.png`。
8. 如果列数过多 (> `MAX_COLUMNS_FULL_PROFILE`),先筛选高方差和高相关列。
9. 特别关注自变量与因变量之间的相关性,如果因变量已知的话。
10. 记录变量间的非线性关系提示,供后续建模方法选择参考。
```python
def correlation_analysis(df: pd.DataFrame, threshold: float = 0.85, fig_dir: str = "figures/eda") -> dict:
numeric_df = df.select_dtypes(include=[np.number])
pearson = numeric_df.corr(method="pearson")
spearman = numeric_df.corr(method="spearman")
high_corr_pairs = []
cols = pearson.columns
for i in range(len(cols)):
for j in range(i + 1, len(cols)):
r = abs(pearson.iloc[i, j])
if r > threshold:
high_corr_pairs.append({
"var1": cols[i],
"var2": cols[j],
"pearson": round(float(pearson.iloc[i, j]), 4),
"spearman": round(float(spearman.iloc[i, j]), 4),
"risk": "multicollinearity",
})
Path(fig_dir).mkdir(parents=True, exist_ok=True)
fig, ax = plt.subplots(figsize=(max(8, len(cols) * 0.5), max(6, len(cols) * 0.4)))
sns.heatmap(pearson, annot=len(cols) <= 15, fmt=".2f", cmap="RdBu_r", center=0, ax=ax)
ax.set_title("Pearson Correlation Matrix")
fig.tight_layout()
fig.savefig(f"{fig_dir}/correlation_heatmap.png", dpi=150)
plt.close(fig)
return {
"num_numeric_cols": len(cols),
"high_corr_pairs": high_corr_pairs,
"skewness": {c: round(float(numeric_df[c].skew()), 4) for c in cols},
"kurtosis": {c: round(float(numeric_df[c].kurtosis()), 4) for c in cols},
}
```
### Phase 5: 数据清洗执行
Input: Phase 2-4 的分析结果、用户确认的处理决策。
Output: 清洗后数据文件保存到 `data/cleaned/`。
1. 按 Phase 2 的决策表处理缺失值:
- 删除缺失率 > 50% 且无保留理由的列
- 对数值列按推荐策略填充
- 对分类列按推荐策略填充
2. 按 Phase 3 的决策表处理异常值:
- 修正明显录入错误
- Winsorize 需要处理的极端值
- 保留并标注的异常值不做修改
3. 按 Phase 4 的建议做必要的变量变换:
- 高偏度列做对数或 Box-Cox 变换
- 标准化/归一化(根据后续模型需求)
4. 保存清洗前后的数据维度对比。
5. 所有清洗操作必须记录到 `artifacts/cleaning_log.json`,包括:操作、影响行数、影响列、原因。
6. 清洗后数据保存为 CSV 格式到 `data/cleaned/`。
7. 如果有多个数据文件,可在此阶段做必要的合并(merge/join)。
8. 数据清洗脚本保存为 `scripts/data_cleaning.py`,确保可复现。
### Phase 6: 使用 Codex MCP 交叉验证预处理策略
Input: 数据概况、缺失值报告、异常值报告、清洗决策。
Output: 修订建议。
1. 使用 `gpt-5.4` 与 `high` 推理强度做独立审查。
2. 提示词必须包含数据概况表和清洗决策表。
3. 要求外部模型检查:
- 缺失值处理策略是否合理
- 异常值判断是否过于激进或保守
- 是否遗漏了重要的数据质量问题
- 变量变换是否会引入偏差
4. 保存 `threadId`,后续可追问具体列的处理建议。
```yaml
mcp__codex__codex:
model: gpt-5.4
config: {"model_reasoning_effort": "high"}
prompt: |
你是数学建模竞赛数据分析专家,请审查以下数据预处理方案。
数据概况:
[粘贴数据 profile 摘要]
缺失值分析:
[粘贴缺失值报告摘要]
异常值分析:
[粘贴异常值报告摘要]
清洗决策:
[粘贴清洗决策表]
请逐项回答:
1. 缺失值处理策略是否合理?有没有更好的插补方法?
2. 异常值的判定标准是否合适?是否有误杀或遗漏?
3. 当前的数据清洗是否可能引入偏差或信息损失?
4. 有没有遗漏的数据质量问题(如重复行、单位不一致、时间格式混乱)?
5. 对后续建模有什么数据层面的建议?
```
### Phase 7: Output
将最终结果写入 `DATA_PREPROCESSING_REPORT.md`。
1. 报告开头写 3-5 句总体摘要,交代数据规模、主要质量问题、清洗操作要点。
2. 主表必须覆盖:列名、数据类型、缺失率、异常率、分布特征、处理策略。
3. 追加"缺失值处理决策表"。
4. 追加"异常值处理决策表"。
5. 追加"高相关变量对表"。
6. 追加"变量变换记录表"。
7. 追加 "Open Questions" 区块,暴露仍不确定的数据问题。
8. 说明清洗后数据的存储位置和格式。
Output files:
| 路径 | 必需 | 内容 | 说明 |
|------|------|------|------|
| `DATA_PREPROCESSING_REPORT.md` | Yes | 主报告 | 提供给 `model-creator` 与 `solve-plan` |
| `data/cleaned/*.csv` | Yes | 清洗后数据 | 供后续建模使用 |
| `figures/eda/missing_heatmap.png` | Recommended | 缺失值热力图 | 可直接用于论文 |
| `figures/eda/boxplots.png` | Recommended | 箱线图矩阵 | 异常值可视化 |
| `figures/eda/correlation_heatmap.png` | Recommended | 相关性热力图 | 可直接用于论文 |
| `figures/eda/distributions/` | Recommended | 各列分布图 | 支持变量变换决策 |
| `artifacts/data_profile_raw.json` | Recommended | 原始数据概况 | 机器可读元信息 |
| `artifacts/missing_value_report.json` | Recommended | 缺失值报告 | 支持复查 |
| `artifacts/outlier_report.json` | Recommended | 异常值报告 | 支持复查 |
| `artifacts/correlation_report.json` | Recommended | 相关性报告 | 支持变量筛选 |
| `artifacts/cleaning_log.json` | Recommended | 清洗日志 | 确保可追溯 |
| `scripts/data_cleaning.py` | Recommended | 清洗脚本 | 确保可复现 |
## Key Rules
- **大文件处理**: If the Write tool fails due to file size, immediately retry using Bash (`cat << 'EOF' > file`) to write in chunks. Do NOT ask the user for permission — just do it silently.
- 始终先探索再清洗,不要跳过 EDA 直接做数据变换。
- 缺失值处理必须区分类型(MCAR/MAR/MNAR),不能无脑填均值。
- 异常值不要自动删除,必须写进决策表供确认。
Voir sur GitHub