3. 数据清洗与预处理:处理缺失值、去除重复项、统一单位与货币、处理异常价格数据

采购支出分析中,原始数据通常来自ERP、SRM、财务系统等多个异构源,脏数据比例可达15%-30%。数据清洗与预处理是确保节约跟踪准确性的基石——"垃圾进,垃圾出"在采购分析中尤为致命。本节将系统讲解四大核心清洗任务:缺失值处理、重复项去除、单位与货币统一、异常价格识别与修正。

3.1 缺失值处理策略

缺失值在采购数据中常见于物料描述、供应商名称、单价、交货日期等字段。处理策略需根据缺失率与字段重要性选择:

缺失率 字段类型 推荐策略 适用场景
< 5% 数值型(单价、数量) 中位数填充 价格波动较小的大宗物料
5% - 20% 分类型(供应商、物料组) 众数填充或模型预测 供应商集中度高的品类
20% - 50% 关键字段(PO编号、物料号) 关联回填(跨系统匹配) ERP与SRM系统间数据桥接
> 50% 非关键描述字段 标记为"未知"或删除字段 不影响核心分析的辅助信息
实战建议:对于单价缺失,优先使用同一物料最近3笔采购订单的加权平均价填充,而非全局中位数。这能保留价格趋势信息,避免节约计算偏差。

3.2 重复项识别与去重

重复数据通常由系统间重复导入、人工录入错误或同一采购单多次记账引起。去重需区分"完全重复"与"业务重复":

  • 完全重复:所有字段完全一致,直接删除保留一条。
  • 业务重复:PO编号相同但行号不同(正常拆分);或PO编号不同但供应商、物料、数量、单价完全一致(疑似重复下单)。
  • 时间窗口重复:同一供应商+同一物料+同一单价,在7天内出现多次,需与业务确认是否为分批交货。
去重规则示例(Python伪代码):
# 基于关键字段的精确去重
df_dedup = df.drop_duplicates(subset=['PO编号', '行号', '物料号', '供应商代码'])

# 基于相似度的模糊去重(适用于描述字段)
from fuzzywuzzy import fuzz
df['物料描述_标准化'] = df['物料描述'].apply(lambda x: standardize_desc(x))
df_dedup_fuzzy = df.groupby('物料号').apply(
    lambda g: g[~g['物料描述_标准化'].duplicated()]
)

3.3 单位与货币统一

跨国采购数据中,单位(千克/磅、个/打、米/码)与货币(USD/EUR/CNY)的不统一是节约跟踪的最大陷阱。统一流程分为三步:

  1. 单位标准化:建立单位转换映射表,将所有采购数量转换为标准计量单位(如统一为千克、个、米)。
  2. 货币统一:按采购订单日期的即期汇率或月度平均汇率,将所有金额转换为分析基准货币(通常为集团报告货币)。
  3. 价格归一化:计算"标准单位价格" = 转换后金额 / 转换后数量,用于跨供应商、跨地区的价格对比。
原始数据 单位转换 货币转换 归一化结果
500 lb, $1,250 USD 500 lb → 226.8 kg $1,250 USD → ¥8,750 CNY ¥38.58 / kg
200 kg, €1,000 EUR 200 kg → 200 kg €1,000 EUR → ¥7,800 CNY ¥39.00 / kg
常见错误:直接使用采购订单上的"单价"字段进行对比,忽略单位差异。例如"个"与"打"的单价相差12倍,若不转换将导致节约计算完全失真。

3.4 异常价格数据识别与处理

异常价格是节约跟踪中最需要关注的数据点——它们可能代表真正的节约机会,也可能是数据错误。识别方法包括统计规则与业务规则:

  • 统计规则:基于IQR(四分位距)或Z-Score识别离群值。通常将超出Q1-1.5×IQR或Q3+1.5×IQR的价格标记为异常。
  • 业务规则:价格低于历史最低价的70%或高于历史最高价的130%;或同一物料在不同供应商间价格差异超过50%。
  • 上下文规则:紧急采购、小批量采购、试制采购的价格通常偏高,需单独标记而非直接剔除。
异常价格处理流程:
# 步骤1:按物料分组计算统计边界
def flag_price_anomaly(group):
    Q1 = group['单价'].quantile(0.25)
    Q3 = group['单价'].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    group['价格异常标记'] = (group['单价'] < lower) | (group['单价'] > upper)
    return group

df = df.groupby('物料号').apply(flag_price_anomaly)

# 步骤2:业务规则二次校验
df['业务异常标记'] = (
    (df['采购类型'] == '紧急') & (df['单价'] > df['物料均价'] * 1.5)
) | (
    (df['采购数量'] < df['最小起订量']) & (df['单价'] > df['物料均价'] * 1.3)
)
数据清洗与预处理全流程 原始采购数据 缺失值处理 中位数/众数/关联回填 重复项去除 精确去重/模糊去重 单位与货币统一 转换映射/汇率归一 异常价格处理 IQR/业务规则标记 数据质量检查 质量合格? (缺失率<5% 且 异常率<3%) 返回重新清洗 清洗后可用数据集

3.5 清洗后的数据验证清单

完成清洗后,建议使用以下清单进行最终验证,确保数据可用于节约跟踪分析:

检查项 验收标准 验证方法
缺失率 关键字段缺失率 < 3% 逐字段统计缺失比例
重复率 完全重复行 = 0 基于主键去重后对比行数
单位一致性 同一物料组单位统一 按物料组分组检查单位字段
货币一致性 所有金额字段为同一基准货币 检查货币字段唯一值
异常价格占比 标记异常 < 5% 统计异常标记字段比例
效率提示:将上述清洗流程封装为可复用的Python函数或SQL存储过程,每次新数据接入时自动执行。建议在清洗前后分别保存数据快照,便于追溯和审计。