日常报表清洗:一段能反复用的 pandas 模板

2026-10-21 · Python数据清洗

每周都要处理一份导出表,每次都从头写清洗代码,写到最后发现漏了某一步, 下游数字就错了。后来我把固定动作抽成模板,只有真正变化的部分才现场写。

模板长这样

import pandas as pd

def clean(df, *, key, date_col, num_cols=()):
    # 1) 去重:保留最后一条(导出表通常后写的更新)
    df = df.drop_duplicates(subset=[key], keep='last')

    # 2) 日期归一:导出的日期列经常混着字符串和 Excel 序列号
    df[date_col] = pd.to_datetime(df[date_col], errors='coerce')

    # 3) 数值列:去掉千分位逗号、货币符号,再转 float
    for c in num_cols:
        s = df[c].astype(str).str.replace(r'[,\s¥$]', '', regex=True)
        df[c] = pd.to_numeric(s, errors='coerce')

    # 4) 空值策略:数值补 0,日期保留 NaT 以便下游识别异常
    for c in num_cols:
        df[c] = df[c].fillna(0)

    return df

几个容易踩的点

1. drop_duplicates 的 keep 参数会静默改数

默认 keep='first'。如果导出表是按修改时间追加的,默认值会留下旧数据, 而且不报错。这类问题只能靠对账发现,非常费时间。

2. errors='coerce' 是双刃剑

它让脏数据变成 NaN 而不抛异常,脚本能跑完。但代价是:格式错误的行会静默消失。 建议清洗完加一句统计:

bad = df[df[date_col].isna()]
if len(bad):
    print(f'[warn] {len(bad)} 行日期解析失败,前 5 行:')
    print(bad.head())

3. 千分位逗号是数值列的天敌

从 Excel 里粘出来的数字经常带 1,234.56。直接 astype(float) 会报错, 用 errors='coerce' 则会变成 NaN,然后被当成 0 —— 金额就这么悄悄归零了。

4. 别用 inplace

链式调用里 inplace=True 返回 None,容易写出一串看似正常实则无效的代码。 统一写成 df = df.xxx(),风格一致,调试也方便。

收尾固定检查三项

  1. 行数:清洗前后各打一次,差值要能解释
  2. 空值:关键列空值率不能超过阈值
  3. 合计:关键指标的和与原始表对比,误差应为零

这三项加起来不到十行代码,但能挡掉绝大部分「报表发出去了才发现不对」的事故。

← 返回首页