{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "21516622",
   "metadata": {},
   "source": [
    "# 需求预测与库存优化\n",
    "\n",
    "对应案例: [需求预测](/docs/case-studies/operations/demand-forecasting-case/)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdd4d85c",
   "metadata": {},
   "source": [
    "## 1. 生成销售数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aeeb4d86",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np, pandas as pd, matplotlib.pyplot as plt\n",
    "from statsmodels.tsa.holtwinters import ExponentialSmoothing\n",
    "np.random.seed(42)\n",
    "n=730; dates=pd.date_range('2023-01-01',periods=n,freq='D')\n",
    "trend=np.linspace(100,150,n)\n",
    "dow=dates.dayofweek; weekly=np.where(dow>=5,30,0)\n",
    "yearly=20*np.sin(2*np.pi*(dates.dayofyear-300)/365)\n",
    "sales=np.maximum(trend+weekly+yearly+np.random.normal(0,15,n),10).round(0).astype(int)\n",
    "df=pd.DataFrame({'date':dates,'sales':sales}).set_index('date')\n",
    "print(df.head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6591b577",
   "metadata": {},
   "source": [
    "## 2. 时序分解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88f7cafa",
   "metadata": {},
   "outputs": [],
   "source": [
    "from statsmodels.tsa.seasonal import seasonal_decompose\n",
    "dec=seasonal_decompose(df['sales'],model='additive',period=7)\n",
    "fig,axes=plt.subplots(4,1,figsize=(14,10))\n",
    "dec.observed.plot(ax=axes[0],title='Sales')\n",
    "dec.trend.plot(ax=axes[1],title='Trend')\n",
    "dec.seasonal.iloc[:90].plot(ax=axes[2],title='Seasonal')\n",
    "dec.resid.plot(ax=axes[3],title='Residual')\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dabdfac8",
   "metadata": {},
   "source": [
    "## 3. 预测 & 安全库存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba633702",
   "metadata": {},
   "outputs": [],
   "source": [
    "train=df.iloc[:-30]; test=df.iloc[-30:]\n",
    "hw=ExponentialSmoothing(train['sales'],seasonal_periods=7,trend='add',seasonal='add').fit()\n",
    "fc=hw.forecast(30)\n",
    "from sklearn.metrics import mean_absolute_error\n",
    "print(f'Holt-Winters MAE: {mean_absolute_error(test.values,fc):.0f}')\n",
    "avg_d=train['sales'].mean(); std_d=train['sales'].std()\n",
    "lt=3; z=1.645; ss=z*std_d*np.sqrt(lt)\n",
    "rop=avg_d*lt+ss\n",
    "print(f'Safety Stock: {ss:.0f}, ROP: {rop:.0f} @ {lt}d lead time, 95% service')\n",
    "# Plot\n",
    "fig,ax=plt.subplots(figsize=(14,5))\n",
    "ax.plot(train.index[-90:],train['sales'].iloc[-90:],label='History',color='gray')\n",
    "ax.plot(test.index,test.values,label='Actual',color='black',lw=2)\n",
    "ax.plot(test.index,fc,label='Forecast',color='#2196F3',lw=2)\n",
    "ax.legend(); ax.set_title('Demand Forecast'); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9315f36e",
   "metadata": {},
   "source": [
    "---\n",
    "*更多分析见 [案例文档](/docs/case-studies/operations/demand-forecasting-case/)*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
