BandPassResult#

class cultivars.spectral.band_pass.BandPassResult(*, cycle, trend, low, high, method, offset, symmetric)[source]

Bases: _SummaryMixin

A filtered panel: the cycle, the remainder, and the bookkeeping.

The cycle is the band-pass component,

\[c_t = \sum_{j} \tilde b_j\, y_{t-j},\]

the input convolved with an approximation to the ideal band-pass weights \(b_0 = (\omega_h - \omega_l)/\pi\) and \(b_j = (\sin j\omega_h - \sin j\omega_l)/(\pi j)\), where \(\omega_l = 2\pi/\texttt{high}\) and \(\omega_h = 2\pi/\texttt{low}\) are the band’s edge frequencies; the trend is whatever the band did not pass, \(y_t - c_t\), so the two sum to the input on every row. Which approximation \(\tilde b\) was used, what it cost in observations, and where its estimate is phase-neutral and final are carried on the record as method, offset, and symmetric rather than left in fine print.

Variables:
  • cycle – The (rows, k) band-pass component. For Baxter-King, rows = nobs - 2 * truncation and the first cycle value aligns with observation offset of the input; for Christiano-Fitzgerald, rows = nobs and offset is zero.

  • trend – The input minus the cycle, over the same rows – everything outside the band, low and high frequencies together.

  • low – Shortest period in the band, in observations per cycle.

  • high – Longest period in the band.

  • method – "baxter-king" or "christiano-fitzgerald".

  • offset – Input observations lost before the first cycle value.

  • symmetric – (rows,) flags marking where the filter applied is symmetric, so the estimate is phase-neutral and final. All rows for Baxter-King by construction; for Christiano-Fitzgerald the flag is a stated convention – at least high observations on each side – because the asymmetric weights decay at the scale of the longest period in the band, and values outside the flag are both phase-shifted and subject to revision as data arrives.

Note

trend is a misnomer the literature shares: it holds the frequencies below the band and those above it together, so for a series with a seasonal or a high-frequency component that part lands in trend too. A user who wants the low-frequency part alone should pass a band whose low is 2, the shortest representable period, so that nothing lies above it. The summary’s “cycle share of variance” is \(\operatorname{var}(c) / (\operatorname{var}(c) + \operatorname{var}(y - c))\), which is a share only when the two are uncorrelated, as an ideal filter’s components are and a truncated one’s nearly are.

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.

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

Example

A 12-period cycle on top of an 80-period swing; the 6-32 band keeps the first and hands the second to trend:

>>> 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, res.offset, res.method, bool(res.symmetric.all())
((276, 1), 12, 'baxter-king', True)
>>> bool(np.allclose(res.cycle + res.trend, y[12:288, None]))
True
>>> round(float(np.corrcoef(res.cycle[:, 0], np.sin(2 * np.pi * t[12:288] / 12))[0, 1]), 2)
0.99
>>> full = ChristianoFitzgeraldFilter(low=6, high=32).filter(y)
>>> full.cycle.shape, full.offset, int(full.symmetric.sum())
((300, 1), 0, 236)

Private methods#

_summary_table()

Build the structured summary: the cycle's variance share per series.