knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
\begin{figure}[h] \centering \includegraphics[width = 12cm]{bHP_illustration} \caption*{Illustration of bHP, by Iris Shi} \end{figure}
library(bHP) library(magrittr)
This vignette introduces the HP filter, the boosted HP filter, and the usage of the R package
bHP
.
The Hodrick-Prescott filter (HP filter; @hodrick1997postwar) is one of the fundamental statistical
tools in macroeconomic data analysis.
Thanks to its simplicity, it has been widely used in empirical
macroeconomics studies. As an operational algorithm, its pros and cons have been debated over
decades, and academic interest has been renewed in recent years to investigate its properties and extensions.
While @hamilton2017you argues against the usage of the HP filter, @Phillips2019boosting propose a
machine learning enhancement of the original HP filter, called the boosted HP filter (bHP),
and establish consistency when it is applied to a large class of trended time series in macroeconomic
applications.
Given a time series $(x_{t}){t=1}^n$ the HP method decomposes it into two additive components: a trend component $f{t}$ and a cyclical component (residual) $c_{t}$. The trend is estimated as [ (\hat{f}{t}^\mathrm{HP} ) =\arg\min{ (f_{t} )} \left{ \sum_{t=1}^{n} (x_{t}-f_{t} )^{2} +\lambda\sum_{t=2}^{n} (\Delta^ 2 f_{t} )^{2} \right}, ] where $\Delta f_{t}=f_{t}-f_{t-1}$, and $\Delta^2 f_{t}= \Delta f_{t}- \Delta f_{t-1} = f_{t}- 2 f_{t-1} + f_{t-2}$, and $\lambda\geq 0$ is a tuning parameter that controls the level of the penalty. The corresponding cycle is [ (\hat{c}{t}^\mathrm{HP} )=( x_t-\hat{f}{t}^\mathrm{HP}). ]
The optimization problem admits a closed form solution. The estimated trend can be written as \begin{equation} \widehat{f}^{\mathrm{HP}}=S x, \end{equation} where $S$ is a deterministic $n\times n$ matrix and $x=(x_1,...,x_n)'$ is the sample data. The estimated trend is [ \widehat{c}^{\mathrm{HP}}=\left(I_{n}-S\right)x, ] where $I_{n}$ is the $n\times n$ identity matrix. The explicit form of $S$ can be found in @Phillips2019boosting.
The choice of the tuning parameter is crucial for the behavior of the HP filter. In practice, @hodrick1997postwar recommend $\lambda=1600$ for quarterly data, and this number and its sampling frequency adjusted version [@ravn2002adjusting] are widely adopted. However, recent research [@phillips2015business] [@hamilton2017you] find the "gold standard" is too rigid for the length of time series that often used in macroeconomic studies.
The intuition of bHP is that, if the cyclical component $\widehat{c}{t}^{\mathrm{HP}}$ still exhibits trending behavior after HP filtering, we continue to apply the HP filter to $\widehat{c}^{\mathrm{HP}}$ to remove the leftover trend residual. After a second fitting, the cyclical component can be written as [ \widehat{c}^{\left(2\right)}=\left(I{n}-S\right)\widehat{c}^{\mathrm{HP}}=\left(I_{n}-S\right)^{2}x, ] where the superscript ``$\left(2\right)$'' indicates that the HP filter is fitted twice. The corresponding trend component becomes [ \widehat{f}^{\left(2\right)}=x-\widehat{c}^{\left(2\right)}=\left(I_{n}-\left(I_{n}-S\right)^{2}\right)x. ] If $\widehat{c}^{\left(2\right)}$ continues to exhibit trend behavior, the filtering process may be continued for a third or further time. After $m$ repeated applications of the filter, the cyclical and trend component are \begin{eqnarray} \widehat{c}^{\left(m\right)} & = & \left(I_{n}-S\right)\widehat{c}^{\left(m-1\right)}=\left(I_{n}-S\right)^{m}x \ \widehat{f}^{\left(m\right)} & = & x-\widehat{c}^{\left(m\right)}. \end{eqnarray}
The number of iterations $m$ is an additional tuning parameter in bHP. In practice, it is recommended that we choose $\lambda$ according to the convention, say $\lambda = 1600$ for quarterly data, and then monitor a stopping criterion as the iteration proceeds. @Phillips2019boosting suggest using either the ADF test or the Bayesian Information Criterion (BIC) to terminate the iteration.
This package bHP
automates bHP. The main function is BoostedHP
.
The user chooses the two tuning parameters lambda
for $\lambda$ and stopping
for the stopping criterion. In particular, three options are available for stopping
:
"BIC"
for the BIC stopping criterion"adf"
for the ADF stopping criterion (default $p$-value 5\%)"nonstop"
keeps iteration until it reaches Max_iter
(default is 100 iterations).The basic usage with the default options is as follows:
BoostedHP(x, lambda = 1600, iter= TRUE, stopping = "BIC", Max_Iter = 100)
The above line produces an object of the bHP
class. We can extract the trend by $trend
, the
cycle by $cycle
. The sequence of trend for each iteration is stored in $trend_hist
,
and $iter_num
reports the number of iterations.
The original HP filter can also be implemented by setting iter = FALSE
along with a lambda
.
One of the applications in @Phillips2019boosting is concerning the international comparison of the Okun's law. We use Ireland's annual GDP here for illustration.
lam <- 100 # tuning parameter for the annual data data(IRE) # load the data 'IRE' bx_HP <- BoostedHP(IRE, lambda = lam, iter= FALSE)$trend bx_BIC <- BoostedHP(IRE, lambda = lam, stopping = "BIC")$trend bx_ADF <- BoostedHP(IRE, lambda = lam, stopping = "adf")$trend bx_nonstop <- BoostedHP(IRE, lambda = lam, iter= TRUE, stopping = "nonstop") %>% predict( ) # use the generic method `predict` is an alternative way to get the trend matplot( y = cbind(IRE, bx_HP, bx_BIC, bx_ADF, bx_nonstop), type = "l", x = 1981:2016, ylab = "data and trends", xlab = "year", main = "Ireland Annual GDP") legend("bottomright", legend = c("data","HP", "BIC", "ADF", "nonstop"), col = 1:5, lty = 1:5)
The trend and cycle can also be extracted by the generic methods predict
and residuals
, respectively.
bx <- BoostedHP(IRE, lambda = lam, stopping = "BIC") IRE_cycle <- residuals(bx)
This is our first R package released on github
, labeled with Version 1.0.
The main function BoostedHP
and associated methods predict
, residuals
and BIC
are complete and
well documented.
The package also contains experimental generic methods print
, plot
and summary
,
which are still preliminary.
#-------- plot ----------- ?plot.bHP #--------- start to plot the content of bHP ----------------- #--------- for dynamic style (default)-------- plot(bx_ADF) plot(bx_ADF, iteration_location = "upright") # change the location of text plot(bx_ADF, iteration_location = c(30,12)) # assign the location of text by x-y co-ordinates plot(bx_BIC, interval_t = 0.8 ) # change the time interval of animation plot(bx_nonstop, cex_legend = 2, cex_text = 3) # change the magnification of legend and text # change the color plot(bx_ADF,main = "dynamic graph with new color",col_raw = "#685F74", col_trend_h = "#39A1A8", col_trend_f = "#DD4B4F", col_pvalue_BIC = "#E96145") plot(bx_ADF,main = "dynamic graph with new trancparency setting",raw_alpha = 200, trend_h_alpha = 55, trend_f_alpha = 250, pvalue_BIC_alpha = 250) plot(bx_HP) # nonstop-iter' bHP doesn't have dynamic picture: returning NA #--------- for JS style ---------- plot(bx_ADF,plot_type = "JS") # change the color plot(bx_ADF,plot_type = "JS",main = "Js graph with new color", col_raw = "#685F74", col_trend_f = "#DD4B4F", col_pvalue_BIC = "#39A1A8") plot(bx_BIC,plot_type = "JS") plot(bx_nonstop,plot_type = "JS") plot(bx_HP,plot_type = "JS") #--------- for static style ---------- plot(bx_ADF,plot_type = "static",cex_legend = 0.7, cex_text = 0.8 ) plot(bx_HP,plot_type = "static") plot(bx_BIC,plot_type = "static",cex_legend = 0.7, cex_text = 0.8 ) plot(bx_nonstop,plot_type = "static",cex_legend = 0.8, cex_text = 0.8 ) #----------- print ------------------------------- ?print.bHP #--------- start to print the content of bHP ----------------- print(bx_ADF) print(bx_ADF, Head = F, Tail = T, trend_hist = F) print(bx_ADF, Head = T, Tail = T, trend_hist = F) print(bx_ADF, Head = F, Tail = F, trend_hist = F) print(bx_BIC, Head = F, Tail = F, trend_hist = T, select_trend_hist = 1:bx_BIC$iter_num) print(bx_BIC, Head = F, Tail = F, trend_hist = T, select_trend_hist = c(1,3,5)) # when the trend_hist is FALSE, select_trend_hist is invalid print(bx_BIC, Head = F, Tail = F, trend_hist = F, select_trend_hist = c(1,3,5)) print(bx_BIC, Head = F, Tail = T, trend_hist = F, print_type = "latex") print(bx_BIC, Head = F, Tail = T, trend_hist = F, print_type = "html") # show the generic print function output print(bx_ADF, type = "generic default") #------------------ summary ----------------- ?summary.bHP summary(bx_ADF) summary(bx_BIC) summary(bx_nonstop) summary(bx_HP) #------------------ predict ----------------- ?predict.bHP predict(bx_HP) #Iterated number of HP filter: 1 predict(bx_ADF) #Iterated number of HP filter: 19 predict(bx_BIC) #Iterated number of HP filter: 5 predict(bx_nonstop) #Iterated number of HP filter: 99 #------------------ residuals ----------------- ?residuals.bHP residuals(bx_HP) #Iterated number of HP filter: 1 residuals(bx_ADF) #Iterated number of HP filter: 19 #------------------ BIC ------------------------- ?BIC.bHP BIC(bx_BIC) #Retrun the value path of BIC. #Iterated number of HP filter: 5 #Keep the path of BIC till iterated 6 times to show the tuning point. #[1] 1.586255 1.366335 1.293931 1.264323 1.254397 1.254620 BIC(bx_nonstop) #Retrun the BIC path of nonstop. #Iterated number of HP filter: 99 #Keep the path of BIC till iterated 100 times to show the tuning point. #[1] 1.586255 1.366335 1.293931 1.264323 1.254397 1.254620 1.260345 1.269139 1.279670 1.291179 #[11] 1.303223 ... ### If the test type is not "adf", Pvalue.bHP will return error # raw HP filter BIC(bx_HP) # Error in BIC.bHP(bx_HP) : # The stationary test type is nonstop-iter, not BIC or nonstop. # by ADF BIC(bx_ADF) #Error in BIC.bHP(bx_ADF) : #The stationary test type is adf, not BIC or nonstop.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.