After asking how to get rid of the seasonality of this set of data, I'd like to ask how to investigate the seasonality in data using Mathematica.

Which tool would you use to study seasonality? I.e. to realize that the seasonality is weekly. Is the Periodogram (link) a valid tool? Can I use SARIMA?

I've extended the set of data.

enter image description here

enter image description here

ps: i've extended the range of data

  • $\begingroup$ you data size is very small so I'd be doubtful that Periodogram (FT) will be that useful for that particular set of data. $\endgroup$ Mar 26, 2015 at 20:45
  • $\begingroup$ @MikeHoneychurch but the SARIMA model could find the periodicity (I can provide time series of 10 years on another example, but this was nice because the other question have already investigated many aspect on this dataset). $\endgroup$
    – Revious
    Mar 27, 2015 at 14:08
  • $\begingroup$ @MikeHoneychurch: I've added extra data. $\endgroup$
    – Revious
    Apr 3, 2015 at 13:39
  • $\begingroup$ Isn't this overly general? What's the actual problem? $\endgroup$ Apr 28, 2015 at 11:04
  • $\begingroup$ @GordonCoale: I'd like to check on if there is a seasonality in the data and to show it in a way that is human readable. I would also like to investigate more tools of future trend forecast but I don't want to ask too many things.. $\endgroup$
    – Revious
    Apr 28, 2015 at 12:01

2 Answers 2


Sjoerd's approach using SARIMAProcess and TimeSeriesModelFit, in particular the last portion with in which you test SARIMA models of differing orders and observe which models are particularly favoured by the AIC, is certainly a valid approach.

However, since you asked about periodograms and other spectral methods in your question, I thought I'd give an answer to show a few additional plots you could generate as part of your initial exploratory data analysis, before you proceed to model fitting.

Let's begin by reading in the data. Since others have already shown you features like DateListPlot, I'm just going to grab the column with the day-by-day traffic counts

counts = Rest[Import["sampleTrafficData.csv"]][[All, 2]] 

Now, because we're dealing with count data, we have a problem -- the variance of the measurements is not constant across our data. So we need to apply some form of variance stabilizing transformation. There are a couple of possible choices, generally falling into families of square-root transforms and logarithmic transforms. For the purposes of this discussion, let's use the Anscombe transform:

anscombe = 2. Sqrt[counts + 3/8]; 
len = Length[anscombe];

Note that I've also converted to purely numerical data at this stage; this speeds up processing later.

One potentially useful diagnostic is the autocorrelation function:

DiscretePlot[CorrelationFunction[anscombe, i], {i, 0, 25}, 
   PlotRange -> {-0.25, 1.045}, Frame -> True, Axes -> True, 
   GridLines -> {None, {{2/Sqrt[len], Dashed}, 
      0, {-2/Sqrt[len], Dashed}}}, 
   FrameLabel -> {"Lags", "Autocorrelation"}, ImageSize -> 500, 
   BaseStyle -> {FontSize -> 12}]

Plot of autocorrelation function

If we had independent and identically distributed measurement errors, we would expect almost all of the lagged autocorrelations to lie between the (approximate) 2$\sigma$ confidence bounds shown by the dashed gridlines. Clearly this is not the case here. Each day's traffic is strongly correlated with the traffic from the previous day. (Since we haven't attempted to remove any trend from the data, we're also likely seeing some contamination from that as well.) In addition, we can see some weak evidence for a regular pattern with period of 7 days -- although the autocorrelation is slowly decreasing at larger and larger lags, it rises to local maxima at lag 7, again at lag 14 (if you squint) and once more at lag 21.

A useful complement to the plot of the ACF is the partial autocorrelation function:

DiscretePlot[PartialCorrelationFunction[anscombe, i], {i, 1, 25}, 
   PlotRange -> {-0.3, 1.045}, Frame -> True, Axes -> True, 
   GridLines -> {None, {{2/Sqrt[len], Dashed}, 
      0, {-2/Sqrt[len], Dashed}}}, 
   FrameLabel -> {"Lags", "Partial Autocorrelation"}, ImageSize -> 500, 
   BaseStyle -> {FontSize -> 12}]

Partial autocorrelation function

The PACF tracks only that component of the autocorrelation not accounted for by earlier lags. In this case, we see that the autocorrelation at lag 1 is doing the bulk of the heavy lifting. If we were going to model this process as a purely autoregressive model, an AR(2) would be a good choice.

Other potentially interesting features lie at lags 7, 10, 16, but these should be interpreted with some caution. These partial autocorrelations are not particularly far outside the expected regime, and since we are dealing with 95% confidence intervals and making comparisons for 25 lags, we naively expect at least one "spurious" result in the mix. Here your physical intuition should come into play -- with a daily sampling rate, a period of 7 suggests a weekly pattern. Knowing what you do about your system, are there similarly compelling reasons to believe in a 10 or 16 day cycle?

