3. 数据清洗基础:缺失值处理、重复值检测、异常值识别与处理策略

成本台账数据在采集与整合过程中,常因系统接口不稳定、人工录入偏差、历史数据迁移遗漏等原因,产生三类核心质量问题:缺失值、重复值与异常值。本章将围绕这三类问题,结合成本台账的典型字段(如物料编码、成本中心、金额、数量、日期),讲解检测方法与处理策略,并给出可落地的 Python 代码示例。

3.1 缺失值处理

3.1.1 缺失值来源与影响

来源场景 典型字段 对财务报表合并的影响
接口传输中断 成本中心编码、物料组 合并时无法匹配映射关系,导致分类错误
人工录入遗漏 金额、数量 成本汇总偏低,毛利计算失真
历史数据不完整 期间、凭证日期 时间序列分析断点,无法生成连续报表

3.1.2 处理策略

  • 删除法:当缺失比例 < 5% 且为随机缺失时,直接删除缺失行或列。适用于关键字段(如金额)缺失。
  • 填充法:
    • 固定值填充:如缺失成本中心时填入“未知成本中心”。
    • 均值/中位数填充:适用于数值型字段(如单价、数量)。
    • 前向/后向填充:适用于时间序列数据,如按日期顺序填充缺失的期间字段。
    • 模型预测填充:使用 KNN 或回归模型预测缺失值,适用于高精度要求场景。
  • 标记法:保留缺失值,但增加一列标记字段,记录该行是否存在缺失,供后续审计追溯。
实战建议:成本台账中“金额”字段缺失时,优先使用同一物料、同一成本中心的平均单价乘以数量进行推算,而非简单使用全局均值。

3.1.3 Python 实现示例

import pandas as pd
import numpy as np

# 加载成本台账数据
df = pd.read_excel('cost_ledger.xlsx')

# 查看缺失概况
print(df.isnull().sum())

# 策略1:删除金额缺失的行
df_clean = df.dropna(subset=['金额'])

# 策略2:用成本中心均值填充单价缺失
df_clean['单价'] = df_clean.groupby('成本中心')['单价'].transform(
    lambda x: x.fillna(x.mean())
)

# 策略3:前向填充期间字段
df_clean['期间'] = df_clean['期间'].fillna(method='ffill')

# 策略4:标记缺失
df_clean['金额缺失标记'] = df['金额'].isnull().astype(int)

3.2 重复值检测

3.2.1 重复类型与识别方法

重复类型 定义 检测方法
完全重复 所有字段值完全相同 df.duplicated()
关键字段重复 凭证号+物料+成本中心相同 df.duplicated(subset=['凭证号','物料编码','成本中心'])
近似重复 金额、数量微小差异(如±0.01) 结合 round() 与分组聚合

3.2.2 处理策略

  • 完全重复:直接保留第一条,删除其余。
  • 关键字段重复:需人工复核,确认是否为同一笔业务重复录入。若确认重复,保留金额合计或取最新记录。
  • 近似重复:设定容忍阈值(如金额差异 < 0.1 元),合并为一条记录,金额取均值。
注意:成本台账中“凭证号+行项目号”组合通常应唯一。若出现重复,可能导致财务报表合并时借贷不平,必须优先处理。

3.2.3 Python 实现示例

# 检测完全重复行
dup_full = df_clean[df_clean.duplicated(keep=False)]
print(f"完全重复行数: {len(dup_full)}")

# 删除完全重复,保留第一条
df_dedup = df_clean.drop_duplicates()

# 检测关键字段重复(凭证号+物料编码)
dup_key = df_dedup[df_dedup.duplicated(
    subset=['凭证号', '物料编码'], keep=False
)]
print(f"关键字段重复行数: {len(dup_key)}")

# 对关键字段重复行:金额取均值,数量求和
df_grouped = df_dedup.groupby(
    ['凭证号', '物料编码', '成本中心'], as_index=False
).agg({
    '金额': 'mean',
    '数量': 'sum',
    '日期': 'first'
})

3.3 异常值识别与处理

3.3.1 异常值类型与识别方法

异常类型 示例 识别方法
业务逻辑异常 数量为负数、金额超过 10 亿 业务规则校验(如金额 > 0)
统计分布异常 单价超出 3 倍标准差 Z-Score、IQR 四分位距
时间序列异常 某月成本突增 500% 环比/同比波动检测

3.3.2 处理策略

  • 业务规则修正:如数量为负,检查是否为红字冲销,若是则保留;否则更正为值。
  • 截尾处理:对超出 99% 分位数的金额,替换为 99% 分位值。
  • 标记隔离:将异常值单独存储至异常台账,供财务人员逐笔确认,不直接修改原始数据。
  • 模型替换:使用同物料、同成本中心的正常值中位数替换。
关键原则:异常值处理必须保留审计轨迹。建议在清洗后的数据集中增加“异常标记”列,记录处理方式与原始值,便于追溯。

3.3.3 Python 实现示例

# IQR 法识别单价异常
Q1 = df_grouped['单价'].quantile(0.25)
Q3 = df_grouped['单价'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

outliers = df_grouped[
    (df_grouped['单价'] < lower_bound) |
    (df_grouped['单价'] > upper_bound)
]
print(f"单价异常行数: {len(outliers)}")

# 截尾处理:将超出上下界的值替换为边界值
df_grouped['单价_清洗'] = df_grouped['单价'].clip(
    lower=lower_bound, upper=upper_bound
)

# 业务规则:金额必须大于0
df_final = df_grouped[df_grouped['金额'] > 0].copy()

3.4 数据清洗流程总览

以下 SVG 图展示了从原始成本台账到清洗后标准数据集的完整流程,包含缺失值、重复值、异常值的处理节点与判断逻辑。

成本台账数据清洗流程 成本台账数据清洗流程 原始成本台账 步骤1:缺失值检测与处理 删除 / 填充 / 标记 步骤2:重复值检测与去重 完全重复 / 关键字段重复 / 近似重复 步骤3:异常值识别与处理 业务规则 / IQR / 截尾 / 标记隔离 步骤4:审计轨迹记录 异常标记列 / 原始值保留 / 处理方式 清洗后标准数据集 输入 ERP / 手工台账 / 接口 输出 财务报表合并接口

3.5 清洗策略对比与选择

问题类型 推荐策略 适用场景 风险提示
缺失值(关键字段) 删除法 缺失比例 < 5% 可能丢失少量有效数据
缺失值(非关键字段) 填充法(均值/中位数) 数值型字段,分布较集中 可能引入偏差
完全重复 直接去重 所有字段相同 无风险
关键字段重复 人工复核 + 聚合 凭证号+物料相同 需业务确认
业务逻辑异常 规则修正 金额为负、数量为负 需确认是否为红字冲销
统计分布异常 截尾处理 单价、金额极端值 可能掩盖真实异常
最佳实践:建议将清洗逻辑封装为可复用的 Python 函数,并输出清洗报告(含缺失率、重复率、异常率),便于财务团队审核与接口联动配置。