In my previous tutorial Arima Models and Intervention Analysis we took advantage of the strucchange package to identify and date time series level shifts structural changes. Based on that, we were able to define ARIMA models with improved AIC metrics. Furthermore, the attentive analysis of the ACF/PACF plots highlighted the presence of seasonal patterns. In this tutorial we will investigate another flavour of intervention variable, the transient change. We will take advantage of two fundamental packages for the purpose:
* tsoutliers * TSA
Specifically, we will compare results obtained by modeling the transient change.
Outliers Analysis
Outliers detection relates with intervention analysis as the latter can be argued as a special case of the former one. A basic list of intervention variable is:
* step response intervention * pulse response intervention
A basic list of outliers is:
* Additive Outliers * Level Shifts * Transient Change * Innovation Outliers * Seasonal Level Shifts
An Additive Outlier (AO) represents an isolated spike.
A Level Shift (LS) represents an abrupt change in the mean level and it may be seasonal (Seasonal Level Shift, SLS) or not.
A Transient Change (TC) represents a spike that takes a few periods to disappear.
An Intervention Outlier (IO) represents a shock in the innovations of the model.
Pre-specified outliers are able to satisfactorily describe the dynamic pattern of untypical effects and can be captured by means of intervention variables.
Intervention Analysis – Common Models
A short introduction of the very basic common models of functions useful for intervention analysis follows.
Step function
The step function is useful to represent level shift outliers.
$$
\begin{equation}
\begin{aligned}
S_{T}(t) &=\left\{
\begin{array}{@{}ll@{}}
0 & \text{if}\ t < T \\
1 & \text{otherwise}\
\end{array} \right.
\end{aligned}
\end{equation}
$$
It can be thought as a special case of the transient change intervention model with delta = 1 (see ahead the transient change model). We can represent it with the help of the filter() function as follows. .
tc <- rep(0, 50) tc[20] <- 1 ls <- filter(tc, filter = 1, method = "recursive") plot(ls, main = "Level Shift - TC delta = 1", type = "s")
By adding up two step functions at different lags, it is possible to represent additive outliers or transitory level shifts, as we will see soon.
Pulse function
The pulse function is useful to represent additive outliers.
$$
\begin{equation}
\begin{aligned}
P_{T}(t) = S_{T}(t)\ -\ S_{T}(t-1)
\end{aligned}
\end{equation}
$$
It can be thought as a special case of the transient change intervention model with delta = 0 (see ahead the transient change model). We can graphically represent it with the help of the filter() function as herein shown.
ao <- filter(tc, filter = 0, method = "recursive") plot(ao, main = "Additive Outlier - TC delta = 0", type = "s")
Level Shift function
The level shift function is useful to represent level shift outliers. It can be modeled in terms of step function with magnitude equal to the omega parameter.
$$
\begin{equation}
\begin{aligned}
m(t) = \omega S_{T}(t)
\end{aligned}
\end{equation}
$$
The graphical representation is the same of the step function with magnitude equal to the omega parameter of the formula above.
Transient change function
The transient change function is useful to represent transient change outliers.
$$
\begin{equation}
\begin{aligned}
\ C(t) = \dfrac{\omega B}{1 – \delta B} P_{T}(t)
\end{aligned}
\end{equation}
$$
We can graphically represent it by the help of the filter() function as herein shown. Two delta values are considered to show how the transient change varies accordingly.
tc_0_4 <- filter(tc, filter = 0.4, method = "recursive") tc_0_8 <- filter(tc, filter = 0.8, method = "recursive") plot(tc_0_4, main = "TC delta = 0.4") plot(tc_0_8, main = "TC delta = 0.8")
Packages
suppressPackageStartupMessages(library(tsoutliers)) suppressPackageStartupMessages(library(TSA)) suppressPackageStartupMessages(library(lmtest)) suppressPackageStartupMessages(library(astsa))
Analysis
In the following, I will analyse the sex ratio at birth as based on the Arbuthnot dataset which provides information of male and female births in London from year 1639 to 1710. As done in ref. [1], a metric representing the fractional excess of boys births versus girls is defined as:
$$
\begin{equation}
\begin{aligned}
\dfrac{(Boys – Girls)}{Girls}
\end{aligned}
\end{equation}
$$
url <- "https://www.openintro.org/stat/data/arbuthnot.csv" abhutondot <- read.csv(url, header=TRUE) boys_ts <- ts(abhutondot$boys, frequency=1, start = abhutondot$year[1]) girls_ts <- ts(abhutondot$girls, frequency=1, start = abhutondot$year[1]) delta_ts <- boys_ts - girls_ts excess_ts <- delta_ts/girls_ts plot(excess_ts)
With the help of the tso() function within tsoutliers package, we identify if any outliers are present in our excess_ts time series.
outliers_excess_ts <- tso(excess_ts, types = c("TC", "AO", "LS", "IO", "SLS"))
outliers_excess_ts
Series: excess_ts
Regression with ARIMA(0,0,0) errors
Coefficients:
intercept TC31
0.0665 0.1049
s.e. 0.0031 0.0199
sigma^2 estimated as 0.0007378: log likelihood=180.34
AIC=-354.69 AICc=-354.38 BIC=-347.47
Outliers:
type ind time coefhat tstat
1 TC 31 1659 0.1049 5.283
A transient change outlier occurring on year 1659 was identified. We can inspect graphically the results too.
plot(outliers_excess_ts)
We found an outlier of Transient Change flavour occurring on year 1659. Specific details are herein shown.
outliers_excess_ts$outliers type ind time coefhat tstat 1 TC 31 1659 0.1049228 5.28339
# time index where the outliers have been detected (outliers_idx <- outliers_excess_ts$outliers$ind) [1] 31
# calendar years where the outliers have been detected outliers_excess_ts$outliers$time [1] 1659
We now want to evaluate the effect of such transient change, comparing the original time series under analysis with the same without such transient change.
#length of our time series
n <- length(excess_ts)
# transient change outlier at the same time index as found for our time series
mo_tc <- outliers("TC", outliers_idx)
# transient change effect data is stored into a one-column matrix, tc
tc <- outliers.effects(mo_tc, n)
TC31
[1,] 0.000000e+00
[2,] 0.000000e+00
[3,] 0.000000e+00
[4,] 0.000000e+00
[5,] 0.000000e+00
[6,] 0.000000e+00
[7,] 0.000000e+00
[8,] 0.000000e+00
[9,] 0.000000e+00
[10,] 0.000000e+00
...
The “coefhat” named data frame stores the coefficient used as multiplier for our transient change tc data matrix.
# converting to a number coefhat <- as.numeric(outliers_excess_ts$outliers["coefhat"]) # obtaining the transient change data with same magnitude as determined by the tso() function tc_effect <- coefhat*tc # definining a time series for the transient change data tc_effect_ts <- ts(tc_effect, frequency = frequency(excess_ts), start = start(excess_ts)) # subtracting the transient change intervention to the original time series, obtaining a time series without the transient change effect excess_wo_ts <- excess_ts - tc_effect_ts # plot of the original, without intervention and transient change time series plot(cbind(excess_ts, excess_wo_ts, tc_effect_ts))
We can further highlight the difference between the original time series and same time series without the transient change effect.
plot(excess_ts, type ='b', ylab = "excess birth ratio") lines(excess_wo_ts, col = 'red', lty = 3, type ='b')
A quick check on the residuals of the time series without the transient change effect confirms validity of the ARIMA(0,0,0) model.
sarima(excess_wo_ts, p=0, d=0, q=0)
Now, we implement a similar representation of the transient change outlier by taking advantage of the arimax() function within the TSA package. The arimax() function requires to specify some ARMA parameters, and that is done by capturing the seasonality as discussed in ref. [1]. Further, the transient change is specified by means of xtransf and transfer input parameters. The xtransf parameter is a matrix with each column containing a covariate that affects the time series response in terms of an ARMA filter of order (p,q). For our scenario, it provides a value equal to 1 at the outliers time index and zero at others. The transfer parameter is a list consisting of the ARMA orders for each transfer covariate. For our scenario, we specify an AR order equal to 1.
arimax_model <- arimax(excess_ts,
order = c(0,0,0),
seasonal = list(order = c(1,0,0), period = 10),
xtransf = data.frame(I1 = (1*(seq(excess_ts) == outliers_idx))),
transfer = list(c(1,0)),
method='ML')
summary(arimax_model)
Call:
arimax(x = excess_ts, order = c(0, 0, 0), seasonal = list(order = c(1, 0, 0),
period = 10), method = "ML", xtransf = data.frame(I1 = (1 * (seq(excess_ts) ==
outliers_idx))), transfer = list(c(1, 0)))
Coefficients:
sar1 intercept I1-AR1 I1-MA0
0.2373 0.0668 0.7601 0.0794
s.e. 0.1199 0.0039 0.0896 0.0220
sigma^2 estimated as 0.0006825: log likelihood = 182.24, aic = -356.48
Training set error measures:
ME RMSE MAE MPE MAPE MASE ACF1
Training set -0.0001754497 0.0261243 0.02163487 -20.98443 42.09192 0.7459687 0.1429339
The significance of the coefficients is then verified.
coeftest(arimax_model)
z test of coefficients:
Estimate Std. Error z value Pr(>|z|)
sar1 0.2372520 0.1199420 1.9781 0.0479224 *
intercept 0.0667816 0.0038564 17.3173 < 2.2e-16 ***
I1-AR1 0.7600662 0.0895745 8.4853 < 2.2e-16 ***
I1-MA0 0.0794284 0.0219683 3.6156 0.0002997 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
The model coefficients are all statistically significative. We remark that the resulting model shows a slight improved AIC result with respect the previous model. Further, both models show improved AIC values with respect previous tutorial discussed ARIMA models.
Let us plot the original time series against the fitted one.
plot(excess_ts) lines(fitted(arimax_model), col = 'blue')
Considering the transient change model formula, we can elaborate a linear filter with the AR parameter as coefficient and the MA parameter to multiply the filter() function result.
# pulse intervention variable
int_var <- 1*(seq(excess_ts) == outliers_idx)
# transient change intervention variable obtained by filtering the pulse according to the
# definition of transient change and parameters obtained by the ARIMAX model
tc_effect_arimax <- filter(int_var, filter = coef(arimax_model)["I1-AR1"],
method = "rec", sides = 1) * coef(arimax_model)["I1-MA0"]
# defining the time series for the intervention effect
tc_effect_arimax_ts <- ts(tc_effect_arimax, frequency = frequency(excess_ts), start = start(excess_ts))
It is interesting to compare two transient change effects, as obtained by the arimax() and the tso() functions.
# comparing transient change effect resulting by ARIMAX (red) with the tso() one (blue) plot(tc_effect_arimax_ts, col ='red', type='l', ylab = "transient change") lines(tc_effect_ts, col ='blue', lty = 3)
By eyeballing the plot above, they appear pretty close.
A quick check on the residuals of the time series without the transient change effect confirms the validity of the ARIMA(0,0,0)(1,0,0)[10] model.
excess_wo_arimax_ts <- excess_ts - tc_effect_arimax_ts sarima(excess_wo_arimax_ts, p=0, d=0, q=0, P=1, D=0, Q=0, S=10)
If you have any questions, please feel free to comment below.
References












Hi, I don’t understand what this bit of code does:
# transient change intervention variable obtained by filtering the pulse according to the
# definition of transient change and parameters obtained by the ARIMAX model
tc_effect_arimax <- filter(int_var, filter = coef(arimax_model)["I1-AR1"], method = "rec", sides = 1) * coef(arimax_model)["I1-MA0"]
Why do you multiply by: coef(arimax_model)["I1-MA0"]?
It is the “omega” coefficient inside the transient change function formula, as above shown.
Thank you very much for this article, it was very helpful!
but could you tell me what is your final conclusion in this your analysis.
My conclusion is to have highlighted the importance of the outliers/intervention analysis in time series. I have shown two techniques on that subject and compared their results, which appear very close.
Thank you very much for this article, it was very helpful!
I have a very general question. If I am looking at time series data and I know an intervention occurred at a specific point in time; however, the tso() function is unable to detect any outliers in the data, would it be too much to assume that the intervention had no significant effect on my variable? Especially with time series of very short length (>30?)
From the original paper “Joint Estimation of Model Parameters and Outlier Effects in Time Series”
from where the tso() implementation is determined.
“Based on these simulation results, the following guidelines for choosing the critical value C are recommended. For a series with moderate length (say between 100 and 200 ob- servations), a critical value C = 3.0 seems to be appropriate. For a series of shorter length, a critical value between 2.5 and 2.9 is recommended. We may consider a critical value greater than 3.0 for series of longer length (e.g., over 200 observations). In practice, it is recommended that more than one critical value be used in the analysis, to allow examination of the sensitivity of the results to the choice of the critical values
For most cases the power is a descreasing function of the critical value.
For critical values 2.75 and 3.0, the power of the procedure for detecting
outliers of size 3 standard deviation (a,) ranges between 50% and 60%, and
that for detecting outliers of sizes 4 and 5 standard deviation ranges between 85% and 99%.”
From the tso() function help:
“If no value is specified for argument cval a default value based on the sample size is used. Let n be the number of observations. If n <= 50 then cval is set equal to 3.0; If n = 450 then cval is set equal to 4.0; otherwise cval is set equal to 3+0.0025*(n-50).”
So with respect to the original paper, for a time series of length <=30, the tso() implementation chooses 3.0 as critical value (cval), while the paper suggests to choose cval within the range [2.5..2.9].
As a suggestion:
1. you may try to decrease the cval to 2.5 by giving it as input to tso()
2. check the ARIMA model the tso() function determines and if it is not satisfactory go for the chance to give the ARIMA model you will determine as input o tso()
If you are doing regression with Arima errors, or if you are just doing an elastic net regression, how would you do the intervention analysis when you know some of the change in the dependent variable is caused by the regressors?
Meaning, it could be a level shift, but that could be due to a level shift in regressor, so adding an indicator variable as a intervention variable would not be correct in this case, right?
PS: I saw one blog where you used breakpoints, and in this blog you use the TSO function. Do you recommend one over the other? TSO seems to tell you what the outlier is, while in your other blog, you seem to have to determine the intervention yourself with breakpoints. Could you please comment?
Suppose you are working with a known math relationship among two entities, relationship determined by some “a-priori” model (such as a macro-economic model), let us say for example time series “y” under analysis has as a regressor “xxx”, and such regression is significant also under level shifts of “xxx”. Then you can check if the regression structure is being broken by some other causes by taking advantage of the strucchange package. Here is some sample code.
set.seed(1227)
# simulate our regressor
xxx <- arima.sim(n=100, list(ar=c(0.2)), sd=sqrt(0.2))
# adding a LS in xxx at time = 50
xxx 50)*1
plot(xxx)
# y is our time series to analyse and we know that y has xxx as a linear regressor; adding some gaussian noise too
y <- 0.4*xxx + rnorm(length(xxx), 0, 0.1)
plot(y)
library(strucchange)
y_lm <- lm(y ~ xxx – 1)
summary(y_lm)
# no breakpoint found because the structure of the linear relationship is not broken by change of structure
bbb <- breakpoints(y ~ xxx – 1)
summary(bbb)
plot(bbb)
# breaking the structure of the relationship among y and xxx by a LS at time=80
y 80)*1
bbb <- breakpoints(y ~ xxx – 1)
# one breakpoint is the BIC minimum value
summary(bbb)
plot(bbb)
# breakpoint at 80
breakdates(bbb, breaks=1)
y_fstats<- Fstats(y ~ xxx – 1)
plot(y_fstats)
Answering now your third question about what package to use.
The tso function within tsoutliers package can be used when you want to identify the ARMA model of your time series "y" while identifiying outliers impact under the categories AO, LS, TC, IO, SLS. The time such outliers impact occur, it is not known a priori. Also, there is not any prior regression based model known about our time series. In this scenario, use tso.
If the intervention event time is known, use arimax() within the TSA package so that you can set the time and the flavor of expected intervention.
In general, the intervention time may be unknown or known. It may be known because some cause-effect relationship is part of the hypothesis. Examples of known interventions and related analysis can be found in the book "Design and Analysis of Time Series Experiments", McClearly, McDowall and Bartos, Oxford press.
If you have a known regression based relationship about two entities (time series), (see for example eq. 5 and 6 in $3 of the strucchange package vignette), then use the breakpoints(), efp(), Fstats() etc. functions of the strucchange package to test if any structural break has occurred. If the relationship between the time series y under analysis "degenerates" in a regression with a constant (y ~ 1), (fact that must be verified as significant before proceeed further with breakpoints identification), then you can use the strucchange package to date the breakpoints (date where level shift occurs) or the tsoutliers package as well by restricting the impact outliers to the LS flavor. Herein an example.
data(Nile)
tso_nile <- tso(Nile, types="LS")
tso_nile
plot(tso_nile)
summary(lm(Nile ~ 1))
nile_bp <- breakpoints(Nile ~ 1)
summary(nile_bp)
plot(nile_bp)
breakdates(nile_bp, breaks=1)
plot(Nile)
fitted.ts <- fitted(nile_bp, breaks = 1)
lines(fitted.ts, col = 4)
lines(confint(nile_bp, breaks = 1))
I have 50 decaying time series curves and each one has a similar event but at different time points in the decay. My goal is to show that the event causes a significant temporary increase in the decay.
What is the best way to handle this? Should I align all time series by the event and estimate the effect of the event? The events occur at different points in each decay so I suspect this will add a a lot of noise? Or should I treat each time series separately and estimate coefficients for each and then construct confidence intervals for each coefficient?
Sorry for the late answer. I think a picture of the time series together with the definition of the decay (I am thinking is a decay time that can be determined starting from the time series itself) may help in understanding your scenario. Also important to understand if those time series take values from the same physical process under observation or not, if the boundary conditions are the same, how to compute such decay time (?), and so on. For intervention analysis, I may suggest two books: Applied Econometric Time Series, W.Enders, Wiley Ed. and Design and Analysis of Time Series Experiments, R. McClearly, D. McDowall, B.J. Bartos, Oxford Press
How to interpret ARIMA(0,0,0)?
In case of ARIMA(0,0,0), the process y(t) under analysis is white noise plus a constant (intercept), i.e.:
y(t) = μ + e(t),
where μ is a constant representing y(t) mean (may be equal to zero), and e(t) is additive gaussian white noise (AWGN) with zero mean and constant variance.
Dear Giorgio,
Can you tell me that, is it possible to test if the intervention coefficient (in your case I1-MA0) is different than 0?
Dear Bora,
You may take advantage of the coefci() function within the lmtest package and then see if zero is comprised by the confindence interval. The “level” input param defaults to 0.95 confidence level, if you want you may also verify it against 0.99 confidence level
coefci(arimax_model)
coefci(arimax_model, level=0.99)
Dear Giorgio,
Really a nice trilogy. Thank you.
Two things I couldn’t understand.
1- At the end, how did you come to the idea of a seasonal arima model with order (0,0,0)x(1,0,0)?
2- You say, you apply sarima to pre-intervention data, however the plots don’t show that. Furthermore, what is excess_wo_arimax_ts in
sarima(excess_wo_arimax_ts, p=0, d=0, q=0, P=1, D=0, Q=0, S=10)? You didn’t define it.