Extracting Business Cycles From Raw Data in R

Some sort of introduction

The analysis of business cycles requires the extraction of the cyclical component of a times series that is usually also influenced by further factors such as an underlying trend or noise. This post presents some methods that are used in the recent literature to extract business cycles from a given series. It is based on the chapter on business cycles by Stock and Watson (1999) in the Handbook of Macroeconomics. I also introduce relatively new methods, like wavelet filters or empirical mode decomposition which are not covered in the handbook. Since the focus of this post is on the implementation of certain filter techniques in R, I will not cover the math. Rather, I will refer to the respective literature. For the examples quarterly data on US real GDP is used, which I obtained directly from FRED.

# Download the data via Quanld and transform it
gdp <- Quandl("FRED/GDPC1",order="asc") # Download data via Quandl
names(gdp) <- c("Time","GDP") # Rename variables
gdp[,"GDP"] <- log(gdp[,"GDP"]) # Take logs

To get an intuition about what it means to extract the cyclical component of a time series, look at the development of log real GDP over time in the following figure.

ggplot(gdp,aes(x=Time,y=GDP)) + geom_line(size=.5) + theme_classic() + labs(title="Log Real US GDP")

There is a clear increasing trend in the data, which appears to become gradually smaller up to the present. Additionally, the series seems to fluctuate around this trend in a more or less regular manner. There are very small deviations of the series from the trend which occur very frequently, but also rather large deviations which can persist over several subsequent periods. The latter are the fluctuations that are relevant for business cycle analysis.

Time detrending

A first approach to exclude a trend from a series is to regress the variable of interest on a time variable and to obtain the residual values. These are plotted in the following figure, where the linear trend was removed.

time.detrend <- residuals(lm(GDP ~ Time, data=gdp)) # Regress GDP on Time and obtain residuals

# Plot
dat <- data.frame("Time"=gdp[,"Time"],"Linearly.Detrended"=time.detrend)
ggplot(dat,aes(x=Time,y=Linearly.Detrended)) + geom_hline(yintercept=0,colour="grey80") + geom_line(size=.5) + theme_classic() + labs(title="Linearly Detrended",y="")

This approach is relatively controversial, since it assumes that there is a constant, linear time trend. As we have seen above, this is not very likely given the steady decrease of the trend’s growth rate over time. However, it is still possible to assume a different functional form of the time trend, e.g. adding a quadratic term, to get rid of the trend. A further disadvantage of this method is that it does only exlude the trend, but not the noise, i.e. the very small fluctuations in the series.

First difference

The next approach is to take first differences as it is commonly taught to obtain stationary time series. This assumes that the data is difference stationary. The result of taking first differences is shown in the following figure, where it is also compared to the time detrended series. The differenced data fluctuates much more around the zero line, but it also contains much noise as well.

fd <- diff(gdp[,"GDP"]) # Take first difference

# Plot
dat <- data.frame("Time"=dat[,"Time"],"First.Difference"=c(NA,fd),"Linearly.Detrended"=dat[,"Linearly.Detrended"])
g <- melt(dat,id.vars="Time",na.rm=TRUE)
levels(g[,2]) <- c("Linear Trend","First Difference")

