| 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。
- 如果
$ARGUMENTS 是文件路径,直接读取。
- 如果
$ARGUMENTS 是目录路径,扫描目录下所有 .csv、.xlsx、.xls、.json、.txt 文件。
- 如果无参数,按优先级搜索:
data/、dataset/、附件/、项目根目录。
- 对每个文件尝试自动检测编码(UTF-8、GBK、GB2312)和分隔符。
- 记录文件名、行数、列数、文件大小、编码格式。
- 如果数据分散在多个文件中,尝试识别它们之间的关联字段。
- 将加载结果保存到
artifacts/data_profile_raw.json。
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}")
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 与缺失值热力图。
- 对每列统计缺失率,按缺失率降序排列。
- 缺失率 >
MISSING_THRESHOLD_WARN (5%) 的列标记为"需要关注"。
- 缺失率 >
MISSING_THRESHOLD_DROP (50%) 的列标记为"建议删除"。
- 分析缺失模式:完全随机缺失 (MCAR)、随机缺失 (MAR)、非随机缺失 (MNAR)。
- 检查行级缺失:是否存在整行大面积缺失的情况。
- 生成缺失值热力图,保存到
figures/eda/missing_heatmap.png。
- 为每种缺失列推荐处理策略:
- 数值型:均值/中位数/KNN/多重插补
- 分类型:众数/单独类别/"Unknown"
- 时序型:前向/后向填充/线性插值
- 如果某列缺失率为 0,也要记录(证明数据完整性)。
- 在报告中单列"缺失值处理决策表"。
- 如果存在明显的非随机缺失模式,必须在报告中警告可能影响建模结果。
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 与箱线图。
- 对每个数值列同时使用 Z-score 和 IQR 方法检测异常值。
- 两种方法都标记为异常的数据点定义为"强异常"。
- 只有一种方法标记的定义为"弱异常"。
- 统计每列的强异常和弱异常数量与占比。
- 对异常值进行分类:
- 录入错误(值域不合理,如负年龄、超范围百分比)
- 极端但合理(如收入分布的极右尾部)
- 结构性异常(特定分组中的系统性偏移)
- 生成箱线图矩阵,保存到
figures/eda/boxplots.png。
- 为每种异常值推荐处理策略:
- 录入错误:修正或删除
- 极端合理值:Winsorize 或保留并标注
- 结构性异常:分组处理或单独建模
- 不要自动删除异常值。所有删除操作必须写进决策表供用户确认。
- 如果某列无异常,也要记录(证明数据质量良好)。
- 异常值对数模竞赛的影响通常比对 ML 竞赛大,因为评委关注模型假设合理性。
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、分布图、相关性热力图。
- 对每个数值列绘制直方图+KDE,观察分布形态。
- 计算偏度和峰度,判断是否需要变换(对数、Box-Cox)。
- 对分类列绘制频率条形图,标注类别数量和各类别占比。
- 计算数值列之间的 Pearson 相关系数矩阵。
- 对非线性关系补充 Spearman 秩相关系数。
- 相关系数绝对值 >
CORRELATION_THRESHOLD (0.85) 的变量对标注"多重共线性风险"。
- 生成相关性热力图,保存到
figures/eda/correlation_heatmap.png。
- 如果列数过多 (>
MAX_COLUMNS_FULL_PROFILE),先筛选高方差和高相关列。
- 特别关注自变量与因变量之间的相关性,如果因变量已知的话。
- 记录变量间的非线性关系提示,供后续建模方法选择参考。
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/。
- 按 Phase 2 的决策表处理缺失值:
- 删除缺失率 > 50% 且无保留理由的列
- 对数值列按推荐策略填充
- 对分类列按推荐策略填充
- 按 Phase 3 的决策表处理异常值:
- 修正明显录入错误
- Winsorize 需要处理的极端值
- 保留并标注的异常值不做修改
- 按 Phase 4 的建议做必要的变量变换:
- 高偏度列做对数或 Box-Cox 变换
- 标准化/归一化(根据后续模型需求)
- 保存清洗前后的数据维度对比。
- 所有清洗操作必须记录到
artifacts/cleaning_log.json,包括:操作、影响行数、影响列、原因。
- 清洗后数据保存为 CSV 格式到
data/cleaned/。
- 如果有多个数据文件,可在此阶段做必要的合并(merge/join)。
- 数据清洗脚本保存为
scripts/data_cleaning.py,确保可复现。
Phase 6: 使用 Codex MCP 交叉验证预处理策略
Input: 数据概况、缺失值报告、异常值报告、清洗决策。
Output: 修订建议。
- 使用
gpt-5.4 与 high 推理强度做独立审查。
- 提示词必须包含数据概况表和清洗决策表。
- 要求外部模型检查:
- 缺失值处理策略是否合理
- 异常值判断是否过于激进或保守
- 是否遗漏了重要的数据质量问题
- 变量变换是否会引入偏差
- 保存
threadId,后续可追问具体列的处理建议。
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。
- 报告开头写 3-5 句总体摘要,交代数据规模、主要质量问题、清洗操作要点。
- 主表必须覆盖:列名、数据类型、缺失率、异常率、分布特征、处理策略。
- 追加"缺失值处理决策表"。
- 追加"异常值处理决策表"。
- 追加"高相关变量对表"。
- 追加"变量变换记录表"。
- 追加 "Open Questions" 区块,暴露仍不确定的数据问题。
- 说明清洗后数据的存储位置和格式。
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),不能无脑填均值。
- 异常值不要自动删除,必须写进决策表供确认。
- 所有清洗操作必须记录到日志,确保可追溯和可复现。
- 相关性分析同时使用 Pearson 和 Spearman,避免遗漏非线性关系。
- 如果列数超过 50 或行数超过 10 万,必须先采样分析。
- 清洗后数据必须保存为独立文件,不要覆盖原始数据。
- 数据编码问题(GBK vs UTF-8)在中国赛题中非常常见,必须自动检测。
- EDA 图表应尽量生成可直接用于论文的高质量图(dpi >= 150,中英文标签)。
- 报告中必须保留
Open Questions,为下游建模暴露数据层面的不确定性。
Composing with Other Skills
problem-analysis
-> problem-decomposer
-> data-preprocessing
-> model-creator
-> solve-plan
data-preprocessing
-> model-creator (提供清洗后数据和变量建议)
-> paper-figure (提供 EDA 图表)
-> paper-write (提供数据描述章节素材)