2024年第四届长三角:汽后配件需求预测问题 详细思路

发布于:2024-05-16 ⋅ 阅读:(61) ⋅ 点赞:(0)

背景

在汽后行业的供应链管理中,精准的需求预测是后续管理及决策的基础。各个汽后配件即为一个库存单位(SKU,Stock Keeping Unit)。如果可以准确预知未来对于各个配件的市场需求,就可以提前将库存放在靠近需求的仓库中,从而降低库存成本,同时保证订单的按时履约。

在需求预测问题中,一般通过历史一段时间的订单情况,结合各配件的属性,得到未来一段时间的预测值。汽后行业所面临的需求预测问题可以分为两类:

  1. 点预测:通过分析历史需求,得到未来一段时间的确定性预估。常用的评价指标为1-wmape和1-smape,定义如下:

1 − wmape = 1 − ∑ ∣ y i − y ^ i ∣ ∑ y i 1 - \text{wmape} = 1 - \frac{\sum |y_i - \hat{y}_i|}{\sum y_i} 1wmape=1yiyiy^i

1 − smape = 1 − 2 × ∑ ∣ y i − y ^ i ∣ ∑ ( y i + y ^ i ) 1 - \text{smape} = 1 - 2 \times \frac{\sum |y_i - \hat{y}_i|}{\sum (y_i + \hat{y}_i)} 1smape=12×(yi+y^i)yiy^i

其中, y i y_i yi为样本点的实际值, h a t y i hat{y}_i hatyi为样本点的预测值,N为预测样本点的数目。

  1. 区间预测:通过分析历史需求的分布情况,得到未来一段时间的区间预测,对未来的不确定性(分位数)进行预估。例如给出90%的分位数预测,表示我们认为未来需求量不超过该预测值的概率为90%。

以上两类预测问题均有着广泛的应用背景。然而,汽后领域会面临历史需求数据的诸多挑战,其中一个比较普遍的问题是数据的间断性,即在时间序列中存在大量的0值,造成整体需求序列的不稳定性。对于此类需求序列的划分及处理策略,往往决定着汽后配件需求预测的准确性。

问题

附件为一份某汽后商家的“历史配件订单表”,包含了2022年1月1日至2023年7月31日wh1仓库的订单,订单中已指明配件(SKU)编码及需求量,可用于预测后续各配件在本仓库的需求量。其中同一天、同一仓库、同一配件,可能一天会存在多个订单,则当天该仓库对该配件的需求量可进行加和处理,若不存在订单,则当天需求量为0。

根据以上信息,请你们建立数学模型完成以下问题:

  1. 问题 1:使用“历史配件订单表”中的数据,预测出各商家在本仓库的配件2023年8月1日至2023年8月31日的需求量。请将预测结果以表格的形式列在正文中,并说明你们如何评价历史回测期间的准确率。

  2. 问题 2:使用“历史配件订单表”中的数据,对各商家在本仓库的配件2023年8月1日至2023年8月15日的需求量进行区间预测,分别给出各配件在本仓库10%、30%、70%、90%分位数的预测值。请将预测结果以表格的形式列在正文中。请说明解决此问题时,给出了哪些基本假设。同时讨论你们如何评价历史回测期间,各分位数预测的准确率。

  3. 问题 3:使用“历史配件订单表”中的数据,根据数据分析及建模过程,这些由配件日需求量形成的时间序列如何分类,研究每一类的特征,从而帮助你们进行更加精准的预测。

详细思路(供参考)

数据预处理

  1. 读取数据:

    • 读取“历史配件订单表”,处理编码问题确保数据正确读取。
  2. 数据清洗:

    • 合并同一天、同一仓库、同一配件的订单,计算每天的需求总量。
    • 处理缺失值,将没有订单的日期需求量记为0。
  3. 生成完整日期数据:

    • 生成2022年1月1日至2023年7月31日的所有日期。
    • 确保每个配件在每个日期都有记录,将缺失日期的需求量记为0。
import pandas as pd

# 读取数据
data = pd.read_csv('历史配件订单表.csv', encoding='gbk')

# 重命名列名
data.columns = ['仓库编码', '配件编码', '日期', '需求量']

# 转换日期格式
data['日期'] = pd.to_datetime(data['日期'], format='%Y/%m/%d')

# 汇总同一天、同一仓库、同一配件的需求量
data_agg = data.groupby(['仓库编码', '配件编码', '日期']).sum().reset_index()

# 生成所有日期
all_dates = pd.date_range(start='2022-01-01', end='2023-07-31')

# 生成仓库和配件的唯一组合
unique_pairs = data_agg[['仓库编码', '配件编码']].drop_duplicates()

# 创建所有日期和配件组合的笛卡尔积
full_index = pd.MultiIndex.from_product([unique_pairs['仓库编码'], unique_pairs['配件编码'], all_dates], names=['仓库编码', '配件编码', '日期'])

# 创建完整的数据框
full_data = pd.DataFrame(index=full_index).reset_index()

