Skip to content

Pandas Data Cleaning:实用工作流(2025)

Updated on

脏数据在早期就阻塞分析。随手 fillna 或字符串替换常常掩盖真正的问题:错误类型、静默重复或把指标拉偏的外值。

PAS 视角:

  • 问题: 类型混杂、缺失和重复会破坏 join 并扭曲统计。
  • 激化: 临时补丁把问题拖到后期,导致返工。
  • 解决: 可复用的清洗清单:统一类型、有意处理缺失、应对外值、去重,并在导出前验证。

工作流速览

步骤任务示例
1. 画像看列/缺失/唯一值df.info(), df.describe(include="all")
2. 规范列清理列名/文本df.columns = df.columns.str.strip().str.lower()
3. 校正类型to_datetimeto_numericastype("category")df["date"] = pd.to_datetime(df["date"], errors="coerce")
4. 处理缺失按规则删/补df["age"] = df["age"].fillna(df["age"].median())
5. 处理外值裁剪或打标df["rev_cap"] = df["revenue"].clip(upper=df["revenue"].quantile(0.99))
6. 去重清理全/半重复df.drop_duplicates(subset=["id", "date"], keep="last")
7. 验证断言范围/类别assert df["score"].between(0,100).all()

1) 快速画像

summary = {
    "rows": len(df),
    "columns": df.shape[1],
    "nulls": df.isna().sum(),
    "unique_counts": df.nunique(),
}
  • 早发现混合类型(df.info())。
  • df.describe(percentiles=[0.01,0.99]) 查看分布尾部。

2) 规范列与文本

df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
df["country"] = df["country"].str.strip().str.title()
  • 统一的名字让 join 和代码更简单。
  • 有奇怪字符时可用 .str.normalize("NFKC")

3) 修正数据类型

df["date"] = pd.to_datetime(df["date"], errors="coerce", utc=True)
df["amount"] = pd.to_numeric(df["amount"], errors="coerce")
df["segment"] = df["segment"].astype("category")
  • errors="coerce" 让坏值显式为 NaT/NaN
  • convert_dtypes() 可批量得到可空、省内存的类型。

4) 处理缺失值

df["age"] = df["age"].fillna(df["age"].median())
df["city"] = df["city"].fillna("Unknown")
df = df.dropna(subset=["id"])  # 必填键
  • 分列决策:数值→中位/均值;分类→众数/占位符。
  • 保留信号:df["age_imputed"] = df["age"].isna()

5) 应对外值

upper = df["revenue"].quantile(0.99)
lower = df["revenue"].quantile(0.01)
df["revenue_capped"] = df["revenue"].clip(lower=lower, upper=upper)
  • 比率可用 Z 分数过滤:abs(z) < 3
  • 金额类优先裁剪而非删除,避免样本量改变。

6) 安全去重

df = df.drop_duplicates(subset=["id", "date"], keep="last")
  • 确认唯一键:assert df.duplicated(["id"]).sum() == 0
  • 需要模糊匹配时先归一化(如邮箱小写)。

7) 导出前验证

assert df["score"].between(0, 100).all()
valid_segments = {"basic", "pro", "enterprise"}
assert df["segment"].isin(valid_segments).all()
  • 测试中可用 pd.testing.assert_frame_equal 比较输出。
  • 在流水线中加入轻量行数/缺失检查以防回归。

端到端迷你流程

def clean(df):
    df = df.copy()
    df.columns = df.columns.str.strip().str.lower()
    df["date"] = pd.to_datetime(df["date"], errors="coerce")
    df["amount"] = pd.to_numeric(df["amount"], errors="coerce")
    df["amount"] = df["amount"].fillna(df["amount"].median())
    df["segment"] = df["segment"].fillna("unknown").str.lower()
    df = df.dropna(subset=["id"])
    df = df.drop_duplicates(subset=["id", "date"], keep="last")
    assert df["amount"].ge(0).all()
    return df
  • 先复制,避免修改原数据。
  • df.pipe(clean) 保持链式调用清晰。

相关指南


关键要点

  • 统一命名、强制类型、有意处理缺失。
  • 外值优先裁剪/打标,不要静默删除。
  • 导出前验证键和数值范围,确保质量。