Solved – Seasonal or non-seasonal? ETS and auto-arima disagree

arimaexponential-smoothingseasonalitytime series

I am working with the following monthly time series to build forecasting models:
enter image description here

From this plot, it's quite tricky to identify if there is some kind of seasonality or not. When I use the ets function, it comes up with a ETS(M,N,N) which I believe suggests non-seasonal data. However, my auto-arima function comes up with a ARIMA(0,1,0)(1,1,0)[12] which suggests seasonality, right? Why do the algorithm disagree? Am I interpreting it right?

Also, would you recommend a way to identify any potential seasonality in my time series? Ideally, I would like to make a preliminary analysis where I can state whether my time series is most likely seasonal, or not. Thank you.

This is the data I'm using:

structure(c(198.5, 206.5, 220.25, 210, 201.5, 235, 248, 249, 
243, 236.25, 236.25, 260.75, 282, 271, 298.8, 351.5, 324.9, 307.8, 
320, 322.6, 334.7, 344.6, 366.3, 395.2, 423.3, 462.4, 435, 440.6, 
412.7, 437.3, 460.9, 453.5, 455.8, 431.4, 424.4, 461.3, 484.7, 
500, 473.7, 435.7, 426.5, 425.2, 440, 320, 349.5, 368.4, 365.7, 
314.7, 329.5, 354.6, 315.9), .Tsp = c(2012.75, 2016.91666666667, 
12), class = "ts")

Best Answer

auto.arima() decides on whether or not to use seasonality based on the OCSB test. ets() uses information criteria to test for seasonality or non, as detailed in Hyndman et al., Forecasting with Exponential Smoothing: The State Space Approach. Incidentally, you are right that ETS(M,N,N) is nonseasonal: it has multiplicative errors, no trend and no seasonality.

Given that the two methods use two different approaches to detect seasonality, it's not surprising that the results will differ, unless seasonality is blatantly obvious. And in your case, seasonality is not obvious at all. Compare your data to an obviously seasonal time series:

series

foo <- structure(c(198.5, 206.5, 220.25, 210, 201.5, 235, 248, 249, 
243, 236.25, 236.25, 260.75, 282, 271, 298.8, 351.5, 324.9, 307.8, 
320, 322.6, 334.7, 344.6, 366.3, 395.2, 423.3, 462.4, 435, 440.6, 
412.7, 437.3, 460.9, 453.5, 455.8, 431.4, 424.4, 461.3, 484.7, 
500, 473.7, 435.7, 426.5, 425.2, 440, 320, 349.5, 368.4, 365.7, 
314.7, 329.5, 354.6, 315.9), .Tsp = c(2012.75, 2016.91666666667, 
12), class = "ts")
opar <- par(mai=c(.5,.5,.1,.1),mfrow=c(1,2))
    plot(foo)
    plot(AirPassengers)
par(opar)

One very useful way to detect seasonality is the so-called seasonplot. Here is a seasonplot of your data against the AirPassengers dataset:

seasonplot

library(forecast)
blackBodyRadiationColors <- function(x) {
    # x should be between 0 (black) and 1 (white)
    foo <- colorRamp(c(rgb(0,0,0),rgb(1,0,0),rgb(1,1,0),rgb(1,1,1)))(x)/255
    apply(foo,1,function(bar)rgb(bar[1],bar[2],bar[3]))
}
n.colors <- ceiling(length(foo)/frequency(foo))
colors.blackBody.foo <- blackBodyRadiationColors(seq(0,0.6,length.out=n.colors))
n.colors <- ceiling(length(AirPassengers)/frequency(AirPassengers))
colors.blackBody.AirPassengers <- blackBodyRadiationColors(seq(0,0.6,length.out=n.colors))

opar <- par(mai=c(.5,.5,.5,.1),mfrow=c(1,2))
    seasonplot(foo,col=colors.blackBody.foo,year.labels=TRUE)
    seasonplot(AirPassengers,col=colors.blackBody.AirPassengers,year.labels=TRUE)
par(opar)

It appears obvious to me that there is essentially no seasonal pattern to your data at all. In addition, as jbowman notes, it doesn't really look very stationary, and the large smoothing parameter ets() estimates ($\alpha=0.94$) looks uncomfortably close to a random walk, too. So it may be an idea to first take differences, then potentially look for seasonality.

(Incidentally, I use black body radiation colors because the Rainbow Color Map (is) (Still) Considered Harmful.)