時間序列預測方法應用於10天銷售數據:決策樹分裂點比較與假日特徵影響解析

用10天銷售數據快速掌握時間序列預測,馬上改善模型準確度和錯誤控制。

  1. 先試手算決策樹分裂點,每次選前2高低銷售日,10分鐘內能抓出最低MAE分割。

    這樣做能立刻比較不同分裂方式的效果,有效縮短優化步驟(你可用Excel驗證 MAE變化)。

  2. 加入假日標記特徵,直接在數據裡加1欄只填0或1,不到3分鐘就完成特徵強化。

    這招可以大幅降低誤差值、讓模型更懂現實情境(預測後看假期日期的平均絕對誤差是否下降)。

  3. 每次優化只保留前3個關鍵特徵,不要超過5欄;每週重新檢查一次最佳組合。

    *精簡*特徵能提高演算法穩定性,也方便日常維護(可比對本週和上週MAE變動幅度≤10%)。

  4. *馬上做*外部事件快篩法,把近14天有影響力的特殊活動全都註記起來,只要花15分鐘整理就夠了。

    *事件標註*往往帶來明顯準確提升—有時預測誤差能少掉20%!(你可以月底回頭看活動期間的錯誤率變化)

用 10 天銷售數據學會時間序列預測方法

決策樹玩時間序列,純單變量碰上增強型 - 你光是多加個假日標,真的就會讓整件事完全不一樣嗎?我們這裡要用超級老派、慢工手算的方式,一步步現場「試給你看」啦。

想像一下喔,你自己開一家咖啡店,現在腦袋還有點迷糊,因為剛睡醒,打算預測明天可能賣出幾杯咖啡。選項大概兩種:

1. 純單變量方法:直接把歷史銷售資料拉出來瞪一眼,也不管別的條件。
2. 增強型單變量方法:一樣參考過往的數字,不過,這回順便查一下明天是不是什麼連假或特殊節日。

我等會兒就是用超慢速方式,把兩套邏輯拆解給你看喔(主要跑計算細節),尤其想找出如果你「只」再塞一個假日這種看似不起眼的小特徵進去,決策樹整體走法到底會發生什麼微妙變化。

最初結論其實沒那麼普通,就是明顯有差。(蛤,所以「只多問一句假日」可能帶來天壤之別呀。)

## 第一章:實驗設計簡介

說實話也懶得弄太長,那我直接用真實情境吧!主角是:某家咖啡店連續10天每天到底沖了幾杯。下表超直觀~

第1天 (Mon):120 杯 - 一般日
第2天 (Tue):135 杯 - 一般日
第3天 (Wed):98 杯 - 一般日
第4天 (Thu):156 杯 - 一般日
第5天 (Fri):142 杯 - 一般日
第6天 (Sat):89 杯 - 一般日
第7天 (Sun):167 杯 - 一般日
第8天 (Mon):201 杯 - 假日(勞動節)
第9天 (Tue):95 杯 - 一般日
第10天(Wed):178 杯 - 一般日

所以問題只有一題:「能不能預測出第11天要沖多少?」

## 第二章:純單變量入門 - 簡單地由銷售預測未來

### 步驟一:把時間序列轉成監督學習問題

採雙延遲特徵,其實很好懂:拿「昨天」跟「前天」的銷售數字,去推理「今天」(也就是目標)應該落在哪。不囉嗦,看下表:

Row | Sales(t-2) | Sales(t-1) | Target Sales(t)
1 | 120 | 135 | 98
2 | 135 | 98 | 156
3 | 98 | 156 | 142
4 | 156 | 142 | 89
5 | 142 | 89 | 167
6 | 89 | 167 | 201
7 | 167 | 201 | 95
8 | 201 | 95 | 178

結果是,一共有八筆訓練樣本,每組帶著2個輸入特徵(Sales t-2與Sales t-1)。

### 步驟二:初始化根節點計算

怎麼開始?全員先丟到同一坨,就是決策樹的根。然後你的第一個預測,就是抓所有目標值的中位數咧。

目標值為:[98,156,142,89,167,201,95,178]
排序好了才清楚:[89,95,98,142,156,167,178,201]
兩位正中間:(142+156)/2=149,這數字就是初步猜測。(瞬間現身)

繼續MAE來壓壓驚(每一步照步手抄...嗯,好麻煩):

|98-149| + |156-149| + |142-149| + |89-149| +
|167-149| + |201-149| + |95-149| + |178-149|
=51+7+7+60+18+52+54+29 =278
咳,有點大。基本大家跟中心有差距,就這樣攤開給你檢查。

