数据采集与预处理:供应链数据源与清洗实战
大家好,我是老张。今天咱们聊聊供应链数据采集和预处理这块硬骨头。
说实话,我做了这么多年量化交易和供应链分析,发现一个残酷的事实: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
小技巧:清洗的时候,别急着删数据。先把异常数据单独存一份,回头分析原因用。我曾经靠这个习惯,发现了一个供应商的作弊行为。
三、缺失值处理:别让"空"坑了你
缺失值,是供应链数据里的常客。为什么会缺失?原因很多:
- 系统对接时丢包了
- 人工录入时漏填了
- 某些字段本来就不是必填项
处理缺失值,我一般分三步走:
- 先看缺失比例:如果超过50%,这个字段基本废了,直接扔掉
- 再看缺失模式:是随机缺失,还是跟某个条件有关?
- 最后选填充方法:均值、中位数、前向填充,还是用模型预测?
举个例子,运输时间这个字段经常缺失。我试过用均值填充,结果模型效果很差。后来发现,运输时间跟距离强相关。于是我用距离做回归预测,效果好了很多。
# 用距离预测运输时间
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σ原则,结果把正常的大促备货当成了异常。后来改用箱线图,并加入时间窗口,才准确识别出真正的异常。
五、知识体系总览
下面这张图,是我梳理的本章知识体系。你可以把它当成一个"地图",随时回来对照。
嗯,这张图把整个流程串起来了。从数据源开始,经过清洗、缺失值处理,再到异常检测,最后输出干净的数据集。每一步都环环相扣。
我的建议:在实际项目中,别想着一步到位。先跑通一个最小流程,再逐步优化。我刚开始做的时候,光清洗就花了两周,后来发现有些步骤可以并行处理,效率提升了不少。
好了,这一章的内容就到这里。数据采集和预处理,看似基础,但真的决定了你后续所有工作的成败。记住一句话:垃圾进,垃圾出。把数据搞干净了,后面的模型才能发挥真正的威力。