diff --git a/Project.toml b/Project.toml
index 0e29e35..6ea3b7d 100644
--- a/Project.toml
+++ b/Project.toml
@@ -5,6 +5,7 @@ version = "0.1.5"
[deps]
Dierckx = "39dd38d3-220a-591b-8e3c-4c3a8c710a94"
+LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[compat]
Dierckx = "0.5"
diff --git a/README.md b/README.md
index 3b4a1ad..27e057c 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@ The package Smoothers provides a collection of smoothing heuristics, models and
* Locally Estimated Scatterplot Smoothing (**loess**)
* Seasonal and Trend decomposition based on Loess (**stl**)
* Simple Moving Average (**sma**)
+* Hodrick-Prescott filter (**hpfilter**) for trend and cyclical components.
@@ -37,6 +38,9 @@ plot!(t,filter(b,a,x), label ="filter(1,[1/"*sw*",...],x)")
# Simple Moving Average
plot!(t, sma(x,w,true), label = "sma(x,"*sw*",true)")
+# Hodrick-Prescott filter
+plot!(t, hpfilter(x, 1600), label = "hpfilter(x, 1600)")
+
```
diff --git a/docs/src/images/smoothers_examples.png b/docs/src/images/smoothers_examples.png
index 3ff67e2..8d03774 100644
Binary files a/docs/src/images/smoothers_examples.png and b/docs/src/images/smoothers_examples.png differ
diff --git a/src/Smoothers.jl b/src/Smoothers.jl
index 7d8bf79..1f3d741 100644
--- a/src/Smoothers.jl
+++ b/src/Smoothers.jl
@@ -2,8 +2,9 @@ module Smoothers
using Dierckx
import Base: filter
+using LinearAlgebra: diagm, I
-export filter, hma, loess, sma, stl
+export filter, hma, loess, sma, stl, hpfilter
# Methods
include("filter.jl")
@@ -11,6 +12,7 @@ include("hma.jl")
include("loess.jl")
include("sma.jl")
include("stl.jl")
+include("hpfilter.jl")
"""
Collection of smoothing heuristics, models and smoothing related applications. The current available smoothers and applications are:
@@ -20,6 +22,7 @@ Collection of smoothing heuristics, models and smoothing related applications. T
`loess`: Locally Estimated Scatterplot Smoothing
`sma`: Simple Moving Average
`stl`: Seasonal and Trend decomposition based on Loess
+ `hpfilter`: Hodrick-Prescott trend decomposition.
"""
Smoothers
diff --git a/src/hpfilter.jl b/src/hpfilter.jl
new file mode 100644
index 0000000..0624d3e
--- /dev/null
+++ b/src/hpfilter.jl
@@ -0,0 +1,91 @@
+"""
+Package: Smoothers
+
+ hpfilter(y, lambda=1600)
+
+Hodrick-Prescott filter vector `y` using smoothing parameter `lambda`. The
+parameter `lambda` adjusts the sensibility of the trend to short-term
+fluctuations. Higher values of `lambda` give more smoothing. Hodrick and
+Prescott suggest `lambda=1600` for quarterly data.
+- `lambda=0` gives no smoothing, so it returns `y`.
+- `lambda`→∞ gives a linear trend.
+
+# Arguments
+- `y`: Vector of data.
+- `lambda`: Smoothing parameter.
+
+# Returns
+Vector with the trend component of `y`.
+
+# Examples
+```julia-repl
+julia> x = 0:0.2:2pi
+0.0:0.2:6.2
+
+julia> y = sin.(x) + 0.1*randn(length(x))
+32-element Vector{Float64}:
+ 0.09200627969509063
+ 0.24164024275732685
+ 0.49994368543454165
+ 0.5122933360879012
+ 0.6699190280838954
+ 0.9630579533383277
+ ⋮
+ -0.7908099824097626
+ -0.5986641704672869
+ -0.6482750454579537
+ -0.2963091814085014
+ 0.050926679466235095
+
+julia> ytrend = hpfilter(y)
+32-element Vector{Float64}:
+ 0.8054238538000016
+ 0.7828566743521803
+ 0.7598436089205433
+ 0.7356005112515279
+ 0.7091807976393933
+ 0.6794983173939206
+ ⋮
+ -0.7006212315020678
+ -0.7350501835373815
+ -0.7671301582815108
+ -0.7977981479965659
+ -0.8279168604991427
+```
+"""
+function hpfilter(y, lambda=1600)
+
+ lambda == 0 && (return y)
+ T = length(y)
+ R = eltype(y)
+
+ # Center part of the matrix
+ A_c = 6*I(T-4) +
+ diagm(1 => repeat([-4], T-5)) +
+ diagm(-1 => repeat([-4], T-5)) +
+ diagm(2 => repeat([1], T-6)) +
+ diagm(-2 => repeat([1], T-6))
+
+ # First and last rows
+ r1 = [1,-2, 1, zeros(R, T-3)...]
+ r2 = [-2, 5, -4, 1, zeros(R, T-4)...]
+
+ A = zeros(R, T, T)
+ # first two rows/cols
+ A[1, :] = r1
+ A[:, 1] = r1'
+ A[2, :] = r2
+ A[:, 2] = r2'
+
+ # center
+ A[3:end-2, 3:end-2] = A_c
+ # last two rows/cols
+ A[end-1, end:-1:1] = r2
+ A[end:-1:1, end-1] = r2'
+ A[end, end:-1:1] = r1
+ A[end:-1:1, end] = r1'
+
+ # Smoothed version
+ y_s = (I(T) + lambda*A)\y
+ y_s
+end
\ No newline at end of file
diff --git a/test/hpfilter.jl b/test/hpfilter.jl
new file mode 100644
index 0000000..3d7755b
--- /dev/null
+++ b/test/hpfilter.jl
@@ -0,0 +1,24 @@
+using Test
+using Smoothers
+
+@testset "hpfilter" begin
+
+ # Input test data
+ x = [1579.1, 1577.7, 1576.9, 1600.4, 1626.4, 1655.5, 1665.1, 1669.0, 1643.8, 1638.6]
+
+ # Computed HP filter from another source with lambda=1600
+ x_hp_trend = [1577.18535023776, 1587.47395687673, 1597.7637601718, 1608.04984805602, 1618.31426861234, 1628.53428876867, 1638.69222903504, 1648.78726349099, 1658.83507107293, 1668.86396367757]
+ # Compute HP filter with package function
+ x_trend = hpfilter(x, 1600)
+ # Are entries approximate?
+ @test all(isapprox.(x_trend, x_hp_trend; atol=1e-8))
+
+ # Test with different data precision
+ x_f32 = Float32.(x)
+ x_trend_f32 = hpfilter(x_f32, 1600)
+ @test eltype(x_trend_f32) == Float32
+
+ # Test with lambda=0
+ @test x == hpfilter(x, 0)
+
+end
\ No newline at end of file
diff --git a/test/runtests.jl b/test/runtests.jl
index b44952a..07fd162 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -9,6 +9,7 @@ const tests = [
"loess",
"sma",
"stl",
+ "hpfilter",
]
printstyled("\nTest Summary List:\n", color=:underline)