diff --git a/docs/404.html b/docs/404.html index 801ccdf..c43aa96 100644 --- a/docs/404.html +++ b/docs/404.html @@ -67,7 +67,7 @@
diff --git a/docs/articles/Getting-started.html b/docs/articles/Getting-started.html index 38262ee..0b80e67 100644 --- a/docs/articles/Getting-started.html +++ b/docs/articles/Getting-started.html @@ -31,7 +31,7 @@ @@ -92,15 +92,15 @@The neverhpfilter
package consists of 2 functions
, 12 economic data sets, Robert Shiller’s U.S. Stock Markets and CAPE Ratio data from 1871 through 2019, and a data.frame
containing the original filter estimates found on table 2 of Hamilton (2017) <doi:10.3386/w23429>. All data objects are stored as .Rdata
files in eXtensible Time Series (xts
) format.
One of the first things to know about the neverhpfilter
package is that it’s functions accept and output, xts
objects. The author uses xts
often as it remains one of the most efficient data objects for managing times series data in econometrics and finance.
An xts
object is a list
consisting of a vector
index of some date/time class paired with a matrix
object of singular data type. In our case, type numeric
. data.table
is also heavily used in finance and has efficient date/time indexing capabilities as well. It is useful when working with large data.frame like lists containing vectors of multiple data types of equal length. If using data.table
or some other index based time series data object, merging the xts
objects created by functions of this package into your preferred data object should be fairly easy. Note xts
is a dependency listed under the “Suggests” field of data.table
DESCRIPTION file.
For more information on xts
objects, go here and here.
The neverhpfilter
package consists of 2 functions
, 12 economic data sets, Robert Shiller’s U.S. Stock Market and CAPE Ratio data from 1871 through 2019, and a data.frame
containing the original filter estimates found on table 2 of Hamilton (2017) <doi:10.3386/w23429>. All data objects are stored as .Rdata
files in eXtensible Time Series (xts
) format.
One of the first things to know about the neverhpfilter
package is that it’s functions accept and output, xts
objects.
An xts
object is a list
consisting of a vector
index of some date/time class paired with a matrix
object containing data of type numeric
. data.table
is also heavily used in finance and has efficient date/time indexing capabilities as well. It is useful when working with large data.frame like lists containing vectors of multiple data types of equal length. If using data.table
or some other index based time series data object, merging the xts
objects created by functions of this package should be fairly easy. Note xts
is a dependency listed under the “Suggests” field of data.table
DESCRIPTION file.
For more information on xts
objects, go here and here.
The yth_glm
function primarily exists to model the output for the yth_filter
. However, one can use this wrapper around glm
independently. On that note, the function API allows you to use the ...
to pass any additional arguments to glm
.
The yth_filter
returns an object of class glm
, so one can use all generic methods associated with objects of that class. Here is an example of passing the results of a yth_glm
model to the plot
function, which outputs the standard plot diagnostics associated with the method.
The yth_glm
function wraps glm
and primarily exists to model the output for the yth_filter
. On that note, the function API allows one to use the ...
to pass any additional arguments to glm
.
The yth_filter
returns an object of class glm
, so one can use all generic methods associated with glm
objects. Here is an example of passing the results of a yth_glm
model to the plot
function, which outputs the standard plot diagnostics associated with the method.
library(neverhpfilter)
data(GDPC1)
@@ -115,7 +115,7 @@
yth_filtered
-This is the main function of the package and both accepts, and outputs an xts
object. The resulting output contains various series discussed in Hamilton (2017). These are a user defined combination of the original, trend, cycle, and random walk series. See documentation and the original paper for further details.
+This is the main function of the package. It both accepts and outputs xts
objects. The resulting output contains various series discussed in Hamilton (2017). These are a user defined combination of the original, trend, cycle, and random walk series. See documentation and the original paper for further details.
@@ -138,8 +138,8 @@
## 2019 Q4 986.3695 985.7645 0.60492133 4.781143
## [1] "xts" "zoo"
-
The output is an xts
object, it inherits all generic methods and capabilities associated with xts
. For example, one can conveniently produce clean times series graphics with plot.xts
.
-Not the use of xts::addPanel
function, which adds a panel plot of the cycle
output of the yth_filter
to the original graph.
+As the output is an xts
object, it inherits all generic methods associated with xts
. For example, one can conveniently produce clean time series graphics with plot.xts
.
+Not the use of xts::addPanel
function, which is used to panel plot the cycle
component of the yth_filter
.
plot(log_RGDP, grid.col = "white", col = "blue", legend.loc = "topleft", main = "100 x Log of Real GDP (GDPC1)")
@@ -148,7 +148,8 @@
Choices for h and p
-In the paper that inspired this package, Hamilton converts the PAYEMS
series into quarterly periodicity and then uses filters on the transformed time series. With the yth_filter
function, one can choose to retain the monthly series and adjust the h
and p
parameters accordingly. The default parameters of h = 8
and p = 4
assume times series data of a quarterly periodicity. For time series of monthly periodicity, one can retain the same look-ahead and lag periods with h = 24
and p = 12
. See the yth_filter
documentation for more details.
+In the original paper, Hamilton aggregates the PAYEMS
monthly employment series into data of quarterly periodicity prior to apply his filter. That is a desirable approach when comparing with other economic series of quarterly periodicity. However, using the yth_filter
function, one can choose to retain the monthly series and adjust the h
and p
parameters accordingly.
+The default parameters of h = 8
and p = 4
assume times series data of a quarterly periodicity. For time series of monthly periodicity, one can retain the same look-ahead and lag periods with h = 24
and p = 12
. See the yth_filter
documentation for more details.
Employment_log <- 100*log(PAYEMS["1950/"])
employment_cycle <- yth_filter(Employment_log, h = 24, p = 12, output = "cycle")
@@ -156,7 +157,7 @@
plot(employment_cycle, grid.col = "white", type = "h", up.col = "darkgreen", dn.col = "darkred",
main = "Log of Employment cycle")
-In addition to adjust for various periodicities, one may wish to consider longer term cycles by extending h
. Below are examples of moving the look-ahead period defined by h
from 8 quarters (2 years), to 20 quarters (5 years), and then 40 quarters (10 years).
+In addition to adjusting parameters to accommodate other periodicities, one may wish to explore longer term cycles by extending h
. Below are examples of moving the look-ahead period defined by h
from 8 quarters (2 years), to 20 quarters (5 years), and then 40 quarters (10 years).
gdp_5yr <- yth_filter(log_RGDP, h = 20, p = 4, output = c("x", "trend", "cycle"))
plot(gdp_5yr["1980/"][,1:2], grid.col = "white", legend.loc = "topleft",
@@ -173,7 +174,7 @@
Conclusion
-These functions give you the option to, in the words of Hamilton, never use the Hodrick-Prescott filter.They identify a more stable trend component that doesn’t have the estimation issues associated with the head
and tail
portions of components generated by the HP filter.
+These functions give you an avenue to filter econometric time series into trend
and cycle
components. They identify a more stable trend and cycle components that doesn’t have the estimation issues associated with the head
and tail
portions of those generated by the HP-filter.
diff --git a/docs/articles/Reproducing-Hamilton.html b/docs/articles/Reproducing-Hamilton.html
index b855f82..a322c30 100644
--- a/docs/articles/Reproducing-Hamilton.html
+++ b/docs/articles/Reproducing-Hamilton.html
@@ -31,7 +31,7 @@
@@ -103,7 +103,7 @@ Justin M Shea
There’s a better alternative. A regression of the variable at date \(t + h\) on the four most recent values as of date \(t\) offers a robust approach to detrending that achieves all the objectives sought by users of the HP filter with none of its drawbacks.
-Using quarterly economic data, Hamilton suggests a linear model dependent on an h = 8
look-ahead period, which is independent of p = 4
lagged variables. An auto-regressive \(AR(p)\) model, dependent on \(t+h\) look-ahead, if you will. This is expressed more specifically by:
+Using quarterly economic data, Hamilton suggests a linear model on a univariate time series shifted ahead by some period h, regressed against a series of variables constructed form lagging the same series by some number of periods, p. An auto-regressive \(AR(p)\) model, dependent on a \(t+h\) look-ahead, if you will. This is expressed more specifically by:
\[y_{t+8} = \beta_0 + \beta_1 y_t + \beta_2 y_{t-1} +\beta_3 y_{t-2} + \beta_4 y_{t-3} + v_{t+8}\] \[\hat{v}_{t+8} = y_{t+8} + \hat{\beta}_0 + \hat{\beta}_1 y_t + \hat{\beta}_2 y_{t-1} + \hat{\beta}_3 y_{t-2} + \hat{\beta}_4 y_{t-3}\]
Which can be rewritten as:
\[y_{t} = \beta_0 + \beta_1 y_{t-8} + \beta_2 y_{t-9} + \beta_3 y_{t-10} + \beta_4 y_{t-11} + v_{t}\]
@@ -111,525 +111,146 @@ Justin M Shea
Implementation
-First, lets run the yth_filter
on Real GDP using the default settings suggested by Hamilton of an \(h = 8\) lookahead period and \(p = 4\) lags. The output is displayed below containing the original series, trend, cycle, and random components.
+First, lets run the yth_filter
on Real GDP using the default settings suggested by Hamilton of an \(h = 8\) look-ahead period (2 years) and \(p = 4\) lags (1 year). The output is displayed below containing the original series, trend, cycle, and random components.
The random component is simply the difference between the original series and its \(h\) look ahead, which is why it leads 8 NA
observations. Due to the \(h\) and \(p\) parameters, trend and cycle components lead with 11 NA
observations.
-
+
data(GDPC1)
gdp_filter <- yth_filter(100*log(GDPC1), h = 8, p = 4)
-kable(head(data.frame(Date=index(gdp_filter), coredata(gdp_filter)), 15), align = 'l')
-
-
-Date
-GDPC1
-GDPC1.trend
-GDPC1.cycle
-GDPC1.random
-
-
-
-1947 Q1
-761.7298
-NA
-NA
-NA
-
-
-1947 Q2
-761.4627
-NA
-NA
-NA
-
-
-1947 Q3
-761.2560
-NA
-NA
-NA
-
-
-1947 Q4
-762.8081
-NA
-NA
-NA
-
-
-1948 Q1
-764.3012
-NA
-NA
-NA
-
-
-1948 Q2
-765.9384
-NA
-NA
-NA
-
-
-1948 Q3
-766.5096
-NA
-NA
-NA
-
-
-1948 Q4
-766.6213
-NA
-NA
-NA
-
-
-1949 Q1
-765.2338
-NA
-NA
-3.503988
-
-
-1949 Q2
-764.8921
-NA
-NA
-3.429356
-
-
-1949 Q3
-765.9192
-NA
-NA
-4.663188
-
-
-1949 Q4
-765.0764
-772.3598
--7.2833996
-2.268271
-
-
-1950 Q1
-768.9313
-773.5218
--4.5905800
-4.630074
-
-
-1950 Q2
-771.9355
-774.6608
--2.7252604
-5.997144
-
-
-1950 Q3
-775.7271
-775.0227
-0.7044108
-9.217473
-
-
-
+head(data.frame(Date=index(gdp_filter), coredata(gdp_filter)), 15)
+## Date GDPC1 GDPC1.trend GDPC1.cycle GDPC1.random
+## 1 1947 Q1 761.7298 NA NA NA
+## 2 1947 Q2 761.4627 NA NA NA
+## 3 1947 Q3 761.2560 NA NA NA
+## 4 1947 Q4 762.8081 NA NA NA
+## 5 1948 Q1 764.3012 NA NA NA
+## 6 1948 Q2 765.9384 NA NA NA
+## 7 1948 Q3 766.5096 NA NA NA
+## 8 1948 Q4 766.6213 NA NA NA
+## 9 1949 Q1 765.2338 NA NA 3.503988
+## 10 1949 Q2 764.8921 NA NA 3.429356
+## 11 1949 Q3 765.9192 NA NA 4.663188
+## 12 1949 Q4 765.0764 772.3598 -7.2833996 2.268271
+## 13 1950 Q1 768.9313 773.5218 -4.5905800 4.630074
+## 14 1950 Q2 771.9355 774.6608 -2.7252604 5.997144
+## 15 1950 Q3 775.7271 775.0227 0.7044108 9.217473
In this next section, I reproduce a few of Hamilton’s tables and graphs, to make sure the functions approximately match his results.
In the Appendix, Employment (All Employees: Total Non-farm series) is plotted in the form of \(100 * log(\)PAYEMS
\()\) and superimposed with it’s random walk representation. (Hamilton 44). There are many good reasons to use xts
when handling time series data. Two of them are illustrated below in efficiently transforming monthly series to.quarterly
and in plot
ing the results of yth_filter
.
data(PAYEMS)
-log_Employment <- 100*log(xts::to.quarterly(PAYEMS["1947/2016-6"], OHLC = FALSE))
-
-employ_trend <- yth_filter(log_Employment, h = 8, p = 4, output = c("x", "trend"), family = gaussian)
-
-plot.xts(employ_trend, grid.col = "white", legend.loc = "topleft", main = "Log of Employment and trend")
data(PAYEMS)
+log_Employment <- 100*log(xts::to.quarterly(PAYEMS["1947/2016-6"], OHLC = FALSE))
+
+employ_trend <- yth_filter(log_Employment, h = 8, p = 4, output = c("x", "trend"), family = gaussian)
+
+plot.xts(employ_trend, grid.col = "white", legend.loc = "topleft", main = "Log of Employment and trend")
When filtering time series, the cycle component is of great interest. Here, it is graphed alongside a random walk representation (Hamilton 44).
-employ_cycle <- yth_filter(log_Employment, h = 8, p = 4, output = c("cycle", "random"), family = gaussian)
-
-plot.xts(employ_cycle, grid.col = "white", legend.loc = "topright", main="Log of Employment cycle and random")
-abline(h=0)
employ_cycle <- yth_filter(log_Employment, h = 8, p = 4, output = c("cycle", "random"), family = gaussian)
+
+plot.xts(employ_cycle, grid.col = "white", legend.loc = "topright", main="Log of Employment cycle and random")
+abline(h=0)
Turning the page, we find a similar graph of the cyclical component of \(100 * log\) of GDP, Exports, Consumption, Imports, Investment, and Government (Hamilton 45).
Below I merge
these data into one xts
object and write a function wrapper around yth_filter
and plot
, which is then lapply
’d over each series, producing a plot for each one.
fig6_data <- 100*log(merge(GDPC1, EXPGSC1, PCECC96, IMPGSC1, GPDIC1, GCEC1)["1947/2016-3"])
-
-fig6_wrapper <- function(x, ...) {
- cycle <- yth_filter(x, h = 8, p = 4, output = c("cycle", "random"), family = gaussian)
- plot.xts(cycle, grid.col = "white", lwd=1, main = names(x))
-}
fig6_data <- 100*log(merge(GDPC1, EXPGSC1, PCECC96, IMPGSC1, GPDIC1, GCEC1)["1947/2016-3"])
+
+fig6_wrapper <- function(x, ...) {
+ cycle <- yth_filter(x, h = 8, p = 4, output = c("cycle", "random"), family = gaussian)
+ plot.xts(cycle, grid.col = "white", lwd=1, main = names(x))
+}
When striving to recreate a statistical method found in a journal or paper, one can perform surprisingly well by thoroughly digesting the relevant sections and “eyeballing” graphs included in the authors work.
-Better still, is a table presenting the authors results, which one may use to directly compare with their own reproduction. Fortunately for us, Hamilton’s Appendix displays such a table which I use to test against estimates computed with functions contained in neverhpfilter
.
When striving to recreate a statistical method found in a journal or paper, one can perform surprisingly well by thoroughly digesting the relevant sections and “eyeballing” graphs included in the original author’s work.
+Better still, is finding a table presenting said author’s estimates, which one can use to directly compare with their own. Fortunately for us, Hamilton’s Appendix displays such a table which I use here to test against functions contained in neverhpfilter
.
His results are displayed below in table 2 (Hamilton 40), which I’ve stored as a data.frame
in this package.
kable(Hamilton_table_2[-NROW(Hamilton_table_2),], align = 'l', caption = "Hamilton's results: table 2, pg. 40")
- | cycle.sd | -gdp.cor | -random.sd | -gdp.rand.cor | -Sample | -
---|---|---|---|---|---|
GDP | -3.38 | -1.00 | -3.69 | -1.00 | -1947-1/2016-1 | -
Consumption | -2.85 | -0.79 | -3.04 | -0.82 | -1947-1/2016-1 | -
Investment | -13.19 | -0.84 | -13.74 | -0.80 | -1947-1/2016-1 | -
Exports | -10.77 | -0.33 | -11.33 | -0.30 | -1947-1/2016-1 | -
Imports | -9.79 | -0.77 | -9.98 | -0.75 | -1947-1/2016-1 | -
Government-spending | -7.13 | -0.31 | -8.60 | -0.38 | -1947-1/2016-1 | -
Employment | -3.09 | -0.85 | -3.32 | -0.85 | -1947-1/2016-2 | -
Unemployment-rate | -1.44 | --0.81 | -1.72 | --0.79 | -1948-1/2016-2 | -
GDP-Deflator | -2.99 | -0.04 | -4.11 | --0.13 | -1947-1/2016-1 | -
S&P500 | -21.80 | -0.41 | -22.08 | -0.38 | -1950-1/2016-2 | -
10-year-Treasury-yield | -1.46 | --0.05 | -1.51 | -0.08 | -1953-2/2016-2 | -
Fedfunds-rate | -2.78 | -0.33 | -3.03 | -0.40 | -1954-3/2016-2 | -
## cycle.sd gdp.cor random.sd gdp.rand.cor Sample
+## GDP 3.38 1.00 3.69 1.00 1947-1/2016-1
+## Consumption 2.85 0.79 3.04 0.82 1947-1/2016-1
+## Investment 13.19 0.84 13.74 0.80 1947-1/2016-1
+## Exports 10.77 0.33 11.33 0.30 1947-1/2016-1
+## Imports 9.79 0.77 9.98 0.75 1947-1/2016-1
+## Government-spending 7.13 0.31 8.60 0.38 1947-1/2016-1
+## Employment 3.09 0.85 3.32 0.85 1947-1/2016-2
+## Unemployment-rate 1.44 -0.81 1.72 -0.79 1948-1/2016-2
+## GDP-Deflator 2.99 0.04 4.11 -0.13 1947-1/2016-1
+## S&P500 21.80 0.41 22.08 0.38 1950-1/2016-2
+## 10-year-Treasury-yield 1.46 -0.05 1.51 0.08 1953-2/2016-2
+## Fedfunds-rate 2.78 0.33 3.03 0.40 1954-3/2016-2
I’ll replicate the table above, combining base R functions with estimates of the yth_filter
function.
Per the usual protocol when approaching such a problem, the first step is to combine data in manner that allows for convenient iteration of computations across all data sets. First, I merge
series which already have a quarterly frequency. These are GDPC1, PCECC96, GPDIC1, EXPGSC1, IMPGSC1, GCEC1, GDPDEF
. At this step, we can also subset observations by the date range used by Hamilton. As all series of which units are measured in prices need to be given the \(100*log\) treatment, I add that to this step as well.
quarterly_data <- 100*log(merge(GDPC1, PCECC96, GPDIC1, EXPGSC1, IMPGSC1, GCEC1, GDPDEF)["1947/2016-3"])
Some of the series we wish to compare have a monthly periodicity, so we need to lower their frequency to.quarterly
. First, merge
monthly series and \(100*log\) those expressed in prices. Leave those expressed in percentages alone. Then, functionally iterate over every series and transform them to.quarterly
. Presumably because more data was available at the time of Hamilton’s work, monthly series include observations from the second quarter of 2016 and so I subset accordingly. Finally, all series are combined into one xts
object, quarterly_data
.
monthly_data <- merge(100*log(PAYEMS), 100*log(SP500$SP500)["1950/"], UNRATENSA, GS10, FEDFUNDS)
-
-to_quarterly_data <- do.call(merge, lapply(monthly_data, to.quarterly, OHLC = FALSE))["1947/2016-6"]
-
-quarterly_data <- merge(quarterly_data, to_quarterly_data)
Now that the data has been prepped, its time to functionally iterate over each series, lapply
ing the yth_filter
to all. The optional argument of output = "cycle"
comes in handy because it returns the labeled univariate cycle component for each series. The same can be done for the random
component as well.
cycle <- do.call(merge, lapply(quarterly_data, yth_filter, output = "cycle"))
-
-random <- do.call(merge, lapply(quarterly_data, yth_filter, output = "random"))
Now that all data have been transformed into both cycle and random components, its time to estimate the standard deviation for each, as well as each components correlation with GDP. This is also a good opportunity to t
ranspose each of our estimates into vertical columned data.frames
, matching Hamilton’s format.
cycle.sd <- t(data.frame(lapply(cycle, sd, na.rm = TRUE)))
-GDP.cor <- t(data.frame(lapply(cycle, cor, cycle[,1], use = "complete.obs")))
-random.sd <- t(data.frame(lapply(random, sd, na.rm = TRUE)))
-random.cor <- t(data.frame(lapply(random, cor, random[,1], use = "complete.obs")))
-
-my_table_2 <- round(data.frame(cbind(cycle.sd, GDP.cor, random.sd, random.cor)), 2)
Hamilton displays the date ranges of his samples so we will do the same.
-I use a simple function I call sample_range
to extract the first and last observation of each series’ index.xts
. This approach serves as a check on the work, as oppose to manually creating labels.
Sample ranges are then t
ransposed into vertical data.frames
and cbind
’d to the existing table of estimates.
sample_range <- function(x) {
- x <- na.omit(x)
- gsub(" ", "-", paste0(index(x[1,]), "/", index(x[NROW(x),])))
-}
-
-data_sample <- t(data.frame(lapply(quarterly_data, sample_range)))
-
-my_table_2 <- cbind(my_table_2, data_sample)
-names(my_table_2) <- names(Hamilton_table_2)
Finally, rbind
Hamilton’s table 2 with my table and compare. The results are nearly identical, inspiring confidence in the replication of this approach.
According to the ‘code and data’ link on the ‘Current Working Papers’ page of Hamilton’s site, both Matlab and RATS were used for computation of the table. It is not surprising that minor differences in estimates would occur, likely due to differences in the underlying data or internal computational choices made by each commercial software product.
- # Combined table
-combined_table <- rbind(Hamilton_table_2[-NROW(Hamilton_table_2),], my_table_2)
-combined_table <- combined_table[order(combined_table$cycle.sd),]
-kable(combined_table, align = 'l', caption = "Hamilton's table 2 compared with estimates from neverhpfilter::yth_filter, sorted by standard deviation of the cycle component. yth_filter estimates are labeled with the suffix '.cycle'")
- | cycle.sd | -gdp.cor | -random.sd | -gdp.rand.cor | -Sample | -
---|---|---|---|---|---|
Unemployment-rate | -1.44 | --0.81 | -1.72 | --0.79 | -1948-1/2016-2 | -
UNRATENSA.cycle | -1.44 | --0.82 | -1.71 | --0.80 | -1948-Q1/2016-Q2 | -
10-year-Treasury-yield | -1.46 | --0.05 | -1.51 | -0.08 | -1953-2/2016-2 | -
GS10.cycle | -1.46 | --0.05 | -1.51 | -0.08 | -1953-Q2/2016-Q2 | -
Fedfunds-rate | -2.78 | -0.33 | -3.03 | -0.40 | -1954-3/2016-2 | -
FEDFUNDS.cycle | -2.78 | -0.33 | -3.03 | -0.41 | -1954-Q3/2016-Q2 | -
PCECC96.cycle | -2.84 | -0.79 | -3.02 | -0.82 | -1947-Q1/2016-Q1 | -
Consumption | -2.85 | -0.79 | -3.04 | -0.82 | -1947-1/2016-1 | -
GDP-Deflator | -2.99 | -0.04 | -4.11 | --0.13 | -1947-1/2016-1 | -
GDPDEF.cycle | -3.00 | -0.03 | -4.12 | --0.14 | -1947-Q1/2016-Q1 | -
Employment | -3.09 | -0.85 | -3.32 | -0.85 | -1947-1/2016-2 | -
PAYEMS.cycle | -3.09 | -0.85 | -3.32 | -0.85 | -1947-Q1/2016-Q2 | -
GDPC1.cycle | -3.37 | -1.00 | -3.65 | -1.00 | -1947-Q1/2016-Q1 | -
GDP | -3.38 | -1.00 | -3.69 | -1.00 | -1947-1/2016-1 | -
Government-spending | -7.13 | -0.31 | -8.60 | -0.38 | -1947-1/2016-1 | -
GCEC1.cycle | -7.16 | -0.31 | -8.59 | -0.38 | -1947-Q1/2016-Q1 | -
IMPGSC1.cycle | -9.75 | -0.76 | -9.91 | -0.75 | -1947-Q1/2016-Q1 | -
Imports | -9.79 | -0.77 | -9.98 | -0.75 | -1947-1/2016-1 | -
EXPGSC1.cycle | -10.75 | -0.33 | -11.32 | -0.30 | -1947-Q1/2016-Q1 | -
Exports | -10.77 | -0.33 | -11.33 | -0.30 | -1947-1/2016-1 | -
GPDIC1.cycle | -13.14 | -0.83 | -13.65 | -0.78 | -1947-Q1/2016-Q1 | -
Investment | -13.19 | -0.84 | -13.74 | -0.80 | -1947-1/2016-1 | -
SP500.cycle | -21.38 | -0.42 | -21.60 | -0.40 | -1950-Q1/2016-Q2 | -
S&P500 | -21.80 | -0.41 | -22.08 | -0.38 | -1950-1/2016-2 | -
The first step is to combine our economic time series into an object that allows for convenient iteration of computations across all data of interest. First, merge
all series of quarterly frequency. These are GDPC1, PCECC96, GPDIC1, EXPGSC1, IMPGSC1, GCEC1, GDPDEF
. At this point, subset observations by the precise date range used by Hamilton. At some point, all series which are measured in prices need to be given the \(100*log\) treatment, so do this now.
quarterly_data <- 100*log(merge(GDPC1, PCECC96, GPDIC1, EXPGSC1, IMPGSC1, GCEC1, GDPDEF)["1947/2016-3"])
Some economic time series we wish to compare are measured in monthly periodicity, so we need to lower their frequency to.quarterly
. merge
monthly series and \(100*log\) those expressed in prices. Leave data measured in percentages be. Then, functionally iterate over every series and transform them to.quarterly
. Finally, all series are combined into one xts
object, I call quarterly_data
.
monthly_data <- merge(100*log(PAYEMS), 100*log(SP500$SP500)["1950/"], UNRATENSA, GS10, FEDFUNDS)
+
+to_quarterly_data <- do.call(merge, lapply(monthly_data, to.quarterly, OHLC = FALSE))["1947/2016-6"]
+
+quarterly_data <- merge(quarterly_data, to_quarterly_data)
Now its time to functionally iterate over each series. I do this by lapply
ing the yth_filter
to each series, while iteratively merge
ing results into one object with do.call
. The optional argument of output = "cycle"
is convenient here as it returns the labeled univariate cycle component for each series. For example, GDPCE1.cycle
. The same approach is use to compute the random
component for each series as well.
cycle <- do.call(merge, lapply(quarterly_data, yth_filter, output = "cycle"))
+
+random <- do.call(merge, lapply(quarterly_data, yth_filter, output = "random"))
Now that all data have been transformed into both cycle
and random
series, its time to estimate the standard deviation for each, as well as each components correlation with GDP
. This is also a good opportunity to t
ranspose each of our estimates into vertical columned data.frames
, matching Hamilton’s format.
cycle.sd <- t(data.frame(lapply(cycle, sd, na.rm = TRUE)))
+GDP.cor <- t(data.frame(lapply(cycle, cor, cycle[,1], use = "complete.obs")))
+random.sd <- t(data.frame(lapply(random, sd, na.rm = TRUE)))
+random.cor <- t(data.frame(lapply(random, cor, random[,1], use = "complete.obs")))
+
+my_table_2 <- round(data.frame(cbind(cycle.sd, GDP.cor, random.sd, random.cor)), 2)
Hamilton displays the date ranges of his samples, so we will do the same, while keeping the xts
date range syntax format. I use a simple function I call sample_range
to extract the first and last observation of each series’ index.xts
. This approach serves as a check on the work, as oppose to manually creating labels. Sample ranges are then t
ransposed into vertical data.frames
and cbind
’d to the existing table of estimates.
sample_range <- function(x) {
+ x <- na.omit(x)
+ gsub(" ", "-", paste0(index(x[1,]), "/", index(x[NROW(x),])))
+}
+
+data_sample <- t(data.frame(lapply(quarterly_data, sample_range)))
+
+my_table_2 <- cbind(my_table_2, data_sample)
+names(my_table_2) <- names(Hamilton_table_2)
Finally, rbind
Hamilton’s table 2 with my table for a visual comparison. The results are nearly identical, inspiring confidence in the replication of the approach, as the functions of the neverhpfilter
package.
According to the ‘code and data’ link on the ‘Current Working Papers’ page of Hamilton’s site, both Matlab and RATS were used for computation of the table. It is not surprising that minor differences in estimates would occur, likely due to differences in the underlying data or internal computational choices made by each commercial software product. While economic time series are publicly available and have a central source at FRED, that is not so for Standard & Poor’s index data. Unsurprisingly, the SP500
data shows the most divergence, and it is not clear what source was used in the original paper (though I have my suspicions for future exploration).
Below, see Hamilton’s table 2 compared with estimates from neverhpfilter::yth_filter, sorted by standard deviation of the cycle component. yth_filter estimates are labeled with the suffix .cycle
# Combined table
+combined_table <- rbind(Hamilton_table_2[-NROW(Hamilton_table_2),], my_table_2)
+
+combined_table[order(combined_table$cycle.sd),]
## cycle.sd gdp.cor random.sd gdp.rand.cor Sample
+## Unemployment-rate 1.44 -0.81 1.72 -0.79 1948-1/2016-2
+## UNRATENSA.cycle 1.44 -0.82 1.71 -0.80 1948-Q1/2016-Q2
+## 10-year-Treasury-yield 1.46 -0.05 1.51 0.08 1953-2/2016-2
+## GS10.cycle 1.46 -0.05 1.51 0.08 1953-Q2/2016-Q2
+## Fedfunds-rate 2.78 0.33 3.03 0.40 1954-3/2016-2
+## FEDFUNDS.cycle 2.78 0.33 3.03 0.41 1954-Q3/2016-Q2
+## PCECC96.cycle 2.84 0.79 3.02 0.82 1947-Q1/2016-Q1
+## Consumption 2.85 0.79 3.04 0.82 1947-1/2016-1
+## GDP-Deflator 2.99 0.04 4.11 -0.13 1947-1/2016-1
+## GDPDEF.cycle 3.00 0.03 4.12 -0.14 1947-Q1/2016-Q1
+## Employment 3.09 0.85 3.32 0.85 1947-1/2016-2
+## PAYEMS.cycle 3.09 0.85 3.32 0.85 1947-Q1/2016-Q2
+## GDPC1.cycle 3.37 1.00 3.65 1.00 1947-Q1/2016-Q1
+## GDP 3.38 1.00 3.69 1.00 1947-1/2016-1
+## Government-spending 7.13 0.31 8.60 0.38 1947-1/2016-1
+## GCEC1.cycle 7.16 0.31 8.59 0.38 1947-Q1/2016-Q1
+## IMPGSC1.cycle 9.75 0.76 9.91 0.75 1947-Q1/2016-Q1
+## Imports 9.79 0.77 9.98 0.75 1947-1/2016-1
+## EXPGSC1.cycle 10.75 0.33 11.32 0.30 1947-Q1/2016-Q1
+## Exports 10.77 0.33 11.33 0.30 1947-1/2016-1
+## GPDIC1.cycle 13.14 0.83 13.65 0.78 1947-Q1/2016-Q1
+## Investment 13.19 0.84 13.74 0.80 1947-1/2016-1
+## SP500.cycle 21.38 0.42 21.60 0.40 1950-Q1/2016-Q2
+## S&P500 21.80 0.41 22.08 0.38 1950-1/2016-2
The estimates generated with the neverhpfilter
package are nearly identical to those displayed by Hamilton(2017). If one has the use case, the generalized functions will estimate higher frequency time series as well as error distributions other than Gaussian. In addition to consulting the paper which inspired this package, check out the documentation for yth_filter
to learn more.
The estimates generated with the neverhpfilter
package are nearly identical to those displayed by Hamilton (2017). If one has the use case, the generalized functions which inherit methods from glm
and xts
will estimate higher frequency time series as well as error distributions other than Gaussian. In addition to consulting the paper which inspired this package, check out the documentation for yth_filter
to learn more, or reach out to me with any questions.
Install from CRAN on R version >= 3.5.0.
install.packages("neverhpfilter")
Or install from the Github master branch on R version >= 3.5.0.
-devtools::install_github("JustinMShea/neverhpfilter")
+devtools::install_github("JustinMShea/neverhpfilter")
Load the package
library(neverhpfilter)
diff --git a/docs/news/index.html b/docs/news/index.html
index aefa734..2cc63b3 100644
--- a/docs/news/index.html
+++ b/docs/news/index.html
@@ -67,7 +67,7 @@
@@ -120,13 +120,24 @@ Content edits and cleanup of vignettes.
+These included, removing the redundant call to library(xts)
as it has been moved to Depends
instead of merely Suggests
, as documented in 0.3-0 below. Thus, calling neverhpfilter
includes it.
While the vignette builder uses the knitr
package, I was also loading the knitr
package to access the kable
function for tables. Testing was going fine, but then knitr
inexplicably began throwing a variety of differing errors across Linux and Windows builds. This appears to be due to Suggested packages it couldn’t import, so removing calls to knitr
in the vignette was an easy place to begin reducing the area of an unknown attack surface. In the modern era, regardless of the original error, any opportunity to reduce dependencies seems the most sensible approach as ever increasing dependency sprawl has bestowed upon R package maintainers a constant, exponentially growing, attack surface.
The decision to remove knitr::kable
from vignettes was also an aesthetic one. In my experience, tables remain an important device for graphic displays of information. While knitr’s html format appears clean at first, closer inspection reveals the undesirable trait of fitting tables to full page width regardless of the number of columns to display. On deeper reflection, I view this as a bug, as it produces the undesirable side effect of too much white space for the reader’s eye to traverse when comparing numbers across columns.
Printing the raw output of an xts
or data.frame
objects keeps columns compact, allowing for clearer visual comparison. The raw output also better communicates to our reader the table was created as a result of some computational process. Plus, in an increasingly sophisticated digital world of Ux, these raw outputs look increasingly, unique, computationally cool, and clean. They serve as a reminder of the objective and scientific nature we strive for in our endeavors.
Feature, updated data through January 2020.
+New vignette Getting started
reworks and replaces Additional examples
.
Increased R version dependency to (>= 3.5.0) for the .Rdata
files.
Moved from testtthat
to tinytest
, and added addy function unit tests and data tests.
Moved from testtthat
to tinytest
, and wrote additional function unit tests and data unit tests.
Moved xts
and zoo
from imports to depends. Now xts
(>= 0.11-0) and zoo
(>= 1.8-0)
Bug fix, see issue-1 here.
Consolidated into two functions. yth_glm
remains unchanged, while yth_filter
has been given an output
argument to specify the return of specific series. This feature eliminates the need for yth_cycle
and yth_trend
, which were helpful when applying the function to multiple data sets. Done so at the strong suggestion of CRAN
.
Consolidated into two functions. yth_glm
remains unchanged, while yth_filter
has been given an output
argument to specify the return of specific series. This feature eliminates the need for yth_cycle
and yth_trend
, which were helpful when applying the function to multiple data sets. Done so at the strong suggestion of CRAN
, and has ultimatly proven a good idea.
Additional data sets have been added to replicate most all of Hamilton’s table 2.
The “Reproducing Hamilton” vignette has been expanded and content has been edited for clarity.
@@ -156,6 +167,7 @@