ChristianoFitzgeraldFilter#

class cultivars.spectral.band_pass.ChristianoFitzgeraldFilter(*, low=6.0, high=32.0)[source]

Bases: object

The Christiano-Fitzgerald asymmetric full-sample band-pass filter.

The random-walk variant – their recommended default – which keeps every observation by letting the filter turn asymmetric toward the endpoints, where the missing ideal weights are absorbed into the endpoint observations under a random-walk premise for the series. At observation \(t\) of a sample of \(T\),

\[c_t = b_0\, y_t + \sum_{j=1}^{T-2-t} b_j\, y_{t+j} + \tilde b_{T-1-t}\, y_{T-1} + \sum_{j=1}^{t-1} b_j\, y_{t-j} + \tilde b_{t}\, y_{0},\]

with \(\tilde b_m = -\tfrac{1}{2} b_0 - \sum_{j=1}^{m-1} b_j\) the endpoint weight that stands in for every ideal weight beyond the sample, the optimal choice when the series is a random walk. The filter is symmetric only far from both ends; elsewhere the estimate is phase-shifted and revised as observations arrive, which is the price of having a value at the recent end at all.

Parameters:
  • low (float) – Shortest period passed, in observations per cycle.

  • high (float) – Longest period passed.

Variables:
  • _low – The validated shortest period.

  • _high – The validated longest period.

Raises:

SpecificationError – If the band is malformed.

Note

The random-walk premise is what makes a linear trend acceptable in the input – a random walk with drift is the assumed process – but it is a premise about the series’ spectrum near zero, not a detrending step, and a series far from a random walk (a stationary series, or one with a strong deterministic trend and little else) is better served by removing the trend first. The symmetric flag on the result marks at least high observations on each side as the region where the asymmetric weights have decayed enough for the estimate to be treated as final; that boundary is a stated convention, not a theorem.

See also

References

Christiano, L. J., & Fitzgerald, T. J. (2003). The band pass filter. International Economic Review, 44(2), 435-465.

Example

>>> import numpy as np
>>> t = np.arange(300.0)
>>> y = np.sin(2 * np.pi * t / 12) + np.sin(2 * np.pi * t / 80)
>>> res = ChristianoFitzgeraldFilter(low=6, high=32).filter(y)
>>> res.cycle.shape
(300, 1)
>>> bool(res.symmetric[150] and not res.symmetric[0])
True
>>> round(float(np.corrcoef(res.cycle[:, 0], np.sin(2 * np.pi * t / 12))[0, 1]), 2)
1.0

Private attributes#