# STL trend of time series using R

I am new to R and to time series analysis. I am trying to find the trend of a long (40 years) daily temperature time series and tried to different approximations. First one is just a simple linear regression and second one is Seasonal Decomposition of Time Series by Loess.

In the latter it appears that the seasonal component is greater than the trend. But, how do I quantify the trend? I would like just a number telling how strong is that trend.

     Call:  stl(x = tsdata, s.window = "periodic")
Time.series components:
seasonal                trend            remainder
Min.   :-8.482470191   Min.   :20.76670   Min.   :-11.863290365
1st Qu.:-5.799037090   1st Qu.:22.17939   1st Qu.: -1.661246674
Median :-0.756729578   Median :22.56694   Median :  0.026579468
Mean   :-0.005442784   Mean   :22.53063   Mean   : -0.003716813
3rd Qu.:5.695720249    3rd Qu.:22.91756   3rd Qu.:  1.700826647
Max.   :9.919315613    Max.   :24.98834   Max.   : 12.305103891

IQR:
STL.seasonal STL.trend STL.remainder data
11.4948       0.7382    3.3621       10.8051
% 106.4          6.8      31.1         100.0
Weights: all == 1
Other components: List of 5
$win : Named num [1:3] 153411 549 365$ deg  : Named int [1:3] 0 1 1
$jump : Named num [1:3] 15342 55 37$ inner: int 2
$outer: int 0 ## 2 Answers I wouldn't bother with stl() for this - the bandwidth for the lowess smoother used to extract the trend is far, far, to small resulting in the small scale fluctuations you see. I would use an additive model. Here is an example using data and model code from Simon Wood's book on GAMs: require(mgcv) require(gamair) data(cairo) cairo2 <- within(cairo, Date <- as.Date(paste(year, month, day.of.month, sep = "-"))) plot(temp ~ Date, data = cairo2, type = "l") Fit a model with trend and seasonal components --- warning this is slow: mod <- gamm(temp ~ s(day.of.year, bs = "cc") + s(time, bs = "cr"), data = cairo2, method = "REML", correlation = corAR1(form = ~ 1 | year), knots = list(day.of.year = c(0, 366)))  The fitted model looks like this: > summary(mod$gam)

Family: gaussian

Formula:
temp ~ s(day.of.year, bs = "cc") + s(time, bs = "cr")

Parametric coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept)  71.6603     0.1523   470.7   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
edf Ref.df       F p-value
s(day.of.year) 7.092  7.092 555.407 < 2e-16 ***
s(time)        1.383  1.383   7.035 0.00345 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

R-sq.(adj) =  0.848  Scale est. = 16.572    n = 3780


and we can visualise the trend and seasonal terms via

plot(mod$gam, pages = 1) and if we want to plot the trend on the observed data we can do that with prediction via: pred <- predict(mod$gam, newdata = cairo2, type = "terms")
ptemp <- attr(pred, "constant") + pred[,2]
plot(temp ~ Date, data = cairo2, type = "l",
xlab = "year",
ylab = expression(Temperature ~ (degree*F)))
lines(ptemp ~ Date, data = cairo2, col = "red", lwd = 2) Or the same for the actual model:

pred2 <- predict(mod\$gam, newdata = cairo2)
plot(temp ~ Date, data = cairo2, type = "l",
xlab = "year",
ylab = expression(Temperature ~ (degree*F)))
lines(pred2 ~ Date, data = cairo2, col = "red", lwd = 2) This is just an example, and a more in-depth analysis might have to deal with the fact that there are a few missing data, but the above should be a good starting point.

As to your point about how to quantify the trend - well that is a problem, because the trend is not linear, neither in your stl() version nor the GAM version I show. If it were, you could give the rate of change (slope). If you want to know by how much has the estimated trend changed over the period of sampling, then we can use the data contained in pred and compute the difference between the start and the end of the series in the trend component only:

> tail(pred[,2], 1) - head(pred[,2], 1)
3794
1.756163


so temperatures are, on average, 1.76 degrees warmer than at the start of the record.

• Looking at the chart, I think there may be some confusion between Fahrenheit and Celsius. – Henry Apr 13 '11 at 15:36
• Well spotted - I've been doing something similar for a few months and the data are in degree C. Was force of habit! – Gavin Simpson Apr 13 '11 at 15:40
• Thanks Gavin, a very nice and understandable answer. I'll try your suggestions. Is it a good idea to plot the stl() trend component and make a linear regression? – pacomet Apr 14 '11 at 7:59
• @pacomet - no, not really, unless you fit a model that accounts for autocorrelation in the residuals as I did above. You could use GLS for that (gls() in package nlme). But as the above shows for Cairo, and the STL suggests for your data, the trend isn't linear. As such, a linear trend would not be appropriate - as it doesn't describe the data properly. You need to try it on your data, but an AM like I show would degrade to a linear trend if that fitted the data best. – Gavin Simpson Apr 14 '11 at 8:07
• @andreas-h I wouldn't do that; the STL trend is over fitted. Fit the GAM with the AR() structure and interpret the trend. That will give a proper regression model which will be far more useful to you. – Gavin Simpson May 17 '12 at 13:56

Gavin provided a very thorough answer, but for a simpler and faster solution, I recommend setting the stl function t.window parameter to a value that is a multiple of the frequency of the ts data. I would use the inferred periodicity of interest (e.g., a value of 3660 for decadal trends with diurnal resolution data). You may also be interested in the stl2 package described in the author's dissertation. I have applied Gavin's method to my own data and it is very effective too.