# 合并原始数据和完整的数据框
full_data = pd.merge(full_data, data_agg, on=['仓库编码', '配件编码', '日期'], how='left')

# 填补缺失值
full_data['需求量'] = full_data['需求量'].fillna(0)

# 显示数据前几行
full_data.head()

如下:
在这里插入图片描述

问题 1: 点预测

步骤和思路:

  1. 特征工程:

    • 提取时间序列特征,如月份、星期、节假日等。
    • 考虑滞后特征,如前几天的需求量。
  2. 模型选择:

    • 选择适合时间序列预测的模型,如ARIMA、SARIMA、Prophet等。
    • 结合机器学习模型,如随机森林、XGBoost等进行建模,利用交叉验证选择最佳模型。
  3. 模型训练和预测:

    • 使用2022年1月1日至2023年7月31日的数据进行训练。
    • 预测2023年8月1日至2023年8月31日的需求量。
  4. 模型评价:

    • 使用1-wmape和1-smape作为评价指标,计算历史回测期间的准确率。
from statsmodels.tsa.statespace.sarimax import SARIMAX
from sklearn.metrics import mean_absolute_error

# 定义评价指标
def wmape(y_true, y_pred):
    return 1 - (abs(y_true - y_pred).sum() / y_true.sum())

def smape(y_true, y_pred):
    return 1 - 2 * (abs(y_true - y_pred).sum() / (y_true + y_pred).sum())

# 训练SARIMA模型并预测
model = SARIMAX(full_data['需求量'], order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
result = model.fit()

# 预测
forecast = result.predict(start=len(full_data), end=len(full_data) + 30)

# 计算评价指标
y_true = full_data['需求量'][-31:]
y_pred = forecast[:31]
wmape_score = wmape(y_true, y_pred)
smape_score = smape(y_true, y_pred)

print(f"1-wmape: {wmape_score}, 1-smape: {smape_score}")

问题 2: 区间预测

步骤和思路:

  1. 特征工程和模型选择:

    • 同问题1的数据预处理和特征工程。
    • 选择适合区间预测的模型,如Quantile Regression Forests、Gradient Boosting等。
    • 使用分位数回归方法,分别预测10%、30%、70%、90%的分位数。
  2. 模型训练和预测:

    • 使用2022年1月1日至2023年7月31日的数据进行训练。
    • 预测2023年8月1日至2023年8月15日的需求量的分位数。
  3. 模型评价:

    • 对历史数据进行回测,计算各分位数的预测准确率。
    • 使用如Pinball Loss等分位数预测的评价指标。

基本假设:

  • 历史需求数据能够代表未来的需求分布。
  • 各个时间点的需求是独立同分布的。
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor

# 定义分位数回归模型
quantiles = [0.1, 0.3, 0.7, 0.9]
models = {q: GradientBoostingRegressor(loss='quantile', alpha=q) for q in quantiles}

# 训练模型
X_train = full_data.drop(columns=['需求量'])
y_train = full_data['需求量']
for q in quantiles:
    models[q].fit(X_train, y_train)

# 预测
X_test = ...  # 生成预测区间的特征数据
predictions = {q: models[q].predict(X_test) for q in quantiles}

# 计算分位数预测的Pinball Loss
def pinball_loss(y_true, y_pred, q):
    return np.mean([max(q * (yt - yp), (q - 1) * (yt - yp)) for yt, yp in zip(y_true, y_pred)])

y_test = ...  # 提供真实需求量数据
pinball_losses = {q: pinball_loss(y_test, predictions[q], q) for q in quantiles}

print(pinball_losses)

问题 3: 时间序列分类

步骤和思路:

  1. 数据预处理: 同问题1的数据预处理。

  2. 时间序列聚类:

    • 使用时间序列聚类方法,如K-means、DBSCAN等对SKU进行聚类。
    • 提取时间序列特征,如季节性、趋势性、波动性等,进行分类。
  3. 特征分析:

    • 对每一类的时间序列特征进行分析,总结各类的特征。
    • 根据各类特征选择适合的预测模型。
from tslearn.clustering import TimeSeriesKMeans
from tslearn.preprocessing import TimeSeriesScalerMeanVariance

# 数据标准化
scaler = TimeSeriesScalerMeanVariance()
data_scaled = scaler.fit_transform(full_data['需求量'].values.reshape(-1, 1))

# 时间序列聚类
kmeans = TimeSeriesKMeans(n_clusters=3, metric="dtw")
clusters = kmeans.fit_predict(data_scaled)

# 添加聚类结果到数据框
full_data['聚类'] = clusters

# 分析每一类的特征
for cluster in range(3):
    cluster_data = full_data[full_data['聚类'] == cluster]
    print(cluster_data.describe())

更多数学建模支持

  1. 查看http://share.mosha.cloud 网站使用gpt4来获取更多的数学建模支持和资源。
  2. 书籍推荐:《Python3编程从零基础到实战》《Python网络爬虫入门到实战》
  3. 社群:592697532

网站公告

今日签到

点亮在社区的每一天
去签到