Next up, we can take a look at the spectral density / FFT / periodogram:

Periodogram[anscombe, PlotRange -> All, Frame -> True, 
   FrameLabel -> {"Frequency (1/days)", "dB"}, 
   GridLines -> {Range[1, 3]/7, None}, ImageSize -> 500, 
   BaseStyle -> {FontSize -> 12}]

Raw periodogram

In the above plot, I've added gridlines for frequencies of 1/7 days and the next two harmonics (2/7, 3/7). With the gridlines to aid the eye, you could make an argument for a peak corresponding to a weekly period, but it does not exactly jump off the page. Unfortunately, the raw periodogram is a biased estimator, particularly when the dynamic range of the signal is large.

There are a couple of workarounds built into Mathematica to help reduce this issue, but they come at a cost. One option is to compute the periodogram for a number of subsets of the data and then average these estimates together. This helps reduce bias, but at the cost of resolution. For example, if we take average the results for the first and last halves of your data set, we obtain:

Periodogram[anscombe, Floor[len/2], PlotRange -> All, Frame -> True, 
   FrameLabel -> {"Frequency (1/days)", "dB"}, 
   GridLines -> {Range[1, 3]/7, None}, ImageSize -> 500, 
   BaseStyle -> {FontSize -> 12}]

Periodogram averaging first and last half of the data

In this plot, it becomes easier to argue for a peak corresponding to a periodicity of 1/7 days, but the peak has also become broader, as we've sacrificed resolution to suppress bias. There are no free lunches.

Alternatively, you can apply a smoothing filter on your data prior to computing the periodogram, such as the Blackman-Harris, using:

Periodogram[anscombe, len, len, BlackmanHarrisWindow, PlotRange->All, ... ]

Periodogram of filtered data

Here we've again picked up slightly stronger evidence of a peak at 1/7 days, but without the broadening of the peak. However, depending on the filtering method you choose, you're potentially washing out (destroying) other features in your dataset. Still no free lunch.

Mathematica comes with a number of common data filters (or "windows") built-in. Try out a couple of different choices in the above code snippet and see how your results change.

If you're really ambitious, you could try a slick combination of the above two techniques called multi-tapering. Unfortunately, this is not (yet) native functionality of Mathematica, so you'd have to roll your own. If you're interested, in these and other techniques, you might want to check out the book by Percival and Walden. (Their newer book on wavelet analysis for time series is also worth checking out.)

Sorry about the length. Hopefully you find this useful, and not too pedantic.

  • $\begingroup$ thanks for this clear instructive answer...book looks interesting...+1 :) $\endgroup$
    – ubpdqn
    May 15, 2015 at 5:14
  • $\begingroup$ it's really interesting and useful, thanks $\endgroup$
    – Revious
    May 18, 2015 at 13:56

First, paste your two columns of data copied from Google docs in Mathematica:

data = ImportString["Day\tTraffic
    6/5/2015\t282", "TSV"] // Rest;

Then convert the few 14s and 15s mingled between the 2014s and 2015s to full years, and convert to a TimeSeries:

dataTS = 
    DateList[{#, {"Day", "Month", "Year"}}] &, 
    {All, 1}
  ] /. {{14, r__} :> {2014, r}, {15, r__} :> {2015, r}} // TimeSeries

Plot it:

dataTS // DateListPlot

Mathematica graphics

Do a fit:

tsm = TimeSeriesModelFit[dataTS]

Mathematica graphics

Examine the fitted model:


SARIMAProcess[2.61293, {-0.244618, -0.233581}, 1, {}, {33, {-0.189394}, 1, {}}, 498.378]

To decipher this, you need the 5th and 1st syntax description (in V10):

Mathematica graphics

Mathematica graphics

The 5th syntax format says that the arguments list may start with a constant that may be omitted when it is zero. If it is present, the following parameters are used as in syntax formats 1-4. We need this part: {33, {-0.189394}, 1, {}}. This maps to:

Mathematica graphics.

So, Mathematica sees 33 days as the most appropriate model. However, this is not the end of the story. Since this is an optimization process in a multiple parameter space finding a global optimum is not guaranteed.

Checking the best candidate's AIC value systematically for periods of up to 35 days yields:

p = d = q = sp = sd = sq = s = Automatic;

TimeSeriesModelFit[dataTS, {"SARIMA", {{p, d, q}, {sp, sd, sq}, #}}][
    "AIC"] & /@ Range[35] // ListPlot

Mathematica graphics

So, 31 days and 7 days are better candidates, and indeed you can see that (more or less) in the plot above.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.