The Basics: Time Series and Seasonal Decomposition
By: Max Miller
Tags: Seasonal Decomposition, Time Series, Data Science Ground Up, Time Series Analysis, Data Science

Data Science from the Ground Up

The Basics: Time Series and Seasonal Decomposition

Handling time stamped data is one of the most intuitively obvious use cases for data science. After all, any data we can collect necessarily must be gathered from or represent the past and we often want to make predictions about unseen cases we’ll encounter in the future, so it makes sense that our models might have an important time component, in some form or other. Time series models are distinct from other sorts of predictive models in that the target variable is both the object of the prediction (for future values) and an input feature of the model (for the historical values).

The first steps in approaching a time series project are frequently to visualize and then decompose the data into trend and cyclical components. From there, you can start to work on more complicated predictive models, though simply decomposing the series can itself yield some valuable insights. This article will focus on the simplest decomposition technique, classical seasonal decomposition, but even this can be quite useful.

When are time series techniques appropriate?

Nowadays, most data, particularly procedurally generated data, comes with a timestamp. Whenever a digital form is filled out and submitted, or whenever a purchase is made online, the exact time is typically recorded and stored. As a result, many datasets come with a time element of some nature, but that doesn’t mean that the time series techniques discussed here are necessarily appropriate. These techniques only really make sense when there is some degree of auto-correlation in the target variable, that is, when the target variable is correlated with itself from earlier periods. If a value from yesterday or last month, or last year can help you predict the value for today, then these time series techniques are appropriate. If that isn’t the case, then they won’t help.

Consider, for instance, the day to day movements in stock prices, which are famously random. Knowing that a stock went up yesterday doesn’t really tell you whether the stock is likely to go up today. Here, for example, is a real life example of day to day stock price movements:

The day to day change in a certain stock’s price. Not really appropriate for time series analysis

The company in question is actually Apple, but you wouldn’t be able to tell that just by looking at the day to day movements — like all stocks, Apple’s bounces around from day to day in a way that defies prediction. Time series analysis won’t help.

Now, instead of looking at the day to day movement in price, let’s consider the stock price itself. Unlike the day to day change, the price does exhibit autocorrelation. Yesterday’s stock price gives me a very good indication of what today’s stock price will be because the price typically doesn’t change by more than a percent or two in any given day. If the price was $200 yesterday, I can be pretty sure it’ll close between $190 and $210 today, barring exceptional events. Out of the seeming noise of the day to day movements, the price itself exhibits a trend that suggests time series analysis might be useful:

The actual price of Apple stock. Maybe appropriate for time series analysis?

But then, stock prices are still stochastic in nature and while there might be some benefit in smoothing out a graph like this depending on what sort of analysis you’re doing, you’re going to have a hard time actually predicting future values. So, let’s consider one final Apple related series, the number of iPhones sold globally each quarter:

Number of iPhones sold each quarter. Definitely appropriate for time series analysis

Now, this is a time series we can work with! Notice that it doesn’t just exhibit a general trend, but also has predictable cycles within the trend — you’ll see a spike in sales each year corresponding with Apple’s first quarter (their accounting year starts in October, so their first quarter is picking up holiday sales). The goal of our time series techniques is often to find and exploit predictable patterns like this, even when the patterns are subtler and harder to spot than the seasonal pattern in iPhone sales.

Rolling averages and trends in time series

A common first step when approaching a new time series project is to smooth out the data with something like a rolling average. This has two main benefits. One is that real world data tends to jump around a bit, even if there is a clear underlying trend, and looking at the rolling average makes it easier to tell how the trend is moving underneath the noise. Second, finding a trend in this or a similar manner is the first step towards creating a seasonal decomposition. Let’s look at a real example. Here is a common starter time series data set, which shows the number of airline passengers there were per month in the US in the fifties:

Air travel boomed in the 1950's

There’s obviously an upward slope as air travel went from a relatively rare luxury in the late 40s to a much more common experience in the 60s, but there’s also clearly a seasonal pattern with travel being consistently higher in some months rather than others. The month to month variation makes it hard to see exactly how fast this growth is. We can smooth out the monthly fluctuations by considering a rolling average, where a few consecutive months are considered together. A particularly high or low value in one month will, therefore, be tempered by the less extreme values in the other months in the rolling window. Consider the original data alongside a six month rolling average:

Our data with a 6 month rolling average

The rolling average doesn’t get as high as the original data at its peaks, or as low during the troughs. One thing you may notice about this particular example is that it also doesn’t seem to hit the peaks and troughs at exactly the same time as the original data. This rolling average is more specifically a trailing average: the six months that make up the average for a given month are that month’s point and the five preceding months. As an alternative, you might consider centering your window on the month. You could, for instance, consider the month itself, the two previous months and the two subsequent months for a 5 month window (this tends to work better with odd numbers of months), but one that coincided with the peaks and troughs of the data rather than lagging them by a month or so:

Comparing rolling windows of equal length but different centers

You might be able to see already, that this strategy has an issue when you get towards the end of your data set — you can’t consider the average of a future month for which you have no data yet! In the above example, the centered rolling average simply stops two months before the end of the dataset while the trailing average goes all the way to the edge. There are some ways to deal with this — perhaps as you get towards the end and start running out of future months, you start to consider fewer months to close the gap — but these may not be satisfactory. (Of course, the trailing average also has a similar problem, but one at the beginning of the series, where you can’t take an average of the last 6 months if you’re only in, say month 2 or 3 of the data set.)