# Define plot function
plot.cycles <- function(d,t) {
ggplot(g,aes(x=Time,y=value,linetype=variable)) +
geom_hline(yintercept=0,colour="grey80") + # Create a horizontal line with value 0
geom_line(size=.5) + # Create line with series and specify its thickness
labs(x="Time",y="",title=t,linetype="Legend:") + # Title of the legend
coord_cartesian(xlim=c(min(g[,1]),max(g[,1])),expand=FALSE) + # Set displayed area
guides(linetype=guide_legend()) + # Set the variables contained in the legend
theme(legend.position="bottom", # Position of the legend
legend.key=element_rect(fill="white"), # Set background of the legend keys
panel.background = element_rect(fill = "white"), # Set background of the graph
axis.line=element_line(size=.3,colour="black"), # Set the size and colour of the axes
axis.text=element_text(colour="black"), # Set the colour of the axes text
panel.grid=element_blank()) # Set grid lines off

# Plot
plot.cycles(d=g,t="Linearly Detrended vs. First Difference")

Hodrick Prescott filter

Hodrick and Prescott (1981) developed a filter, which seprates a time series into a trend, cyclical and noise component. The hpfilter function is contained in the mFilter package and requires the time series and a smoothing parameter. The literature suggest a value of 1600 for the latter. However, it is possible to choose a much higher value as well. The follwoing figure plots the values of the cyclical component of real GDP obtained by the Hodrick-Prescott filter and compares it with the values of a linearly detrended series. The behaviour of both series appear quite similar, except that the HP-series fluctuates more around zero, whereas the linearly detrended series still includes components of the trend. Additionally, the cyclical HP series still includes some noise-like components.

hp <- hpfilter(gdp[,2],freq=1600)$cycle # Apply filter and obtain data of the cycle compontent

# Plot
dat <- cbind(dat,data.frame("Hodrick.Prescott"=hp))
g <- melt(dat[,c(1,4,3)],id.vars="Time",na.rm=TRUE)
levels(g[,2]) <- c("Hodrick Prescott","Linearly Detrended")

plot.cycles(g,"Hodrick Prescott vs. Linearly Detrended")

Although widely used in economics, the HP filter is also heavily criticised for some features. See, for example, a good overview in a Bruegel blog post by Jérémie Cohen-Setton and Yury Yatsynovich and here for a recent (bit technical) critique by James Hamilton.

Baxter King filter

Baxter and King (1994,1999) proposed a filter which yields similar results as the HP filter, but which takes out a lot of that noise-like behaviour shown above. The function bkfilter is also contained in the mFilter package. It requires the series, a lower and an upper bound of the amount of periods, where cycles are assumed to occur (pl and pu), and a smoothing factor nfix. The literature (cf. NBER, Stock and Watson (1999)) suggests that business cycles last from 6 to 32 months. These values were used to specify the lower and upper bound of the cycle periodicity. The results of the BK filter are shown in the following figure. A relatively series drawback of this method is that the smoothing factor leads to the loss of observations at the beginning and the end of the series. This might be a problem with small samples.

bk <- bkfilter(gdp[,2],pl=6,pu=32,nfix=12)$cycle[,1] # Apply filter and obtain cycle component

# Plot
dat <- cbind(dat,data.frame("Baxter.King"=bk))
g <- melt(dat[,c(1,5,4)],id.vars="Time",na.rm=TRUE)
levels(g[,2]) <- c("Baxter King","Hodrick Prescott")

plot.cycles(g,"Baxter King vs. Hodrick Prescott")

Wavelet filter

Yogo (2008) proposed to use wavelet filters to extract business cycles from time series data. The advantage of this method is that the function does not only allow to extract the trend, cycle and noise of a series, but also to become more specific about the periods within which cycles occur. However, there is not full freedom in that, since the technique can only capture periodicities of a power of two, i.e. 2,4,8,16,32 and so on.

The methods implementation in R is also neat, but requires some additional data transformation before it can be used. A useful function is contained in the waveslim package and is called mra (“multiresolution analysis”). It requires a differenced version of the time series and the depth of the decomposition.

The function gives multiple series, which have to be cumulated with cumsum to translate them back into data that reflects cyclical patterns. Further, some series can be combined with rowSums. This is useful when the cycles that last 8 to 16 and 16 to 32 periods should be analysed together as it is done in the following figure. Not surprisingly, the wavelet filter yields a similar result as the BK filter, since the upper bound of cycle periods is equal in both and the lower bound differs only by two.


wavelet <- as.data.frame(mra(diff(gdp[,2]),J=5)) # Apply filter

# Plot
dat <- cbind(dat,data.frame("Wavelet"=c(NA,cumsum(rowSums(wavelet[,3:4])))))
g <- melt(dat[,c(1,6,5)],id.vars="Time",na.rm=TRUE)
levels(g[,2]) <- c("Wavelet","Baxter King")

plot.cycles(g,"Wavelet vs. Baxter King")

Empirical mode decomposition (EMD)

Based on Huang et al. (1998) Kozic and Sever (2014) propose empirical mode decomposition as a further method of business cycle extraction. The function emd can be found in the EMD package and requires a differenced time series, a boundary condition and a rule that specifies at which point the iterative process has achieved a sufficiently satisfying result and can stop. The results of this filter method are relatively different from the HP, BK and wavelet filter. It is the task of every researching to assess whether working with this method is justified or not.

emd <- as.data.frame(emd(xt=diff(gdp[,2]),boundary="wave",stoprule="type2")$imf)

dat <- cbind(dat,data.frame("EMD"=c(NA,cumsum(rowSums(emd[,3:6])))))
g <- melt(dat[,c(1,7,4)],id.vars="Time",na.rm=TRUE)
levels(g[,2]) <- c("EMD","Hodrick Prescott")

plot.cycles(g,"EMD vs. Hodrick Prescott")


Baxter, M., & King, R. O. (1999). Measuring Business Cycles: Approximate Band-Pass Filters for Economic Time Series. Review of Economics & Statistics, 81(4), 575–593.

Kožić, I., & Sever, I. (2014). Measuring business cycles: Empirical Mode Decomposition of economic time series. Economics Letters, 123(3), 287–290. doi:10.1016/j.econlet.2014.03.009

Stock, J. H., & Watson, M. W. (1999). Business Cycle Fluctuations in US Macroeconomic Time Series. In J. B. Taylor & M. Woodford (Eds.), Handbook of Macroeconomics. Handbook of Macroeconomics (pp. 3–64). Elsevier.

Yogo, M. (2008). Measuring business cycles: A wavelet analysis of economic time series. Economics Letters, 100(2), 208–212. doi:10.1016/j.econlet.2008.01.008



      1. Hello! Thanks for your comment! It seems that the homepage of the Fed was updated and it’s not possible to download the data in the way I was used to. Now, I use the Quandl-Package to download the series.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s