Components topic
Components
A model is a sum of components:
y(t) = sum_i H_i(t) x_i(t) + eps(t), eps ~ N(0, measurementVariance)
Each is a Gaussian process kernel in state-space form and owns a few consecutive states. The blocks are stacked block-diagonally, so a component only adds its own cost.
| component | kernel | states | prior | parameters |
|---|---|---|---|---|
LocalLevel |
Brownian motion, min(s,t) |
1 | diffuse | 1 |
LocalLinearTrend |
cubic spline, min(s,t)³/3 + min(s,t)²|s−t|/2 |
2 | diffuse | 1 |
TrigonometricSeasonal |
min(s,t) Σⱼ cos λⱼ(s−t) |
2 per harmonic | diffuse | 1 |
RegressionComponent |
a constant per column | 1 per column | diffuse | 0 |
Matern |
Matérn, ν = 1/2, 3/2, 5/2 | 1, 2, 3 | stationary | 2 |
StochasticCycle |
ρ^|τ| cos(2πτ/p), period estimated |
2 | stationary | 3 |
The last column is how many search dimensions each adds to fit. The
measurement variance is concentrated out and adds none, so a trend plus twenty
holiday indicators is a one-dimensional search.
Supported kernels
Only kernels with a finite-dimensional state-space form are supported, since
the linear cost relies on the Markov property; the table above is the list. A
new one is added by writing a Component, not by passing k(s, t), which
would cost O(N³).
The squared exponential is not supported: it has no exact finite-state form.
The non-stationary ones
These have no proper prior: the level, and the phase of a pattern, come from the data alone. The engine treats them as flat directions; see exact diffuse initialisation.
LocalLevel
A level following a Wiener process. One state, A(dt) = 1, Q(dt) = σ² dt. The
posterior mean is a linear interpolant through shrunken observations, the
continuous-time counterpart of simple exponential smoothing.
Use it when the series has no persistent direction. It will not extrapolate.
LocalLinearTrend
The default. The state is (level, slope); the slope is driven by white noise
and the level is its integral:
d(mu) = nu dt, d(nu) = sigma dB
The implied kernel is the cubic spline kernel, so the posterior mean is a natural cubic smoothing spline (Wahba 1978), here with a credible band and in linear time. How it works has the kernel and the smoothing parameter.
Unlike LocalLevel it carries a slope, and extrapolates it.
TrigonometricSeasonal
A repeating pattern of known period whose shape can drift. Each harmonic is a pair of states that rotate into one another over time, and the observation reads their sum.
TrigonometricSeasonal(period: 7, harmonics: 2, processVariance: 1e-3)
Two or three harmonics resolve a weekly shape on daily readings. At and past
the Nyquist frequency, 2 · harmonics · gap ≥ period, a harmonic aliases onto
a lower one or leaves a state the data can never see, so fit and smooth
refuse it with an UnderdeterminedModelException. The check uses the typical
gap between readings, so the period can be in any time unit: period: 1 with
time in years and monthly readings is fine.
processVariance is the rate at which the pattern may change shape, not its
amplitude. All harmonics share it (Harvey's specification), so the component
has one parameter.
Two properties worth knowing:
- Averaged over a full period each harmonic integrates to zero, so the component has no level of its own and does not compete with a trend for one. Over a stretch shorter than a period the two are confounded.
- Shrinking the variance to zero does not remove the component. It only stops the pattern changing, leaving a rigid Fourier series whose starting coefficients have a flat prior. Over less than one period a rigid sinusoid is very nearly a constant plus a slope, so an annual component on 180 days of data draws a cycle of 1.14 peak to trough in a series that has none, while reporting its variance at the floor. The component's own posterior standard deviation shows the problem: it is 1.27 there, larger than the pattern it drew and eighteen times the 0.07 of the total signal. With a year of data it is 0.065. See Choosing a model.
Dummy-variable seasonality is not available, since it has no sensible A(dt)
for a non-integer gap.
RegressionComponent
Coefficients on known functions of time, such as a fortnight over Christmas, a conference, a course of medication or a dose:
RegressionComponent([
IndicatorRegressor('christmas', [(from: 350, to: 364)]),
StepRegressor('dose', knots, values),
])
IndicatorRegressor is one while something is happening and zero otherwise;
StepRegressor holds a value between known instants. A regressor is data rather
than a closure, so it can be sent to an isolate.
Each coefficient is one state with A = I and Q = 0 under a flat prior, so
parameterCount is zero: a coefficient is a flat direction, which exact
diffuse initialisation integrates out anyway. Coefficients add no search
dimension and come with posterior standard errors from the same pass as the
trend:
posterior.coefficients.first; // christmas: 1.121 +/- 0.091
Because such a state never moves, the backward pass skips it; see
Component.isStatic.
The forward pass still carries every column as a flat direction, and its cost
grows roughly with the square of the number of columns: on two years of daily readings
a likelihood evaluation of a trend and a weekly seasonal takes 0.4 ms, with one
indicator 0.5 ms, and with twenty 20 ms, so a fit with twenty indicators costs
about fifty times one without.
The stationary ones
These have a proper prior: they stay around zero and forget their past. That suits a deviation from a trend rather than a level, and they add no flat directions.
Matern
The Matérn kernel at ν = 1/2, 3/2 and 5/2, exactly, with one, two and three states.
final model = StructuralModel([
LocalLinearTrend(processVariance: 1e-4),
Matern.oneHalf(variance: 0.1, lengthScale: 3),
]);
fit(model, data, minimumMeasurementVariance: 0.029 * 0.029);
Use it for short-lived correlated deviations that the trend should not follow.
ν = 1/2 is an Ornstein–Uhlenbeck process, the continuous-time AR(1). It takes
up correlated day-to-day variation that a trend-only model would count as
noise.
The order sets how rough the path is: LocalLinearTrend has a continuous
derivative, and ν = 1/2 allows corners.
Give fit a minimumMeasurementVariance whenever a Matérn is in the model.
With correlated day-to-day variation in the data, the likelihood can prefer a
large Matérn and a noise level near zero. fit searches for the alternative
and warnings reports it, but a floor at the instrument's resolution avoids
the problem.
StochasticCycle
An approximate rhythm: a damped cosine, rather than a pattern that repeats exactly. A seasonal of period 7 repeats every seven days forever; a cycle of period 7 tends to come round after about a week, drifts out of step, and comes back.
Its period is estimated, and the likelihood is multimodal in it: a cycle at
half the period explains every second peak and has its own maximum. fit scans
that axis much more finely than the others before the local search.
It needs a lot of data. Below about four complete cycles the period cannot be estimated, and below eight it is very noisy, although the fit still returns a value.
Check the damping before the period. On a series with no cycle the damping
goes to the top of its bracket, and the width reported for the period becomes
tiny and meaningless. warnings reports both, and the StochasticCycle API
documentation has the details.
A floor from the sampling
Below the sampling interval, a shape parameter measured in time units no longer
describes a different model. A Matérn with ν = 1/2 and a length scale shorter
than the gap between readings is effectively white noise, so it competes with
the measurement error instead of the trend, and the likelihood slightly prefers
it. Allowed down to a length scale of 0.01 on daily readings with a true noise
level of 0.3, it reports the noise as 0.002, with a band covering every
point and a trend that interpolates the noise.
So fit raises the bottom of lengthScaleBounds to the typical gap between
visits, and a cycle's periodBounds to twice it, which is the Nyquist limit.
Readings much closer together than the typical gap, such as two weighings on one
morning, count as one visit. A higher lower bound of your own is kept, and the
upper bound is used as given.
Writing your own
Import package:state_space/authoring.dart and extend Component. It is a
base class, so the subclass must be declared final, base or sealed:
final class MyComponent extends Component {
const MyComponent();
@override
String get name => 'MyComponent';
// stateDim, parameterCount, transition, processNoise, observationAt,
// diffuseStates, properPrior, parameters, withParameters
}
Nine members have no default: stateDim, parameterCount, transition and
processNoise (A(dt) and Q(dt), exact for any non-negative gap including
zero), observationAt, diffuseStates, properPrior for the states that are
not diffuse, parameters and withParameters. Override name with a literal,
and parameterSpecs if any parameter is not a variance.
MatrixBlock is the matrix type components write into: a view onto the
engine's buffer, so a component fills its own block in place without knowing
where that block sits.
checkComponent tests what the engine relies on and the type system cannot: the
declared lengths, A(0) = I and Q(0) = 0, a symmetric positive semi-definite
Q, consistency across gaps (Q(s + t) = A(t) Q(s) A(t)' + Q(t)), the
withParameters round trip, and an honest isStatic. It returns one sentence
per problem:
test('my component', () => expect(checkComponent(MyComponent()), isEmpty));
Classes
- Component Components How it works
- One additive block of a structural time-series model.
- LocalLevel Components
-
A level that follows a Wiener process:
d(mu) = sigma dB. - LocalLinearTrend Components
- A smoothly varying level whose rate of change is a Wiener process.
- Matern Components
- A stationary Matérn process: the standard Gaussian process kernel, in state-space form.
- ParameterSpec Components
- What one entry of a component's parameter vector is, as far as fitting is concerned.
- RegressionComponent Components
- Coefficients on known columns, estimated as part of the state.
- StochasticCycle Components
- A damped oscillation whose period is estimated from the data.
- TrigonometricSeasonal Components
- A repeating pattern of fixed period whose shape drifts over time.