Skip to main content
added info on GAMLSS
Source Link
Stephan Kolassa
  • 130.7k
  • 22
  • 264
  • 497

Retail sales are typically characterized by overdispersion (Fildes et al., 2022, IJF - ping me on ResearchGate if you don't have access), which I included in the simulation above by generating data using a negative binomial distribution. If you want equidispersed data, you could use a Poisson distribution, which is simpler (but in my opinion less realistic).

More generally, one could use a GAMLSS (Generalized Additive Model for Location, Shape and Scale), where a relationship like the one above is used not only to parameterize the mean or location as in the example above, but also other parameters of a suitable distribution, like the shape or scale. Such methods generalize the simple negbin assumption used above. A good place to start is Ziel (2022, IJF) (open arXiv version here), who used GAMLSS methods to forecast retail sales, specifically the Walmart sales in the M5 forecasting competition.

R code for the picture above:

R code:

Retail sales are typically characterized by overdispersion (Fildes et al., 2022, IJF - ping me on ResearchGate if you don't have access), which I included in the simulation above by generating data using a negative binomial distribution. If you want equidispersed data, you could use a Poisson distribution, which is simpler (but in my opinion less realistic).

More generally, one could use a GAMLSS (Generalized Additive Model for Location, Shape and Scale), where a relationship like the one above is used not only to parameterize the mean or location as in the example above, but also other parameters of a suitable distribution, like the shape or scale. Such methods generalize the simple negbin assumption used above. A good place to start is Ziel (2022, IJF) (open arXiv version here), who used GAMLSS methods to forecast retail sales, specifically the Walmart sales in the M5 forecasting competition.

R code for the picture above:

Source Link
Stephan Kolassa
  • 130.7k
  • 22
  • 264
  • 497

The simplest hierarchical data generating process could use a parameter $p_i$ for the $i$-th product and another parameter $s_j$ for the $j$-th store. Then you could generate data with mean $p_i+s_j$ to stand for sales of product $i$ in store $j$.

You can then expand on this in various ways.

  • You could use a day of week pattern $w_{tj}$ that depends on the day of week of date $t$ (so $w_{tj}=w_{t+7,j}$) and on the store $j$, then add this to the mean: $p_i+s_j+w_{tj}$. You now have a weekly pattern that is common to all products in a store, but will differ between stores.
  • Or, as for overall means, you could mix day of week patterns per store as above with another set of patterns per product, $v_{ti}$ for the $i$-th product, for a mean of $p_i+s_j+w_{tj}+v_{ti}$.
  • Products are typically related hierarchically, with sales of different SKUs of milk being more closely related to each other than to sales of canned soup and diapers. You could model this by including terms that are set on various hierarchical levels.
  • You could add some yearly seasonality, e.g., by including harmonics. These could again be common per store, or per product, or per product hierarchy level.
  • Nothing says your terms have to be added. Multiply them together if you want.
  • Sales are typically integer, so I would recommend using some count data distribution. I personally like the negative binomial.
  • Especially if you use the negbin, it's natural to use a log link, by having each day's mean sales be $\exp f_{tij}$, however you calculated the log-mean $f_{tij}$ per day, product and store.

Here are some sales with $\mu_{tij}=\exp(p_i+s_j+w_{tj})$, i.e., we have a store-level day of week pattern:

simulated sales

R code:

n_stores <- 3
n_products <- 5
n_weeks <- 10
nb_overdispersion <- 2  # >1

set.seed(1)
store_levels <- rnorm(n_stores)
product_levels <- rnorm(n_products)
weekday_patterns <- replicate(n_stores,rnorm(7))

sales <- list()
for ( ss in 1:n_stores ) {
    sales[[ss]] <- list()
    for ( pp in 1:n_products ) {
        mu <- exp(store_levels[[ss]]+product_levels[[pp]]+rep(weekday_patterns[,ss],n_weeks))
        size <- mu/(nb_overdispersion-1)
        sales[[ss]][[pp]] <- rnbinom(7*n_weeks,mu=mu,size=size)
    }
}

opar <- par(mfcol=c(n_products,n_stores),las=1,mai=c(.3,.6,.4,.1))
    for ( ss in 1:n_stores ) {
        for ( pp in 1:n_products ) {
            plot(sales[[ss]][[pp]],type="o",pch=19,xlab="",
                ylab=ifelse(ss==1,paste("Product",pp),""),
                main=ifelse(pp==1,paste("Store",ss),""))
        }
    }
par(opar)