日常报表清洗:一段能反复用的 pandas 模板
每周都要处理一份导出表,每次都从头写清洗代码,写到最后发现漏了某一步, 下游数字就错了。后来我把固定动作抽成模板,只有真正变化的部分才现场写。
模板长这样
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(),风格一致,调试也方便。
收尾固定检查三项
- 行数:清洗前后各打一次,差值要能解释
- 空值:关键列空值率不能超过阈值
- 合计:关键指标的和与原始表对比,误差应为零
这三项加起来不到十行代码,但能挡掉绝大部分「报表发出去了才发现不对」的事故。
← 返回首页