Point forecasts versus the whole distribution

Today let's look at why a single forecasted number can hide a lot, using sixty days of real cake orders from a small bakery.

Maple & Rye is a bakery that takes orders for custom celebration cakes. Over the last 60 days it logged how many were ordered each day, mostly between 0 and 8 cakes, but with five catering-spike days jumping to between 15 and 22 at once. Across all 60 days the mean comes to 4.65 cakes a day, well above the median of 3.

Look at how those five catering-spike days tower over the rest of the history above. Any forecast for tomorrow has to somehow account for days like that, not just the quiet, ordinary ones.

What forecast() gives you by default: the mean

fable's simplest forecasting model is MEAN(): it takes the average of the whole history and repeats that same number as its forecast for every future day. Fit it on Maple & Rye's 60 days of orders, then forecast 14 days ahead.

RInteractive R
# Build the 60-day cake orders series, fit a MEAN model, and forecast 14 days ahead with a bootstrapped distribution library(tsibble) library(fable) library(fabletools) set.seed(42) base_orders <- rpois(60, 3) spike_orders <- rbinom(60, 1, 0.12) * rpois(60, 14) orders <- base_orders + spike_orders cakes <- tsibble(Day = 1:60, Orders = orders, index = Day) fit <- cakes |> model(Mean = MEAN(Orders)) set.seed(123) fc <- fit |> forecast(h = 14, bootstrap = TRUE, times = 2000) fc #> # A fable: 14 x 4 [1] #> # Key: .model [1] #> .model Day Orders .mean #> <chr> <dbl> <dist> <dbl> #> 1 Mean 61 sample[2000] 4.62 #> 2 Mean 62 sample[2000] 4.53 #> 3 Mean 63 sample[2000] 4.71 #> 4 Mean 64 sample[2000] 4.69 #> 5 Mean 65 sample[2000] 4.67 #> 6 Mean 66 sample[2000] 4.85 #> 7 Mean 67 sample[2000] 4.67 #> 8 Mean 68 sample[2000] 4.47 #> 9 Mean 69 sample[2000] 4.77 #> 10 Mean 70 sample[2000] 4.78 #> 11 Mean 71 sample[2000] 4.52 #> 12 Mean 72 sample[2000] 4.59 #> 13 Mean 73 sample[2000] 4.65 #> 14 Mean 74 sample[2000] 4.54

  

bootstrap = TRUE and times = 2000 tell forecast() to build each day's distribution by resampling the model's own residuals 2000 times, instead of assuming the distribution follows a bell curve. The Orders column holds each day's whole distribution, printed as sample[2000] since it is really 2000 simulated values, not one number. The .mean column pulls out just one summary of that distribution: its arithmetic mean.

Read the very first row. Day 61 is tomorrow, and .mean there is 4.62. That is forecast()'s own point forecast: the single number it hands you if you only look at .mean.

RInteractive R
# Round tomorrow's mean point forecast up to a whole cake, since a fraction of a cake cannot be baked ceiling(fc$.mean[1]) #> [1] 5

  

Maple & Rye cannot bake 4.62 of a cake, so the bakery would round this up to 5 whole cakes.