You can also vary the size of the window to adjust how much smoothing you do. As you increase the window, the rolling average will get less and less peaky:

Comparing windows of various size

There are a few other ways of smoothing out time series and finding trend lines, but they tend to be variations on the theme of the simple average. One common one worth mentioning is the Exponentially Weighted Moving Average (EWMA) which, true to its name, averages previous periods, but weights them so that the more recent periods count for more in the average.

Classical Seasonal Decomposition

One use of a creating a smoothed trend line in this fashion is to perform seasonal decomposition, to break out the original data into components for the trend, cyclical deviations from this trend and then whatever residuals are left over. How do we go from a smoothed line to the full decomposition? Well, notice that as you increase the size of the rolling average’s window the impact of the seasonal fluctuations fade away. If your rolling average considers a window that is the same size as your seasonal cycle, it essentially washes out the seasonal cycle altogether by considering one of each month in the cycle:

The 12 month rolling average doesn’t really have ‘seasons’ anymore

We’re looking for a seasonal cycle that accounts for the differences between the original data and this season-less rolling average. There are two ways to try to calculate this seasonal element. The first is as an additive model. In this method, we’ll imagine that the true value for any given month is the value of the trend at that month plus a static seasonal value that changes from month to month within a year but stays roughly the same for the same month in consecutive years, with some (hopefully small) amount of error left over. Let’s start by calculating a ‘de-trended’ series: taking our original series and subtracting the trend from it:

Our de-trended series. Spoiler: the fact that the amplitude of the seasonal pattern grows is a bad sign

This de-trended series shows us just the seasonal cycle along with whatever other noise there is in our data set. Now we can simply consider each month within the cycle one by one. How much above or below the trend is the average January data point? Or the average February? We can simply take a month by month average to get an approximation of the seasonal cycle:

Our seasonal component

Looking at these average seasonal components like this you can see clearly how air travel varied from month to month, with much more travel in the summer months than in the winter. Now we can model our original data as being comprised by the trend and this seasonal component:

Putting together our trend and seasonal component

Our modeled data in the green line isn’t horrible, but it’s clearly got a problem — it consistently overshoots in the early years and consistently undershoots in the later years. Actually, we could have foreseen this problem arising when we graphed out the de-trended data and saw that the seasonal swings got more and more extreme. In this case there is a simple fix, which is to consider the second way of decomposing the time series, the multiplicative model.

The multiplicative model works similarly to the additive one, except in this case we say that the final data for any given month is some value from the trend multiplied by some seasonal adjustment that stays roughly the same year over year. The additive model says something like “the number of flights is typically 40,000 higher than the trend in June”, while the multiplicative model says “the number of flights is typically 10% higher than the trend in June”. In this way, the multiplicative model scales the size of the seasonal cycle as the trend rises or falls.

Calculating the multiplicative seasonal cycle is similar to calculating the additive one, except that to find the de-trended series, we divide the original data by the trend:

The multiplicative de-trended series. Notice it no longer grows in amplitude as time goes on

We can find the monthly adjustments by averaging each month of this de-trended series, and then model out our data as the trend multiplied by the appropriate seasonal adjustment:

Much better!

You might already be able to tell that successfully decomposing a time series set in this fashion depends crucially on properly identifying the period of the seasonal cycle. Is it a yearly cycle? Or does it take two years? Or only 6 months? Often times there are clear calendar cycles — week to week, month to month or year to year — but depending on what sort of data you are working with, there might not be any reason for it to conform to the calendar. Choosing an inappropriately sized period will render your decomposition functionally useless.

As an example, imagine you were looking through a data set like the one we’ve been considering, but you didn’t know what it represented and didn’t assume that it had an even yearly period of 12 months. What happens if you select an 11 month period instead of a 12 month one? Well, at first things seem to be going ok. When you plot out your rolling average, it looks like you’ve more or less removed the seasonality:

An 11 month rolling average seems to work just fine…

When you perform the rest of your steps to decompose the series along the 11-month period, however, you quickly see that something isn’t right:

Well, as model’s go, this isn’t very helpful

This seasonal decomposition is basically useless, adding no real value above the smoothed out trend line. As you can see classical decomposition really depends on finding a sensible value for the period.

Classical decomposition may seem simple, and in a way it is and old-fashioned to boot, having been developed in the 1920s. Yet, hopefully, you can see that simple as it is, it can be fairly powerful, modeling out our data well. It may also be clear how you can extend this sort of technique to start predicting future values as well, drawing out the trend a few extra months and applying the appropriate monthly adjustment.

It’s also worth bearing it in mind for how it captures the time series process in general: try to isolate the trend, look for predictable patterns and account for them. You can then look for explanations for any significant deviations. There are more complicated ways of isolating trends, but they will all, basically by necessity, look at multiple points in time to try and isolate the trend from the day to day, week to week, or month to month variation. There are more advanced methods to look for predictable patterns in the de-trended data, including methods that consider multiple patterns of different periods happening at the same time, but in principle, they also work much the same way.

In my next post on time series, I’ll profile some of these more advanced methods and how they can be extended for predicting future values.