Why forecast sales?
Humans have the magical ability to plan for future events, for future gain. It’s not quite a uniquely human trait. Because apparently ravens can match a 4-year-old.
An abundance of data, and some very nice R packages, make our ability to plan all the more powerful.
A couple of months ago we looked at sales from an historical perspective in Digital Marketplace. Six months later. In this post, we’ll use the sales data to March 31st to model a time-series forecast for the next two years. The techniques apply to any time series with characteristics of trend, seasonality or longer-term cycles.
Why forecast sales? Business plans require a budget, e.g. for resources, marketing and office space. A good projection of revenue provides the foundation for the budget. And, for an established business, with historical data, time-series forecasting is one way to deliver a robust projection.
The forecast assumes one continues to do what one’s doing. So, it provides a good starting-point. Then one might, for example, add assumptions about new products or services.
The power of iteration
Businesses typically deal with many product / service lines. So the ability to iteratively forecast multiple time series is very powerful.
We’ll deal first with each government framework: G-Cloud (cloud services), and DOS (Digital Outcomes & Specialists). Then we’ll iterate through G-Cloud’s lot structure: Cloud Hosting, Cloud Software and Cloud Support. Only three child levels, but the principle is easily scaled up.
G-Cloud suppliers are contractually-obliged under the government’s framework to report monthly on their buyer invoicing. So, if some months were missed, then there would be a one-time catch-up in a later month. This could result in the odd outlier in the Digital Marketplace sales data. However, as revealed in the more detailed analysis (with code), none was discovered.
By decomposing the historical data we can tease out the underlying trend and seasonality:
- Trend: G-Cloud sales have grown over time as more suppliers have added their services to the government frameworks. And more Public Sector organizations have found the benefits of purchasing Cloud services this way. Why? Because it’s a faster, simpler, more transparent and competitive contracting vehicle.
- Seasonality: Suppliers often manage their sales and financials based on a quarterly cycle. There’s a particular emphasis on a strong close to the financial year (often December 31st for commercial enterprises). And government buyers may want to make optimal use of their budgets at the close of their financial year (March 31st). Consequently, we see quarterly seasonality with an extra spike in March, and a secondary peak in December.
Forecasting sales for each framework
Using AutoRegressive Integrated Moving Average (ARIMA) modelling, we can select from close to 100 models to describe the autocorrelations in the data. Then we can use the generated model to forecast future sales.
In the plot below, we project two years ahead with 80% and 95% prediction intervals. This means the darker-shaded 80% range should include the future sales value with an 80% probability. Likewise with a 95% probability when adding the wider and lighter-shaded area.
The DOS framework (for project-related services) was launched more recently in June 2016. It exhibits different time-series characteristics. Hence a different ARIMA model.
Forecasting sales for the component lots
The G-Cloud framework comprises three lots. There are different ways of forecasting multiple time series. We will do so in one shot, with the best model tailored to each lot. The possible approaches, and code used, are detailed here.
Ravens aren’t yet ready for forecasting with R. But then neither are 4-year-olds, are they?
R packages and functions (excluding base) used throughout this analysis.
|purrr||map; map2_df; possibly; set_names; simplify; some; when|
|readr||guess_encoding; locale; read_csv; parse_number|
|dplyr||mutate; filter; group_by; if_else; summarise; desc; first; select; arrange; as_tibble; between; bind_rows; case_when; collapse; count; data_frame; n; summarize|
|tibble||as_tibble; data_frame; enframe|
|stringr||str_c; fixed; str_remove; str_count; str_detect; str_extract; str_replace|
|rebus||or; alpha; literal; whole_word|
|lubridate||month; year; ceiling_date; date; days_in_month; myd; parse_date_time; tz; ymd|
|tidyr||fill; unnest; nest|
|forecast||forecast; auto.arima; autoplot; BoxCox; mstl; ndiffs; nsdiffs; tsclean; BoxCox.lambda; seasonal|
|ggplot2||autoplot; xlab; ylab; aes; theme; labs; element_rect; geom_ribbon; unit; alpha; element_line; element_text; facet_wrap; geom_line; geom_path; geom_text; ggplot; margin; scale_x_date|
|scales||or; alpha; literal; whole_word|
View the code here.