数据采集与预处理:供应链数据源与清洗实战

大家好,我是老张。今天咱们聊聊供应链数据采集和预处理这块硬骨头。

说实话,我做了这么多年量化交易和供应链分析,发现一个残酷的事实:80%的模型效果,其实取决于数据质量,而不是算法多花哨。你想想看,数据都是脏的、乱的,再牛的模型也是白搭。

这一章,我就把我在多个项目中踩过的坑、总结的经验,掰开了揉碎了讲给你听。

一、供应链三大数据源:ERP、WMS、TMS

做供应链异常检测,首先得知道数据从哪来。我个人习惯把数据源分成三大类:

数据源 核心数据 常见问题
ERP(企业资源计划) 采购订单、销售订单、库存台账、财务数据 数据口径不一致、历史数据缺失
WMS(仓储管理系统) 入库单、出库单、库位信息、盘点记录 时间戳错乱、数量单位不统一
TMS(运输管理系统) 运单、轨迹、签收记录、运费明细 GPS漂移、状态更新延迟

我在项目中遇到过最头疼的事:ERP里的库存数据和WMS里的实物库存对不上。明明系统显示有100件,仓库里只找到80件。这种差异,就是异常信号的源头。

核心观点:不要迷信任何一个单一数据源。交叉验证,才是王道。

二、数据清洗:把脏数据洗干净

数据清洗,说白了就是给数据"洗澡"。我见过太多人,拿到数据直接跑模型,结果一塌糊涂。

常见的脏数据有这几类:

  • 重复数据:同一个订单录了两次
  • 格式不一致:日期有的是"2024-01-01",有的是"2024/01/01"
  • 逻辑错误:出库时间早于入库时间
  • 单位混乱:有的用"箱",有的用"个"

我一般会写一个清洗流水线,像工厂流水线一样,一步步处理:

# 这是我常用的清洗流程
def clean_supply_chain_data(df):
    # 1. 去重
    df = df.drop_duplicates(subset=['order_id'])
    
    # 2. 统一日期格式
    df['date'] = pd.to_datetime(df['date'], errors='coerce')
    
    # 3. 过滤逻辑错误
    df = df[df['out_time'] >= df['in_time']]
    
    # 4. 单位转换(统一为"个")
    unit_map = {'箱': 12, '打': 12, '件': 1}
    df['quantity'] = df.apply(lambda x: x['quantity'] * unit_map.get(x['unit'], 1), axis=1)
    
    return df

小技巧:清洗的时候,别急着删数据。先把异常数据单独存一份,回头分析原因用。我曾经靠这个习惯,发现了一个供应商的作弊行为。

三、缺失值处理:别让"空"坑了你

缺失值,是供应链数据里的常客。为什么会缺失?原因很多:

  • 系统对接时丢包了
  • 人工录入时漏填了
  • 某些字段本来就不是必填项

处理缺失值,我一般分三步走:

  1. 先看缺失比例:如果超过50%,这个字段基本废了,直接扔掉
  2. 再看缺失模式:是随机缺失,还是跟某个条件有关?
  3. 最后选填充方法:均值、中位数、前向填充,还是用模型预测?

举个例子,运输时间这个字段经常缺失。我试过用均值填充,结果模型效果很差。后来发现,运输时间跟距离强相关。于是我用距离做回归预测,效果好了很多。

# 用距离预测运输时间
from sklearn.linear_model import LinearRegression

# 用有完整数据的样本训练模型
train_data = df[df['transit_time'].notna()]
model = LinearRegression()
model.fit(train_data[['distance']], train_data['transit_time'])

# 预测缺失值
missing_idx = df['transit_time'].isna()
df.loc[missing_idx, 'transit_time'] = model.predict(df.loc[missing_idx, ['distance']])

注意:千万别用全局均值填充时间序列数据。我见过有人这么干,结果把季节性波动全抹平了,模型完全失效。

四、异常值检测:找到那些"不对劲"的数据

异常值检测,是供应链异常信号挖掘的核心。说白了,就是找出那些"不对劲"的数据点。

我常用的方法有三种:

方法 适用场景 优点 缺点
3σ原则 正态分布的数据 简单快速 对非正态分布效果差
箱线图(IQR) 任意分布 鲁棒性强 对极端值不敏感
孤立森林 高维数据 处理复杂模式 计算量大

我个人最喜欢用箱线图。为什么?因为它不受极端值影响,而且直观。你想想看,一个订单的金额突然比平时高了10倍,用箱线图一眼就能看出来。

# 箱线图法检测异常
def detect_outliers_iqr(data, column):
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
    return outliers

避坑指南:我曾经在检测库存异常时,直接用3σ原则,结果把正常的大促备货当成了异常。后来改用箱线图,并加入时间窗口,才准确识别出真正的异常。

五、知识体系总览

下面这张图,是我梳理的本章知识体系。你可以把它当成一个"地图",随时回来对照。

供应链数据采集与预处理知识体系 数据源 ERP(订单/库存) WMS(仓储/出入库) TMS(运输/轨迹) 数据清洗 去重 格式统一 逻辑校验 缺失值处理 比例评估 模式分析 填充策略 异常值检测 3σ原则 正态分布 箱线图 任意分布 孤立森林 高维数据 输出:干净、可用的数据集

嗯,这张图把整个流程串起来了。从数据源开始,经过清洗、缺失值处理,再到异常检测,最后输出干净的数据集。每一步都环环相扣。

我的建议:在实际项目中,别想着一步到位。先跑通一个最小流程,再逐步优化。我刚开始做的时候,光清洗就花了两周,后来发现有些步骤可以并行处理,效率提升了不少。

好了,这一章的内容就到这里。数据采集和预处理,看似基础,但真的决定了你后续所有工作的成败。记住一句话:垃圾进,垃圾出。把数据搞干净了,后面的模型才能发挥真正的威力。

专注资料整理