2. 间歇需求数据特征:零膨胀、需求稀疏性、需求波动模式、间歇性分类

间歇需求数据与连续需求数据在统计分布上存在本质差异。理解这些特征是构建因果特征与集成模型的前提。本章从四个核心维度展开:零膨胀、需求稀疏性、需求波动模式以及间歇性分类。

2.1 零膨胀(Zero-Inflation)

零膨胀是指数据中零值出现的频率远高于标准计数分布(如泊松分布)的预期。在供应链场景中,零值可能来自两种机制:

  • 结构零(Structural Zero):该时间段内该SKU根本无需求(如季节性停产、产品退市)。
  • 抽样零(Sampling Zero):理论上存在需求,但恰好在观测周期内未发生(如随机缺货、客户延迟下单)。

区分这两种零值对因果建模至关重要。若将结构零误判为抽样零,模型会错误地学习到“低需求”的因果路径,导致预测偏差。

关键指标: 零比例(Zero Ratio)= 零值周期数 / 总周期数。当零比例 > 0.5 时,需优先考虑零膨胀模型(如ZIP、ZINB)。

2.2 需求稀疏性(Demand Sparsity)

稀疏性描述的是非零需求事件之间的间隔长度。即使零比例相同,稀疏程度也可能差异巨大。例如,一个SKU每10周出现1次需求(间隔9周),与另一个SKU每10周出现2次但集中在同一周,其稀疏模式完全不同。

稀疏等级 平均需求间隔(ADI) 典型场景
低稀疏 ADI < 1.32 快消品、标准备件
中稀疏 1.32 ≤ ADI < 2.5 工业耗材、季节性配件
高稀疏 ADI ≥ 2.5 航空备件、大型设备维修件

稀疏性直接影响模型的时间步长选择与特征窗口设计。高稀疏数据中,滑动窗口均值会引入大量零值噪声,需改用“事件间隔”作为因果特征的时间轴。

2.3 需求波动模式(Demand Volatility Pattern)

间歇需求不仅“稀疏”,而且“波动剧烈”。波动模式通常用需求变异系数(CV²)和需求峰度(Kurtosis)来刻画。

  • CV²(变异系数平方):衡量非零需求量的离散程度。CV² > 1 表示高度波动,常见于促销驱动或项目型需求。
  • 峰度:衡量极端大值的出现频率。高峰度(> 3)意味着存在“突发大单”,这类事件往往由外部因果事件(如政策变化、竞品断供)触发。
工程建议: 在特征工程中,将 CV² 和峰度作为分组特征,用于后续模型集成时的基学习器分配。高波动组优先使用树模型,低波动组优先使用线性模型。

2.4 间歇性分类(Intermittent Classification)

为了统一建模策略,业界通常将间歇需求数据分为四类。以下分类基于 Syntetos-Boylan 近似分类法,结合 ADI 与 CV² 两个维度:

间歇需求四象限分类(Syntetos-Boylan 近似) ADI(平均需求间隔)→ CV²(变异系数平方)→ ADI=1.32 CV²=0.49 平稳间歇 ADI < 1.32 CV² < 0.49 例:标准备件、稳定耗材 波动间歇 ADI < 1.32 CV² ≥ 0.49 例:促销品、项目型物料 稀疏平稳 ADI ≥ 1.32 CV² < 0.49 例:长周期维护件 稀疏波动 ADI ≥ 1.32 CV² ≥ 0.49 例:航空备件、应急物资 分类依据:ADI 衡量稀疏性,CV² 衡量波动性。四象限对应不同的因果特征与模型策略。

基于上述分类,可制定差异化的特征工程与模型选择策略:

分类 因果特征重点 推荐基学习器
平稳间歇 历史均值、季节性虚拟变量 Croston / 轻量GBDT
波动间歇 促销日历、价格弹性、竞品事件 XGBoost / LightGBM
稀疏平稳 事件间隔、累计需求、外部宏观指标 TSB / 贝叶斯结构时间序列
稀疏波动 因果图结构、干预变量、滞后效应 因果森林 / 深度状态空间模型
常见陷阱: 不要对所有间歇需求SKU使用同一套特征模板。例如,将“促销标志”作为特征加入稀疏平稳类,会导致过拟合,因为该类SKU几乎不参与促销。分类先行,特征后建。

2.5 特征工程中的零膨胀处理

在因果特征建模中,零膨胀数据需要特殊的特征变换:

  • 零值标记特征:构造二元变量 is_zero,作为因果图中的干预节点。
  • 间隔编码:将时间轴转换为“自上次非零需求以来的周期数”,作为连续特征。
  • 累积需求窗口:使用“过去N次非零事件的总和”代替“过去N天的总和”,避免零值稀释信号。
# 示例:构造间隔编码与累积需求特征(Python伪代码)
import pandas as pd

def build_intermittent_features(df, sku_col='sku', date_col='date', demand_col='demand'):
    df = df.sort_values([sku_col, date_col])
    # 间隔编码
    df['last_nonzero'] = df.groupby(sku_col)[demand_col].transform(
        lambda x: x.eq(0).cumsum() - x.eq(0).cumsum().where(x.ne(0)).ffill().fillna(0)
    )
    # 累积需求(基于非零事件)
    df['cum_demand_nonzero'] = df.groupby(sku_col)[demand_col].transform(
        lambda x: x[x > 0].cumsum().reindex(x.index, method='ffill').fillna(0)
    )
    return df

通过上述特征变换,零膨胀数据中的“零”不再是噪声,而是携带因果信息的信号——它可能意味着库存充足、需求转移或外部事件缺失。