✨ 真实供应链数据中,缺失、重复、异常 是三大“脏数据”元凶。 本章用最直观的方法 + Python 代码,让数据变得干净可靠。
供应链数据常因传感器故障、人工漏录产生 NaN。策略:删除 或 填充。
| 方法 | 适用场景 | Pandas 实现 | 备注 |
|---|---|---|---|
| 删除行 | 缺失比例 < 5% | df.dropna() | 直接丢弃 |
| 删除列 | 缺失 > 70% | df.drop(columns=...) | 特征失效 |
| 均值/中位数填充 | 数值型,对称分布 | df.fillna(df.mean()) | 不影响整体均值 |
| 前向/后向填充 | 时间序列 (需求预测) | df.fillna(method='ffill') | 延续最近值 |
| KNN / 插值 | 非线性趋势 | df.interpolate() | 线性/多项式 |
import pandas as pd import numpy as np # 模拟供应链需求数据 (含缺失) df = pd.DataFrame({ '日期': pd.date_range('2025-01-01', periods=10, freq='D'), '销量': [120, np.nan, 135, 142, np.nan, 158, 163, np.nan, 177, 185], '库存': [800, 765, np.nan, 690, 672, np.nan, 590, 550, np.nan, 480] }) print('原始缺失情况:\n', df.isnull().sum()) # 策略1: 删除缺失比例 > 40% 的列 (此处库存无超阈值,仅演示) if df.isnull().mean()['库存'] > 0.4: df.drop(columns=['库存'], inplace=True) # 策略2: 销量用中位数填充 (抗异常) df['销量'] = df['销量'].fillna(df['销量'].median()) # 策略3: 库存用线性插值 (时间序列) df['库存'] = df['库存'].interpolate(method='linear') print('清洗后缺失:\n', df.isnull().sum())
重复录入、系统抽风导致重复行。供应链中重复订单会放大需求预测误差。
df.drop_duplicates()keep='first'/'last'# 构建含重复的供应链订单 df_dup = pd.DataFrame({ '订单号': ['ORD001', 'ORD002', 'ORD001', 'ORD003', 'ORD002'], 'SKU': ['A100', 'B200', 'A100', 'C300', 'B200'], '需求量': [50, 30, 50, 20, 30] }) print('重复行数:', df_dup.duplicated().sum()) # 基于所有列去重 df_clean = df_dup.drop_duplicates() print('完全去重后:\n', df_clean) # 基于订单号去重 (保留第一条) df_clean_key = df_dup.drop_duplicates(subset=['订单号'], keep='first') print('按订单号去重:\n', df_clean_key)
供应链中突发促销、录入错误会产生离群点。常用 IQR 或 Z-score 检测。
| 方法 | 原理 | 阈值 | 适用 |
|---|---|---|---|
| IQR 四分位距 | Q1 - 1.5×IQR / Q3 + 1.5×IQR | 超出即为异常 | 偏态分布 |
| Z-score | (x-μ)/σ | |Z| > 3 | 近似正态 |
| 业务规则 | 如销量 < 0 或 > 日均10倍 | 自定义 | 强业务知识 |
# 模拟销量 (含异常值 500 和 -30) sales = pd.Series([120, 135, 142, 158, 163, 177, 185, 500, -30, 149]) Q1 = sales.quantile(0.25) Q3 = sales.quantile(0.75) IQR = Q3 - Q1 lower = Q1 - 1.5 * IQR upper = Q3 + 1.5 * IQR print(f'下界: {lower:.1f}, 上界: {upper:.1f}') outliers = sales[(sales < lower) | (sales > upper)] print('异常值索引及数值:\n', outliers) # 处理: 用中位数替换异常 sales_clean = sales.copy() median_val = sales.median() sales_clean[(sales < lower) | (sales > upper)] = median_val print('清洗后销量:\n', sales_clean.tolist())
🎯 实战要点总结