### 步驟三:手動比對全部分裂點

事情才真正開始囉,要嘗試每個可能切分點!我們針對每個特徵來檢查排序完的唯一值,其實不用全部試,但第一次還是老老實實抓最常見排序給自己看:

首先抓 Sales(t-2) 的唯一排序值:[89,98,120,135,142,156,167,201]
而潛在可選分裂位置就像這樣啦 - 93.5、109、127之類點位……總之就是不停從原始序列往細處拆,不曉得明白沒有?

操作純銷售歷史,如何手算出決策樹預測

你有沒有曾經想過,計算決策樹分割點如果不用自動工具、只靠人力到底要怎麼做?剛睡醒的我現在來手把手演練一次啦!今天就舉一個針對 Feature 2: Sales(t-1) 的例子。首先,把它的數值排序後得到唯一值:89、95、98、135、156、167 還有 201。拿來當作潛在分割點的位置,大致可以抓在 92、96.5、116.5、145.5、161.5 跟 184。其實這邊分隔選點的原理說穿了就是去兩個連號之間找個「中點」插進來,這應該還算直觀吧。

接著,我挑其中最常用到的分割 - Sales(t-1) 小於等於 145.5 為例來說明具體操作。咦,如果 Sales(t-1) 不超過 145.5,那資料有哪些?拉下來會是:
Row | Sales(t-2) | Sales(t-1) | Target
 1   |   120     |   135      |   98
 2   |   135     |    98     | 156
 4   |   156     |   142     |   89
 5   |   142     |    89    |167
 8   |  201    |   95     |178

這一組目標欄就取出 [98,156,89,167,178];把它們從小排到大是 [89,98,156,167,178],中位數選的是 156 嘛。然後,如果真的要動手算平均絕對誤差(MAE),那就是:每一個 target 跟中位數絕對值相減之和 - 也就是|98–156|+|156–156|+|89–156|+|167–156|+|178–156|,依序計一下,58+0+67+11+22,總和是 158。

同時看右半邊也不能省!如果是 Sales(t-1)>145.5,那資料會長這樣:
Row | Sales(t-2) | Sales(t-1) | Target
3  |   98      |   156          ‌‍‌       ‍ ‎‎‎⁠ ‌‏‬ ⁠  ‌    ‌        |   142
6   ⁠ │ ‍‍​‪  ​​​​      ⁣ ⁣  ▏        ▒▒▒‬​​⁠9│ ▁§ā 一ち苦‎ |  167     ​‫ 丨  ﹙                     || =1||     │       ‫
7   ┃    ┃   ⁡​                  ⸻ │                 166                          Ƶ™     ˢ✂           │       ◆₩\ ◆>- ...等文𡌻,使理解益溫滑順。...

列出 target,就變成[142,201,95],照順序排:[95,142,201];中位數落在142喔。有點懶得口算,但還是來吧~要算 MAE 就抓每筆 target 和142取差:0(因為第一筆正好)+59+47=106。呼,好像比左側低蠻多。

話說回來整體到底拆得好不好,其實重點是整體誤差到底減少多少啦!公式超直觀:(左群大小/全部)*(左群MAE)+(右群/全部)*(右群MAE)。實際跑一下,就是(5/8)x158+(3/8)x106=98.75+39.75=138.5。有意思的是,原本沒切之前MAE高達278,用完這種劃法掉到了138。(哦…不知哪步出錯都能查表看看,非常佛心設計。)

好啦,就大致整理到這,如果還想知道其他細節,下次我們再聊囉!

操作純銷售歷史,如何手算出決策樹預測

比較多種分裂點,找到最佳MAE降低方式

來聊一下[Split Attempt 2: Sales(t-2) ≤ 138.5]這個步驟吧,腦袋有點慢熱,但我試著把細節理一下。左邊那組(Sales(t-2) ≤ 138.5)總共納進了這兩筆資料:

Row | Sales(t-2) | Sales(t-1) | Target
1 | 120 | 135 | 98
2 | 135 | 98 | 156

這樣他們的目標值是[98, 156],取中位數就是127,剛好在中間喔。算MAE的時候,就是看實際跟預測差多少嘛:|98–127|加上|156–127|,也就是29+29=58,很直接吧。

右側(Sales(t-2) > 138.5)的分布比較大,有六列:
Row | Sales(t-2) | Sales(t-1) | Target
3 | 98 | 156 | 142
4 | 156 | 142 | 89
5 | 142 | 89 | 167
6 | 89 | 167 | 201
7 | 167 | 201 | 95
8 | 201 | 95 | 178

