BaxterKingFilter#

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

Bases: object

The Baxter-King symmetric truncated band-pass filter.

The ideal band-pass weights \(b_j\) truncated at lag \(K\) and recentred,

\[\tilde b_j = b_j - \frac{1}{2K + 1} \sum_{i=-K}^{K} b_i, \qquad |j| \le K,\]

so that the weights sum to zero and the filter has zero gain at frequency zero: a constant, and a linear trend, are removed exactly rather than approximately. The filter is symmetric, hence phase-neutral everywhere it is defined, and it is defined only where \(K\) observations exist on each side, which is why a Baxter-King cycle ends \(K\) observations before the data does.

Parameters:
  • low (float) – Shortest period passed, in observations per cycle. The quarterly business-cycle convention is 6.

  • high (float) – Longest period passed; 32 quarterly.

  • truncation (int) – Half-length of the moving average; 12 is the paper’s quarterly recommendation. Larger approximates the ideal filter better and loses more sample.

Variables:
  • _low – The validated shortest period.

  • _high – The validated longest period.

  • _truncation – The validated half-length \(K\).

Raises:

SpecificationError – If the band or truncation is malformed.

Note

Larger truncation is not free in either direction: the approximation to the ideal gain improves as \(K\) grows, but \(2K\) observations are lost, and at the paper’s recommendation of 12 a quarterly sample loses six years. The recentring means the weights are no longer the ideal filter’s even inside the band; the gain at the band’s edges leaks and ripples, which is the price of finite length and is the same for every truncated filter.

See also

References

Baxter, M., & King, R. G. (1999). Measuring business cycles: Approximate band-pass filters for economic time series. Review of Economics and Statistics, 81(4), 575-593.

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 = BaxterKingFilter(low=6, high=32, truncation=12).filter(y)
>>> res.cycle.shape
(276, 1)
>>> bool(np.std(res.cycle) < np.std(y))
True
>>> float(np.abs(BaxterKingFilter().filter(3.0 + 0.1 * t).cycle).max()) < 1e-12
True

Private attributes#