學(xué)建模到零售決策:需求預(yù)測、庫存優(yōu)化與促銷分析實戰(zhàn))
1. 項目概述從一道賽題到一套完整的方法論“楊阿姨的困惑”這道題是2013年“認證杯”數(shù)學(xué)建模挑戰(zhàn)賽第一階段D題的賽題。十多年過去了當(dāng)我重新翻開這道題的全過程文檔和程序時依然覺得它是個絕佳的教學(xué)案例和實戰(zhàn)范本。這道題之所以經(jīng)典不在于它用了多么高深的算法而在于它完美地詮釋了數(shù)學(xué)建模的核心思想如何將一個看似模糊、充滿生活氣息的實際問題轉(zhuǎn)化為清晰、可量化、可計算的數(shù)學(xué)模型并最終給出有指導(dǎo)意義的結(jié)論。題目描述了一位“楊阿姨”在社區(qū)經(jīng)營一家小型便利店她面臨一系列經(jīng)營決策上的困惑如何根據(jù)歷史銷售數(shù)據(jù)預(yù)測未來需求如何優(yōu)化進貨策略以平衡庫存成本和缺貨風(fēng)險如何安排促銷活動以最大化利潤這些困惑本質(zhì)上就是供應(yīng)鏈管理、庫存優(yōu)化和銷售預(yù)測的經(jīng)典問題是任何實體零售經(jīng)營者都會遇到的痛點。數(shù)學(xué)建模的任務(wù)就是為楊阿姨提供一套基于數(shù)據(jù)的決策支持方案。網(wǎng)絡(luò)上關(guān)于這道題的討論和資源一直很熱從SPSSPRO工具的使用到各種預(yù)測、優(yōu)化算法的實現(xiàn)再到完整論文的撰寫形成了一個豐富的學(xué)習(xí)生態(tài)。這恰恰說明了它的價值它像一座橋梁連接了抽象的數(shù)學(xué)理論與鮮活的實際應(yīng)用。無論是正在備戰(zhàn)數(shù)模競賽的學(xué)生還是對數(shù)據(jù)驅(qū)動決策感興趣的從業(yè)者深入剖析這道題的解決全過程都能獲得遠超題目本身的收獲——一套結(jié)構(gòu)化的問題分析、模型構(gòu)建與求解驗證的思維框架。2. 問題拆解與模型構(gòu)建思路面對“楊阿姨的困惑”第一步也是最關(guān)鍵的一步就是問題拆解。你不能一頭扎進數(shù)據(jù)里而是要先理解業(yè)務(wù)邏輯。題目通常不會把問題直接寫成數(shù)學(xué)公式它給的是場景和描述。我們的工作就是翻譯。2.1 核心問題識別與量化楊阿姨的困惑可以歸納為三個層次需求預(yù)測問題明天、下周、下個月各種商品能賣出去多少這是所有決策的基礎(chǔ)。預(yù)測不準要么貨架空空損失銷售額要么倉庫爆滿壓占資金。庫存優(yōu)化問題基于預(yù)測的需求每次應(yīng)該進多少貨進貨太多有庫存持有成本資金占用、倉儲空間、商品過期風(fēng)險進貨太少又會產(chǎn)生缺貨成本銷售損失、顧客流失。需要在兩者間找到平衡點。促銷策略問題是否應(yīng)該對某些商品進行促銷促銷力度多大促銷能帶來多少額外的銷量同時又是否會侵蝕利潤這涉及到價格彈性分析和收益管理。對應(yīng)的我們需要將其轉(zhuǎn)化為可研究的科學(xué)問題需求預(yù)測利用歷史銷售時間序列數(shù)據(jù)建立預(yù)測模型估計未來一段時間內(nèi)各商品的需求量。這通常是一個時間序列分析問題。庫存優(yōu)化在需求不確定預(yù)測有誤差的前提下建立庫存模型以總成本采購成本庫存持有成本缺貨成本最小化為目標求解最優(yōu)的訂貨點和訂貨量。這通常是一個隨機規(guī)劃或新聞vendor模型問題。促銷評估分析歷史促銷數(shù)據(jù)量化價格變化對銷量的影響需求價格彈性并構(gòu)建利潤模型評估不同促銷方案下的凈利潤變化。這可以是一個回歸分析或優(yōu)化問題。2.2 模型選型與工具準備明確了問題接下來就是選擇“武器”。2013年的賽題選手們可能多用MATLAB、LINGO或基礎(chǔ)的SPSS。今天我們的工具庫更加豐富但思想一脈相承。需求預(yù)測模型選型傳統(tǒng)時間序列模型如移動平均MA、指數(shù)平滑Exponential Smoothing包括Holt-Winters模型處理趨勢和季節(jié)性。這些模型簡單、直觀對于具有一定規(guī)律性的銷售數(shù)據(jù)非常有效。SPSSPRO中的“時間序列預(yù)測”模塊能很方便地實現(xiàn)。ARIMA模型如果數(shù)據(jù)表現(xiàn)出非平穩(wěn)性如趨勢明顯ARIMA模型是更強大的工具。它通過差分使序列平穩(wěn)再結(jié)合自回歸和移動平均進行預(yù)測。這是時間序列預(yù)測的經(jīng)典方法。機器學(xué)習(xí)模型考慮到現(xiàn)今數(shù)據(jù)的豐富性也可以嘗試如隨機森林、梯度提升樹如XGBoost甚至簡單的神經(jīng)網(wǎng)絡(luò)如LSTM進行多變量預(yù)測。但對于“楊阿姨的便利店”數(shù)據(jù)量可能有限傳統(tǒng)時間序列模型往往是更穩(wěn)健、可解釋性更強的起點。注意模型不是越復(fù)雜越好。首先要對歷史銷售數(shù)據(jù)做探索性分析EDA畫圖觀察是否存在明顯的趨勢Trend、季節(jié)性Seasonality和周期性Cycle。如果數(shù)據(jù)平穩(wěn)且規(guī)律明顯指數(shù)平滑可能就足夠了。盲目上復(fù)雜模型容易過擬合。庫存優(yōu)化模型選型經(jīng)濟訂貨批量模型如果需求相對穩(wěn)定這是一個經(jīng)典起點。它平衡了訂貨成本每次下單的固定成本和庫存持有成本給出一個最優(yōu)的訂貨量。但EOQ假設(shè)需求是確定性的這是它的主要局限。報童模型這是處理單周期、隨機需求問題的基石。它適用于易腐品如面包、報刊或短銷售周期的商品。模型的核心是尋找一個臨界點最優(yōu)訂貨量使得最后一單位產(chǎn)品的期望邊際收益等于期望邊際損失。安全庫存模型對于多周期、需要持續(xù)補貨的商品在EOQ的基礎(chǔ)上引入安全庫存是關(guān)鍵。安全庫存的量取決于需求的不確定性預(yù)測誤差的標準差和服務(wù)水平目標如希望95%的情況下不缺貨。計算安全庫存需要用到需求預(yù)測的誤差分布信息。仿真模型當(dāng)問題結(jié)構(gòu)復(fù)雜如多種商品、有補貨提前期、有容量限制時解析模型可能難以求解。此時可以使用蒙特卡洛仿真模擬成百上千次可能的未來需求場景來評估和優(yōu)化不同的庫存策略。Python的SimPy庫或AnyLogic等專業(yè)工具可以實現(xiàn)。促銷分析模型選型線性/非線性回歸核心是建立銷量與價格、促銷活動0/1變量、節(jié)假日等因素之間的回歸方程。通過回歸系數(shù)可以估計價格彈性。例如銷量 β0 β1 * 價格 β2 * 促銷標志 ...。差分法比較促銷期間與非促銷期間的銷量差異同時盡量控制其他因素的影響如選擇相鄰的、屬性相似的周次進行比較來估計促銷的凈效果。優(yōu)化模型在估計出價格彈性的基礎(chǔ)上可以構(gòu)建一個利潤最大化模型Max Profit (價格 - 成本) * 預(yù)測銷量(價格)通過求導(dǎo)或搜索找到最優(yōu)定價。工具準備今天我們完全可以用更現(xiàn)代化的工具鏈來復(fù)現(xiàn)和超越當(dāng)年的解決方案。核心是Python生態(tài)pandas和numpy用于數(shù)據(jù)處理statsmodels和pmdarima用于傳統(tǒng)時間序列分析scikit-learn和xgboost用于機器學(xué)習(xí)預(yù)測scipy用于優(yōu)化求解??梢暬瘎t離不開matplotlib和seaborn。當(dāng)然SPSSPRO作為一個集成化的統(tǒng)計分析平臺其圖形化操作界面對于快速驗證思路、完成基礎(chǔ)分析依然有巨大優(yōu)勢特別適合建模初學(xué)者。3. 數(shù)據(jù)預(yù)處理與探索性分析實戰(zhàn)拿到楊阿姨的歷史銷售數(shù)據(jù)通常是CSV或Excel格式第一步不是急著跑模型而是“讀懂”數(shù)據(jù)。這個階段的工作直接決定了后續(xù)模型的質(zhì)量。3.1 數(shù)據(jù)清洗與規(guī)整假設(shè)我們有一份名為sales_data.csv的數(shù)據(jù)包含字段日期、商品ID、商品名稱、銷售量、銷售額、是否促銷。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 讀取數(shù)據(jù) df pd.read_csv(sales_data.csv, parse_dates[日期]) print(df.head()) print(df.info()) print(df.describe())常見問題及處理缺失值檢查銷售量、銷售額是否有NaN。如果缺失很少且隨機可以直接刪除該行df.dropna(subset[銷售量])。如果缺失有規(guī)律如特定節(jié)假日門店關(guān)門可以考慮用前向填充df[銷售量].fillna(methodffill)或均值/中位數(shù)填充但需謹慎最好結(jié)合業(yè)務(wù)理解。實操心得對于時間序列線性插值df[銷售量].interpolate(methodlinear)有時比用固定值填充更合理因為它能保持序列的連續(xù)性。異常值銷售量是否出現(xiàn)負數(shù)或極大值通過箱線圖或3σ原則識別異常值。重要不要輕易刪除異常值先分析原因。可能是數(shù)據(jù)錄入錯誤如多輸了一個0也可能是真實的業(yè)務(wù)事件如大型團購。如果是錯誤修正或刪除如果是真實事件在建模時需要特殊考慮例如在預(yù)測模型中將其視為外部沖擊或使用對異常值穩(wěn)健的模型。數(shù)據(jù)格式確保日期列已被正確解析為datetime格式商品ID為類別型是否促銷為布爾型或0/1整數(shù)。3.2 探索性數(shù)據(jù)分析這是發(fā)現(xiàn)規(guī)律、啟發(fā)模型選擇的黃金階段。# 1. 整體趨勢按天聚合總銷量 daily_sales df.groupby(日期)[銷售量].sum() plt.figure(figsize(14,6)) plt.plot(daily_sales.index, daily_sales.values) plt.title(每日總銷售量趨勢) plt.xlabel(日期) plt.ylabel(銷售量) plt.grid(True) plt.show() # 2. 季節(jié)性分析查看周內(nèi)效應(yīng)星期幾效應(yīng) df[星期] df[日期].dt.dayofweek # Monday0, Sunday6 weekly_pattern df.groupby(星期)[銷售量].mean() plt.figure(figsize(10,5)) plt.bar([Mon, Tue, Wed, Thu, Fri, Sat, Sun], weekly_pattern) plt.title(周內(nèi)日均銷售量模式) plt.xlabel(星期) plt.ylabel(平均銷售量) plt.show() # 3. 商品貢獻度分析帕累托分析 product_sales df.groupby(商品名稱)[銷售量].sum().sort_values(ascendingFalse) product_sales_cumsum product_sales.cumsum() / product_sales.sum() top_n (product_sales_cumsum 0.8).sum() # 找出貢獻80%銷量的商品數(shù)量 print(fTop {top_n} 種商品貢獻了80%的銷量。) # 后續(xù)建??梢灾攸c聚焦這些核心商品。 # 4. 促銷效果初探 promo_effect df.groupby(是否促銷)[銷售量].mean() print(f促銷期間日均銷量: {promo_effect[1]:.2f}) print(f非促銷期間日均銷量: {promo_effect[0]:.2f}) print(f促銷提升比例: {(promo_effect[1]/promo_effect[0] - 1)*100:.2f}%)從EDA中我們能得到什么趨勢銷量整體是在增長、下降還是平穩(wěn)這決定了我們是否需要先對序列進行差分等平穩(wěn)化處理。季節(jié)性是否存在明顯的每周循環(huán)周末銷量是否顯著高于工作日這提示我們在時間序列模型中必須加入季節(jié)性成分。商品特征哪些是“明星商品”高銷量、高利潤哪些是“長尾商品”對不同類別的商品庫存策略應(yīng)差異化。促銷初步證據(jù)促銷是否真的帶來了銷量提升提升幅度大概是多少這為后續(xù)的定量分析提供了方向。踩坑記錄我曾有一次忽略了對“節(jié)假日”的檢查直接建模結(jié)果預(yù)測在國慶節(jié)當(dāng)天嚴重偏低。因為歷史數(shù)據(jù)中國慶節(jié)門店休息銷售量為0模型學(xué)到了這個模式。但實際上未來國慶節(jié)可能營業(yè)。因此必須將“是否節(jié)假日”作為一個外部變量加入模型或者在預(yù)處理時將特殊節(jié)假日的異常值進行適當(dāng)處理如用相鄰日期的均值替代。4. 需求預(yù)測模型的實現(xiàn)與評估我們選擇幾種有代表性的模型進行實現(xiàn)并以一種核心商品為例。4.1 基于指數(shù)平滑的預(yù)測假設(shè)我們已針對某個核心商品如“礦泉水”提取了其每日銷售量的時間序列series。from statsmodels.tsa.holtwinters import ExponentialSmoothing from sklearn.metrics import mean_absolute_error, mean_squared_error # 劃分訓(xùn)練集和測試集例如最后14天作為測試 train_size len(series) - 14 train, test series[:train_size], series[train_size:] # 擬合Holt-Winters三重指數(shù)平滑模型考慮趨勢和季節(jié)性 # 假設(shè)我們觀察到明顯的年度內(nèi)周季節(jié)性period7 model_hw ExponentialSmoothing(train, trendadd, # 加法趨勢 seasonaladd, # 加法季節(jié)性 seasonal_periods7).fit() # 進行預(yù)測 forecast_hw model_hw.forecast(steps14) # 評估 mae_hw mean_absolute_error(test, forecast_hw) rmse_hw np.sqrt(mean_squared_error(test, forecast_hw)) print(fHolt-Winters模型 - MAE: {mae_hw:.2f}, RMSE: {rmse_hw:.2f}) # 可視化 plt.figure(figsize(12,6)) plt.plot(train.index, train.values, label訓(xùn)練集) plt.plot(test.index, test.values, label真實值, colorgreen) plt.plot(test.index, forecast_hw, labelHolt-Winters預(yù)測, colorred, linestyle--) plt.legend() plt.title(Holt-Winters指數(shù)平滑預(yù)測效果) plt.show()4.2 基于ARIMA模型的預(yù)測ARIMA模型參數(shù)(p,d,q)的選擇是關(guān)鍵通常通過觀察自相關(guān)圖(ACF)、偏自相關(guān)圖(PACF)或使用網(wǎng)格搜索AIC準則。from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf from statsmodels.tsa.arima.model import ARIMA import pmdarima as pm # 一個優(yōu)秀的自動ARIMA庫 # 1. 平穩(wěn)性檢驗 result adfuller(train) print(fADF Statistic: {result[0]:.4f}) print(fp-value: {result[1]:.4f}) # 如果p-value 0.05說明序列非平穩(wěn)需要差分。 # 2. 觀察ACF和PACF圖手動定階參考 fig, axes plt.subplots(1, 2, figsize(12,4)) plot_acf(train, lags40, axaxes[0]) plot_pacf(train, lags40, axaxes[1]) plt.show() # ACF拖尾PACF在p階后截尾 - AR(p)模型 # PACF拖尾ACF在q階后截尾 - MA(q)模型 # 兩者都拖尾 - ARMA(p,q)模型 # 3. 使用pmdarima自動定階推薦尤其對新手 auto_model pm.auto_arima(train, start_p0, start_q0, max_p5, max_q5, seasonalTrue, m7, # 周季節(jié)性 traceTrue, # 打印搜索過程 error_actionignore, suppress_warningsTrue, stepwiseTrue) # 使用逐步搜索更快 print(auto_model.summary()) # 4. 使用自動找到的最佳模型進行預(yù)測 forecast_arima, conf_int auto_model.predict(n_periods14, return_conf_intTrue) # 評估 mae_arima mean_absolute_error(test, forecast_arima) rmse_arima np.sqrt(mean_squared_error(test, forecast_arima)) print(fARIMA模型 - MAE: {mae_arima:.2f}, RMSE: {rmse_arima:.2f})4.3 模型評估與選擇我們得到了兩個模型的預(yù)測誤差。通常我們會用多個指標MAE RMSE MAPE在測試集上綜合評估。模型MAE (平均絕對誤差)RMSE (均方根誤差)MAPE (平均絕對百分比誤差)特點Holt-Winters值1值2值3擅長處理趨勢和季節(jié)性模型簡單直觀。ARIMA值4值5值6更通用能處理更復(fù)雜的序列模式但參數(shù)調(diào)優(yōu)稍復(fù)雜。如何選擇如果MAPE更小通常意味著模型相對精度更高。不僅要看誤差還要看預(yù)測圖。觀察預(yù)測值是否捕捉到了測試集的波動模式預(yù)測區(qū)間是否合理覆蓋了真實值考慮業(yè)務(wù)解釋性和穩(wěn)定性。Holt-Winters的三個參數(shù)水平、趨勢、季節(jié)業(yè)務(wù)上容易理解。ARIMA模型可能更靈活但過擬合風(fēng)險稍高。實操心得對于商業(yè)預(yù)測我往往會采用“模型組合”或“取優(yōu)”策略。例如用Holt-Winters做基線模型用ARIMA做對比。對于關(guān)鍵商品甚至可以簡單地對兩個模型的預(yù)測結(jié)果取平均這有時能有效降低單一模型的波動風(fēng)險。5. 庫存優(yōu)化模型的求解與應(yīng)用有了需求預(yù)測我們得到一個預(yù)測值μ和預(yù)測誤差的標準差σ我們就可以為楊阿姨計算科學(xué)的進貨量了。這里我們以經(jīng)典的報童模型和安全庫存模型為例。5.1 單周期報童模型求解適用于進貨后一次性銷售、期末未售出則報廢的商品如日報、節(jié)日禮品、生鮮。模型參數(shù)c: 單位進貨成本元/件p: 單位銷售價格元/件s: 單位殘值期末未售出的處理價值元/件通常s c。μ: 預(yù)測的平均需求量σ: 需求量的標準差代表不確定性Q: 需要決策的訂貨量關(guān)鍵公式最優(yōu)訂貨量Q*滿足累積分布函數(shù)F(Q*) (p - c) / (p - s)其中F(·)是需求量的概率分布函數(shù)。我們通常假設(shè)需求量服從正態(tài)分布N(μ, σ^2)。import scipy.stats as stats def newsvendor_order_quantity(p, c, s, mu, sigma): 報童模型最優(yōu)訂貨量計算假設(shè)需求正態(tài)分布 p: 售價 c: 成本 s: 殘值 mu: 預(yù)測平均需求 sigma: 預(yù)測需求標準差 # 計算關(guān)鍵比率Critical Ratio cr (p - c) / (p - s) # 確保關(guān)鍵比率在(0,1)區(qū)間內(nèi) if cr 0: return 0 # 賣一單虧一單不如不進 elif cr 1: # 理論上應(yīng)無窮大實踐中可設(shè)一個上限如 mu 5*sigma return mu 5 * sigma else: # 計算標準正態(tài)分布的分位數(shù) z stats.norm.ppf(cr) # 最優(yōu)訂貨量 Q_star mu z * sigma return max(0, Q_star) # 確保非負 # 示例某面包成本2元售價5元當(dāng)天未售出殘值0.5元 p, c, s 5, 2, 0.5 mu, sigma 100, 20 # 預(yù)測日均需求100個標準差20 Q_opt newsvendor_order_quantity(p, c, s, mu, sigma) print(f最優(yōu)訂貨量應(yīng)為: {Q_opt:.1f} 個) print(f關(guān)鍵比率為: {(p-c)/(p-s):.3f}) print(f對應(yīng)的服務(wù)水平不缺貨概率為: {stats.norm.cdf((Q_opt-mu)/sigma):.2%})5.2 多周期安全庫存模型對于可以持續(xù)銷售、需要周期性補貨的商品如礦泉水、零食我們需要在定期或定量補貨策略中設(shè)置安全庫存。模型參數(shù)LT: 補貨提前期天從下單到貨物上架的時間。D: 提前期內(nèi)的平均需求量 (D μ * LT)。σ_L: 提前期內(nèi)需求的標準差。如果每日需求獨立則σ_L σ * sqrt(LT)。CSL: 目標周期服務(wù)水平Cycle Service Level即在一個補貨周期內(nèi)不缺貨的概率。z: 對應(yīng)目標CSL的標準正態(tài)分布分位數(shù)如CSL95%則z≈1.645。安全庫存計算公式安全庫存 SS z * σ_L再訂貨點計算公式再訂貨點 ROP D SS μ * LT z * σ * sqrt(LT)def calculate_safety_stock_and_rop(mu_daily, sigma_daily, lead_time, service_level): 計算安全庫存和再訂貨點 mu_daily: 日平均需求 sigma_daily: 日需求標準差 lead_time: 補貨提前期天 service_level: 目標服務(wù)水平如0.95 # 提前期內(nèi)的平均需求 D_leadtime mu_daily * lead_time # 提前期內(nèi)需求的標準差 sigma_leadtime sigma_daily * np.sqrt(lead_time) # 對應(yīng)服務(wù)水平的標準正態(tài)分位數(shù) z stats.norm.ppf(service_level) # 安全庫存 safety_stock z * sigma_leadtime # 再訂貨點 reorder_point D_leadtime safety_stock return safety_stock, reorder_point # 示例礦泉水日需求預(yù)測 mu50箱sigma8箱補貨提前期3天希望達到95%的服務(wù)水平 mu_d, sigma_d 50, 8 LT 3 CSL 0.95 SS, ROP calculate_safety_stock_and_rop(mu_d, sigma_d, LT, CSL) print(f安全庫存應(yīng)為: {SS:.1f} 箱) print(f再訂貨點應(yīng)為: {ROP:.1f} 箱) print(f這意味著當(dāng)庫存水平下降到約{int(ROP)}箱時就應(yīng)立即下單補貨。補貨量可以參考EOQ模型計算。)核心要點與避坑需求分布假設(shè)上述計算均假設(shè)需求服從正態(tài)分布。對于銷量低、波動大的商品如小眾商品正態(tài)假設(shè)可能不成立應(yīng)考慮泊松分布或負二項分布。提前期不確定性上面的模型假設(shè)提前期是固定的。現(xiàn)實中提前期也可能波動。如果提前期也不確定均值為LT標準差為σ_lt那么提前期內(nèi)需求的標準差公式會變得更復(fù)雜σ_L sqrt(LT * σ_d^2 mu_d^2 * σ_lt^2)。忽略提前期波動會低估安全庫存。服務(wù)水平的成本更高的服務(wù)水平意味著更多的安全庫存和資金占用。楊阿姨需要權(quán)衡是愿意承擔(dān)更高的庫存成本還是接受偶爾缺貨帶來的銷售損失可以通過計算不同服務(wù)水平下的庫存持有成本與缺貨成本來尋找總成本最低的“經(jīng)濟服務(wù)水平”。6. 促銷效果分析與策略優(yōu)化促銷不是簡單的“打折就能多賣”需要定量分析其真實影響和最優(yōu)力度。6.1 構(gòu)建促銷分析數(shù)據(jù)集我們需要一個面板數(shù)據(jù)包含價格、促銷標志、銷量以及其他可能的影響因素如節(jié)假日、周末。# 假設(shè)我們已經(jīng)有一個按天和商品匯總的數(shù)據(jù)框 df_agg # 包含列日期、商品ID、價格、銷售量、是否促銷、是否周末、是否節(jié)假日 # 為簡化我們分析單個核心商品 df_product df_agg[df_agg[商品ID] core_product].copy() df_product[價格變化率] df_product[價格].pct_change() # 計算價格變動百分比 df_product[銷量變化率] df_product[銷售量].pct_change() # 計算銷量變動百分比 # 查看價格與銷量的散點圖 plt.figure(figsize(10,6)) plt.scatter(df_product[價格], df_product[銷售量], alpha0.5) plt.xlabel(價格) plt.ylabel(銷售量) plt.title(價格-銷量散點圖) plt.grid(True) plt.show()6.2 需求價格彈性回歸分析我們可以建立一個簡單的線性回歸模型來估計價格彈性。更嚴謹?shù)淖龇ㄊ鞘褂脤?shù)線性模型這樣回歸系數(shù)直接就是彈性系數(shù)。import statsmodels.api as sm # 使用對數(shù)線性模型log(銷量) β0 β1 * log(價格) β2 * 是否促銷 β3 * 是否周末 ε df_model df_product.dropna(subset[銷售量, 價格]).copy() df_model[log_銷量] np.log(df_model[銷售量]) df_model[log_價格] np.log(df_model[價格]) # 準備自變量 (添加常數(shù)項) X df_model[[log_價格, 是否促銷, 是否周末]] X sm.add_constant(X) # 添加常數(shù)項β0 y df_model[log_銷量] # 擬合OLS回歸模型 model_ols sm.OLS(y, X).fit() print(model_ols.summary()) # 解讀log_價格的系數(shù)就是需求的價格彈性。 # 例如系數(shù)為 -2.5意味著價格每上漲1%銷量平均下降2.5%。 price_elasticity model_ols.params[log_價格] print(f估計的需求價格彈性為: {price_elasticity:.3f})6.3 促銷方案利潤模擬有了價格彈性我們就可以模擬不同促銷折扣下的利潤情況。def simulate_promotion_profit(base_price, cost, base_demand, elasticity, discount_rate): 模擬給定折扣率下的利潤 base_price: 原價 cost: 成本 base_demand: 原價下的預(yù)測銷量 elasticity: 價格彈性負值 discount_rate: 折扣率如0.8表示打8折 promo_price base_price * discount_rate # 根據(jù)價格彈性估算銷量變化 price_change_ratio (promo_price - base_price) / base_price demand_change_ratio elasticity * price_change_ratio promo_demand base_demand * (1 demand_change_ratio) # 計算利潤 profit_per_unit promo_price - cost total_profit profit_per_unit * promo_demand return promo_demand, total_profit # 示例礦泉水原價2元成本1元平時每天賣100瓶價格彈性為-2.0 base_price, cost 2.0, 1.0 base_demand 100 elasticity -2.0 discount_rates [1.0, 0.95, 0.9, 0.85, 0.8] # 不打折95折9折85折8折 results [] for dr in discount_rates: demand, profit simulate_promotion_profit(base_price, cost, base_demand, elasticity, dr) results.append({ 折扣率: dr, 促銷價: base_price * dr, 預(yù)測銷量: demand, 單件毛利: base_price * dr - cost, 總利潤: profit }) results_df pd.DataFrame(results) print(results_df)通過這個模擬表格楊阿姨可以清晰地看到雖然打8折能極大提升銷量但由于單價毛利被壓縮總利潤可能反而低于打9折或95折。最優(yōu)促銷力度就在那個使總利潤最大化的點上。深度思考這個模型是簡化的?,F(xiàn)實中促銷還可能帶來“蠶食效應(yīng)”搶了其他高利潤商品的銷量和“滯后效應(yīng)”促銷結(jié)束后銷量會下滑。更復(fù)雜的模型需要引入交叉彈性、促銷衰減變量等。但對于楊阿姨的小店這個簡單模型已經(jīng)能提供遠超經(jīng)驗的決策依據(jù)。7. 模型集成與決策支持系統(tǒng)構(gòu)建單一的模型解決單一的問題。要真正解決楊阿姨的“困惑”我們需要將預(yù)測、庫存、促銷模型整合起來形成一個閉環(huán)的決策支持流程。7.1 構(gòu)建自動化分析流水線我們可以設(shè)計一個每周/每月運行的腳本自動化完成以下步驟數(shù)據(jù)更新與預(yù)處理自動讀取最新的銷售流水清洗并匯總。需求預(yù)測對每個核心商品運行時間序列模型生成未來N天的銷量預(yù)測及預(yù)測區(qū)間μ ± z*σ。庫存決策對于報童型商品調(diào)用newsvendor_order_quantity函數(shù)計算建議訂貨量。對于可補貨商品調(diào)用calculate_safety_stock_and_rop函數(shù)并結(jié)合當(dāng)前庫存水平判斷是否需要補貨并給出建議補貨量如使用EOQ模型。促銷分析定期如每月運行回歸模型更新各商品的價格彈性估計。當(dāng)考慮促銷時調(diào)用simulate_promotion_profit函數(shù)評估不同方案的利潤。報告生成將以上結(jié)果輸出成一份簡潔的報表包含未來一周銷量預(yù)測Top 10商品列表當(dāng)前庫存狀態(tài)與補貨建議清單哪些商品低于再訂貨點建議補多少促銷潛力商品分析列出價格彈性高、通過適度促銷能顯著提升總利潤的商品及建議折扣# 偽代碼框架示意 def weekly_decision_support(data_path): # 1. 數(shù)據(jù)加載與清洗 df load_and_clean_data(data_path) # 2. 對每個商品循環(huán)或向量化操作 decision_report [] for product_id in core_products: # 2.1 提取該商品歷史序列 ts extract_time_series(df, product_id) # 2.2 訓(xùn)練預(yù)測模型得到未來7天的 mu_forecast, sigma_forecast mu, sigma forecast_product(ts, horizon7) # 2.3 獲取商品主數(shù)據(jù)成本、售價、類型等 product_info get_product_master_data(product_id) # 2.4 根據(jù)商品類型進行庫存決策 if product_info[type] perishable: order_qty newsvendor_order_quantity(...) decision {產(chǎn)品: product_info[name], 類型: 報童型, 建議訂貨量: order_qty} else: ss, rop calculate_safety_stock_and_rop(mu, sigma, ...) current_stock get_current_stock(product_id) if current_stock rop: order_qty eoq_calculation(...) # 經(jīng)濟訂貨批量計算 decision {產(chǎn)品: product_info[name], 類型: 可補貨, 狀態(tài): 需補貨, 建議補貨量: order_qty} else: decision {產(chǎn)品: product_info[name], 類型: 可補貨, 狀態(tài): 庫存充足} decision_report.append(decision) # 3. 生成促銷分析報告略 # 4. 整合報告輸出為Excel或HTML generate_report(decision_report, ...) print(本周決策支持報告已生成。)7.2 從模型到行動的挑戰(zhàn)與應(yīng)對將數(shù)學(xué)模型落地到小店的日常經(jīng)營還會遇到一些挑戰(zhàn)數(shù)據(jù)質(zhì)量與連續(xù)性模型效果嚴重依賴數(shù)據(jù)。楊阿姨需要堅持規(guī)范地記錄每日銷售、進貨和價格變動數(shù)據(jù)。初期數(shù)據(jù)量少時模型可能不穩(wěn)定可以更多依賴模型給出的“趨勢”和“相對值”而非絕對數(shù)值。模型置信度管理要理解模型不是神諭。特別是預(yù)測模型一定有誤差。給楊阿姨的報告里除了一個預(yù)測數(shù)字一定要附上“預(yù)測范圍”如“預(yù)計明天銷量在85-115件之間”并說明這個范圍的可信度如“有90%的把握”。這能幫助她理解風(fēng)險。策略的靈活性模型建議的訂貨量是基準。楊阿姨還需要根據(jù)她的經(jīng)驗、天氣、周邊臨時活動如社區(qū)運動會等信息進行微調(diào)。模型是“科學(xué)”的部分她的經(jīng)驗是“藝術(shù)”的部分兩者結(jié)合才是最好的決策。系統(tǒng)簡易性對于楊阿姨一個復(fù)雜的Python腳本不如一個設(shè)計良好的Excel表格友好。最終交付物可以是一個半自動化的Excel模板她只需要每周粘貼新的銷售數(shù)據(jù)大部分計算由預(yù)設(shè)的公式和簡單的宏來完成關(guān)鍵結(jié)果用醒目的顏色標出。回顧“楊阿姨的困惑”這道賽題它的價值遠不止于一份獲獎?wù)撐?。它完整地展示了一個數(shù)據(jù)驅(qū)動決策的閉環(huán)從業(yè)務(wù)問題出發(fā)進行數(shù)據(jù)分析和建模得到量化結(jié)論最終反饋到業(yè)務(wù)動作中。這個過程里用到的預(yù)測、優(yōu)化、回歸分析等思想是數(shù)據(jù)分析的通用語言。通過這個案例的深度實踐你掌握的不僅僅是一道題的解法而是一套應(yīng)對未來無數(shù)個“張阿姨”、“李老板”困惑的思維工具和實戰(zhàn)能力。真正吃透這個過程比單純追求模型的復(fù)雜度更有意義。