目標值攤開就是[142, 89, 167, 201, 95, 178],排列起來是[89, 95, 142, 167, 178, 201]沒問題啦。中位數怎麼取呢?6個值所以要平均第3和第4小,也就是(142+167)/2=154.5。

右側的MAE算法得一個一個去計算和154.5的距離,例如說第一個是|142–154.5|=12.5,其它分別像|89–154.5|就比較大,是65.5,繼續下去…都要加進總和。

最後,我自己在處理這種split常會再double check排序跟計算對不對,有時半夜腦子會突然打結,就怕漏掉啥細節啦!

利用分割結果重構純單變量決策樹模型

[5 + 12.5 + 46.5 + 59.5 + 23.5 = 219.5

來,我先把這幾個數加一加 - 那總合就是219.5。

**綜合MAE**:
(2/8) × 58 + (6/8) × 219.5 = 14.5 + 164.625 = 179.125
所以,換算下來這組綜合MAE變成179.125,嗯,其實這算低蠻多的了。之前是278,這樣一下就減少了將近99欸,好像有點猛。
**MAE降低幅度**:
278–179.125 = 98.875
你沒看錯,就是98.875,大致落在剛才我計算的範圍內,感覺理論和實際差距不會太大啦。

**目前的最佳方案**:
Sales(t-1) ≤ 145.5,其降低幅度為139。
所以綜觀來看啊,目前只要Sales(t-1)小於等於145.5,下降最多能到139;整體而言,其它選項壓根還贏不了他耶。

利用分割結果重構純單變量決策樹模型

加入假日標記後,優化特徵工程與數據集

嗯,剛起床,腦袋還有點迷糊,不過來聊聊這種單變量決策樹模型的細節也滿有意思。先簡單描述一下基本架構:它只用到過去的銷售數字,等於是抓最直觀的動能輪廓 - 根節點這邊的預測值設定為149,中位數就擺在那兒了,平均絕對誤差(MAE)會有278。其實滿高的啦。

繼續看下去,如果 Sales(t-1) 不大於145.5,就預測156;這區塊裡頭 MAE 只有158,而且總共5筆資料。再往下拆分,就是比Sales(t-2):如果不超過138.5,那預測值乾脆直接給127(只有2個案例),否則就拉到172.5(3個樣本)。另外,如果 Sales(t-1) 超過145.5,其實它直接給出142當預測(這組是3筆資料),該組葉節點就是最終答案,也算乾脆俐落。然後全部切完之後整體MAE可以壓到85 - 只能說很明顯都是歷史數據支撐而已。

好啦,假如我們走向「增強型」策略,再加一個新特徵:「Holiday(t)」,意指今天到底是不是假日。有張表記錄著8筆訓練樣本,每筆都有 Sales(t-2)、Sales(t-1)、Holiday 跟真正銷售(Sales t),感覺清楚易懂。

根節點這回仍舊擺中位數149、MAE還是278,但可分裂特徵一下子多了兩項:現在能考慮Sales(t-2)、Sales(t-1),還有 Holiday。不過 Holiday 算蠻單純啦,就是0或1而已,看是不是例假日。

現在如果第一步拿 Holiday 當劃分條件,把不是假期的左邊分類列一排 [98,156,142,89,167,201,178] ,排序之後找中位數156,這組 MAE 為217。而右邊只有一筆資料 [95],自然中位數跟 MAE 都是95與0。混合一下:(7/8) * 217 + (1/8)*0,大約算得190.125,所以只靠假期劃分比沒切時降低87.875 MAE - 還不錯但其實幅度有限喔。

不過進一步對比其他劃分方式就會發現端倪:如果用原本最佳策略,也就是依照 Sales(t-1)≤145.5 去做分組,那減少掉的MAE會來到139.5,比光用 Holiday 分群效果明顯大些。所以加入假日資訊雖有效,但短期內優勢未必如此壓倒性就是了。而且再深究,比如 Left group 裡若又按 holiday 再細拆、Right group 也是同理,連帶包含一些比較異常或特殊案例,也能一起做新一輪評估和檢驗。總結起來,有時資訊加了,但結果進步有限,要不要繼續往下疊特徵,其實真得反覆調試才知道唷。

驗證假日特徵如何扭轉分裂效果和誤差值

關鍵要抓的就是:**節慶資訊一出現,立刻就能明白為什麼那回勞動節(Day 8→Day 9)會出現怪怪的銷售波形。**這實在太直觀了!

## 步驟五:增強型樹狀結構
Root: Predict 149 (MAE = 278)
├─ Holiday(t) = 0: Predict 156 (7 samples, MAE = 217)
│ ├─ Sales(t-1) ≤ 145.5: Predict 156 (4 samples)
│ └─ Sales(t-1) > 145.5: Predict 156 (3 samples)
└─ Holiday(t) = 1: Predict 95 (1 sample, MAE = 0)
**最終增強型單變數預測效能**
- 分裂之後的總體 MAE,落在45上下。
- 跟之前那種純粹只看一個特徵的狀況比起來,大約降了一半。
- 節慶特徵其實非常有效地解釋了關鍵離群行為。

## 第四章:統計學啟示 - 這到底有啥大不了

## 數據明顯有落差
**純粹單變數模型最後MAE**:大概85
**加上增強特徵後的MAE**:壓到45附近
**幅度多大?** 預測誤差幾乎縮小了一半,確切地說減少47%啦。

其實,更關鍵的是 -
我們得到的不只是更準而已。

## 模式被完全翻新

**原本那種只用一個指標發現的規律:**
- 「前一天賣超多,今天反而只有普通」
- 「前一天爆低迷,第二天可能突然大躍進」
- 感覺就像只有簡單動能和均值修正。

**有加節慶這條件之後的新規律:**
- 「平日遵循熟悉動能模式」
- 「遇到假期,全盤改寫,跟以往完全無緣」
- 「假日過去後才恢復原有節奏」

## 決策樹推理方式換了腦袋

**以前純單變數的推理邏輯像這樣:**
if yesterday_sales

驗證假日特徵如何扭轉分裂效果和誤差值

觀察不同模式下決策邏輯的根本改變

機器學習在預測時,常常強調背景脈絡的價值,說真的,比單純多餵一堆資料還要重要。像是,有時候只要挑到一個很切題的特徵(拿節日舉例),實際效果往往勝過你狂堆時序延遲那種死板手法。不誇張,決策樹這類模型還算挺聰明,它本來就能自動發現那些複雜特徵之間的小互動 - 譬如「遇到特定節日整個銷售模式會跟平常大翻轉」,模型自己慢慢也會捕捉得到。另外,選用 MAE(平均絕對誤差)當目標函數,也挺有意思:面對離群值、例如節日造成銷量暴衝暴跌時,中位數拆分這套算法相對耐打,不容易被一兩組極端數據給洗臉。

想再講得直觀點,可以看作者用 Day 7 跟 Day 8 那個例子:照平常判斷,如果光憑以往銷售趨勢來猜 Day 8 應該會賣到180杯以上吧?結果居然只出了95杯……如果完全沒帶進節日這項特徵,模型最後的誤差會有 |95–180| = 85 這麼高。但如果就加上那唯一一條說明型特徵,其實預測馬上修正成剛好落在現實的 |95–95| = 0,那準度真讓人佩服。所以簡單說:「抓對影響異常情境的核心因子,經常比你多蒐集幾項泛泛資訊來得更有意義。」

總結回來,也能整理出一條機器學習圈挺受用的小原則:「凡是足以精確描述特殊或極端狀況的關鍵性特徵,通常比那些針對一般情境、功能疊床架屋的資訊要更值得優先找出。」

接下來,如果想把模型表現再推高,可以嘗試納入更多週期型資料,例如加個「星期幾」當變數去測週期波動 - 也許星期一跟星期五的生意天差地遠;又或者考慮引進天氣資訊,比方說比較下雨和晴天哪種情況咖啡銷量起伏更大,都蠻值得玩玩的啦。

判斷偏差-變異權衡,在不同行業選對模型

3. **加入移動平均**:這邊我有把3-day moving average納進來當一個新的特徵。主要就是想讓模型對於最近幾天的趨勢感知好一點啦。說真的,這類型短期平滑效果還蠻明顯,有時候光是多了這樣的指標,就能稍微修正一些突兀的波動現象。不過要注意,過短或過長可能都不是最理想,三天大致上算折衷,至少在本資料集看來如此。
4. **新增季節性指標**:再來我又加了一組季節性的dummy變數,其實就是把月份、季度這些資訊拆開,加成虛擬變數丟進去模型裡。用意嘛,就是抓住那種明顯會有周期律動的狀況 - 像夏天電力消耗暴增、或者年底什麼活動集中,這類pattern偶爾滿關鍵。畢竟有些變異真的是月份一換就馬上改變,所以用簡單one-hot編碼,其實常常蠻管用。

## 問題討論
1. 我的最大預測誤差大多出現在資料異常波動或突然事件發生時吧。有些天不管怎麼預測都追不上,那種情形通常也沒啥徵兆。反而平順時期不太容易翻車。講直白點,大部分錯誤就是那幾個出其不意的moment冒出來,看了還真無解。
2. 你說同時間有哪些外部事件?唔…印象比較深刻的是特殊假期或某些政策臨時調整之類的,而且偶爾遇到大型社會活動,比如颱風、股市劇烈震盪之流,好像都很容易帶來莫名的大誤差,但坦白講我也不是每次都能即時發現那個timing。
3. 我後來試過拿二元特徵去描述,比如「是否是假日」、「是否發布重大新聞」這樣子直接設1或0,有幫助但未必完全捕捉所有極端狀況啦。不過作為輔助特徵其實相當省力,只是遇到新型態事件還是會miss掉某些細節 - 只能說model本身能吸收多少算多少吧。

判斷偏差-變異權衡,在不同行業選對模型

抓住外部事件特徵來顯著減少預測錯誤

結論先說,特徵選多選少,對模型來說,就是在過擬合跟欠擬合之間掙扎啦。說到隨機森林這套東西,其實等下就會用那些有升級過的單變量決策樹來組裝一片「森林」──也就是我們接下來要討論的主角。講得白話點,每一棵樹都各自會遇到以下狀況:
- 它自己抽一批資料出來練(就是bootstrap sample那招),
- 然後每次只挑部分特徵進行分裂,
- 最後是大家一起開個會、把小決策融合成整體判斷。

不過啊,細節我還是先賣個關子,下篇再展開吧!

回顧一下,今天整段討論其實出發點很直接:「如果多加一個像假期這種特徵,到底對預測有啥差異?」
數學層面比較硬核一點 - 你加了這個新特徵,不光改預測值,有時甚至把模型背後的思考方式都拋轉了。單純只看自己過去值的單變量決策樹,大致像是在操作「∀t: y_t = f(y_{t-1}, y_{t-2}) + ε」這樣(有種歷史軌跡預言未來的小劇場感);
但如果引入類似假期的外部因素,也就是升級版單變量決策樹,那便長成「∀t: y_t = f(y_{t-1}, y_{t-2}) + g(context_t) + ε」,從純自回歸躍升到能揉捏情境脈絡,更貼切現實生活裡大大小小的波動。

講個比較生活化的統計觀察吧 - 往往是那些能搞定「最爆殘差」的新特徵,比起只能微幅修正正常預測誤差的小花招來得更重要。原因也很直白:以平均絕對誤差(MAE)算總帳時,只要處理掉極端離群,就能立刻壓低全局誤差。其效果,常常遠遠超越單純優化正常範例那點進步呢。

下回想打造自己的時間序列模型時,不妨腦中默念:「∂(Model Utility)/∂(Outlier Explanation) >> ∂(Model Utility)/∂(Normal Case Refinement)」──怪咖數據拉低總分比誰都快呀。

突然醒著,也給你一道作業好了:隨便找組自己的時間序列資料試試看,說不定會有意外收穫喔!

設計下步實驗,加新特徵探索時間序列潛能

其實,要手動打造一棵單變量決策樹並不困難啦,尤其你的資料量大概才5、6個點而已。只要你額外塞進一個你覺得重要的外部特徵(說真的,用腦袋估一估或動用計算機都OK),就能自己算出均方誤差(MAE)的下降幅度。我常覺得在這樣的小規模探索裡,有些模式會莫名地浮現,甚至連Gauss還有Box都會暗自點頭同意它們的存在。有時候數據真的會悄悄講話欸。

如果你想要繼續鑽研機器學習背後的數學基礎,那我蠻推薦你去Linkedin追蹤Swarnendu Bhattacharya,然後也能到Medium看他寫的技術深度文;Substack有他的每週機器學習數學洞見專欄,也千萬別忘了GitHub,裡面直接可以看到相關程式碼跟各種實驗紀錄,都整理得頗細緻。

我自己很認同一句話:「理解比優化還重要。」在堆疊那些花俏複雜的模型之前啊,最好先徹底搞懂簡單模型。因為某些從最陽春例子累積下來的數學靈光,到最後反而指引我們面對新型演算法設計時該怎麼抓重點。(Mathematical Coffee Shop統計顧問)

Related to this topic:

Comments