Double exponential smoothing a.k.a. Holt-Winters smoothing tracks level and trend of a time series in coupled IIRs:
$\qquad$ In: $Y_t$, t = 0 1 2 ...
$\qquad$ State: $L_t, T_t \quad$ -- level and trend
$\qquad$ Out: estimate $\hat{Y}_{t+1}$
$\qquad$ Parameters: a, b (a.k.a. $\alpha, \beta$)
$\qquad$ Step equations:
$\qquad$ $\qquad L_t = a Y_t + (1 - a) (L_{t-1} + T_{t-1}) \quad$ -- level
$\qquad$ $\qquad T_t = b (L_t - L_{t-1}) + (1 - b) T_{t-1} \quad$ -- trend
$\qquad$ $\qquad \hat{Y}_{t+1} = L_t + T_t$

How can I calculate the transfer function of this smoother, for given $a$ and $b$ ?

(Two possible approaches:

  • manipulate the step equations into ratio-of-polynomials form
  • brute force iterate with input $e^{2 \pi i f t}$: converges slowly for small $a, b$.

The first of these is beyond me, the second unsatisfactory.)


2 Answers 2


I would use the $\mathcal{Z}$-transform. Let me change the notation slightly by using $n$ as the discrete time index, lower case letters for time domain quantities, $x[n]$ as the observed input data sequence, and $y[n]$ as the output sequence, which is an estimate of $x[n+1]$. With this notation, the time domain equations are

$$\tag{1}\begin{align} l[n]&=ax[n]+(1-a)(l[n-1]+t[n-1])\\ t[n]&=b(l[n]-l[n-1])+(1-b)t[n-1]\\ y[n]&=l[n]+t[n] \end{align}$$

Taking the $\mathcal{Z}$-transform of these 3 equations gives

$$\begin{align}\tag{2} L(z)&=aX(z)+(1-a)z^{-1}(L(z)+T(z))\\ T(z)&=b(1-z^{-1})L(z)+(1-b)z^{-1}T(z)\\ Y(z)&=L(z)+T(z) \end{align}$$

From the second equation in (2) we can express $T(z)$ in terms of $L(z)$:


Plugging (3) into the first equation of (2), we can express $L(z)$ in terms of $X(z)$:


from which, after some algebra, you get


Finally, plugging (3) into the last equation of (2) gives


and combining with (4) results in a relation between the output $Y(z)$ and the input $X(z)$:


where $H(z)$ is the desired transfer function, which is of course a second order IIR filter.

  • $\begingroup$ Thanks ! Fwiw, an example using your answer in Python / numpy / scipy.signal is under gist.github.com/denis-bz . $\endgroup$
    – denis
    Apr 8, 2015 at 15:02
  • $\begingroup$ @denis: OK, great, hope it works as expected. $\endgroup$
    – Matt L.
    Apr 8, 2015 at 15:34

I can't leave a comment, but does denis' scipy.signal sample still exist? The impulse response of my attempt diverges:

from scipy import signal
import matplotlib.pyplot as plt

alpha = 0.4
beta = 0.01
system = (
          (alpha*(1+beta), -alpha),  # Numerator
          (1, alpha*(1+beta)-2, 1-alpha)  # Demoninator

t, y = signal.impulse2(system)
plt.plot(t, y)
  • $\begingroup$ I think that signal.impulse2 takes a continuous-time system as an input (which would be unstable with the given numerator). However, you're dealing with a discrete-time system. You can probably use signal.dimpulse. $\endgroup$
    – Matt L.
    Oct 9, 2019 at 22:07
  • $\begingroup$ It's back under gist.github.com/denis-bz; hope it helps $\endgroup$
    – denis
    Oct 10, 2019 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.