3

I was utilising CausalImpact for a study. Only recently did I realise that the model described in the associated paper was different to the default model implemented in the package.

The paper was read like the default model was Local Linear Trend model for the structural time series. However, the default model used in the package is Local Level Model.

Could the author confirm this?

I think the author should probably make this clear in their online document or in the package help.

Fred
  • 525
  • 2
  • 10

2 Answers2

1

The default model in CausalImpact is defined in CausalImpact:::ConstructModel:

https://github.com/google/CausalImpact/blob/master/R/impact_model.R

A model with too many components can sometimes offer too much flexibility, providing unrealistically widening forecasts. This is why the default model does not include a local linear trend component, as you pointed out. You can add this and other components by defining a custom model:

https://google.github.io/CausalImpact/CausalImpact.html#using-a-custom-model

Kay Brodersen
  • 1,743
  • 10
  • 10
  • Thanks for the reply. I realised the difference between the paper description and package implementation when I tried to customise the model with bsts package and applied to the same dataset. And indeed, the use of local linear trend model resulted in a complete different fit and forecast. – Fred Mar 13 '17 at 13:22
0

As Kay mentioned, the Local Linear Trend is not included in the package, though they do provide a means of passing in a fitted bsts model if the user would like this functionality. This, however, means that the user might have to do re-implement other convenient features of the package (e.g., data normalizing and setting priors). A potentially simpler alternative is to hot patch the CausalImpact library.

First, copy ConstructModel from impact_model.R in a local function called, e.g., MyConstructModel. Change the line

ss <- AddLocalLevel(ss, y, sigma.prior = sd.prior)

and replace it with

sd_diffy = sd(diff(y), na.rm=TRUE)
slope.sd.prior <- Boom::SdPrior(sigma.guess = model.args$prior.level.sd * sd_diffy,
                        upper.limit = sd_diffy,
                        sample.size = kLocalLevelPriorSampleSize-1)
ss <- AddLocalLinearTrend(ss, y, level.sigma.prior = sd.prior, slope.sigma.prior=slope.sd.prior)

This assumes the same format for the priors on the slope as on the level (though with one less observation for estimating differences than levels).

Then, patch the package

R.utils::reassignInPackage("ConstructModel", pkgName="CausalImpact", MyConstructModel)

You could expand the above to make the MyConstructModel handle both cases and decide by looking at model.args, but then you'd also have the change the validation code that sanitizes model.args as well.

BeingQuisitive
  • 106
  • 1
  • 5