1

Given some data x:

from pandas_datareader.data import DataReader as dr
x = np.squeeze(dr('DTWEXB', 'fred').dropna().values)

I want to calculate another vector y as follows:

enter image description here

Where alpha equals 0.03, in this case.

Can I do this with scipy.lfilter?. Similar question here, but in that case the starting value of the result is 0 and that is throwing something off.

My attempt:

from scipy.signal import lfilter
a = 0.03
b = 1 - a
y0 = x[0]
y = lfilter([a], [y0, -b], x)

The results should be:

true_y = np.empty(len(x))
for k in range(0, len(true_y)):
    if k == 0:
        true_y[k] = x[0]
    else:
        true_y[k] = a*x[k] + b*true_y[k-1]
print(true_y)
[ 101.1818      101.176862    101.16819314 ...,  120.9813121   120.92484874
  120.85786628]
Brad Solomon
  • 38,521
  • 31
  • 149
  • 235

2 Answers2

3

The correct arguments for the coefficients of the transfer function are [a] and [1, -b].

To handle your desired initial condition, you can create the correct initial state for the filter using scipy.signal.lfiltic:

zi = lfiltic([a], [1, -b], y=[x[0]])

Then call lfilter with the zi argument:

y, zo = lfilter([a], [1, -b], x, zi=zi)

Here are an x, y (computed using lfilter with zi), and your true_y:

In [37]: x
Out[37]: array([ 3.,  1.,  2.,  0., -1.,  2.])

In [38]: y
Out[38]: 
array([ 3.        ,  2.94      ,  2.9118    ,  2.824446  ,  2.70971262,
        2.68842124])

In [39]: true_y
Out[39]: 
array([ 3.        ,  2.94      ,  2.9118    ,  2.824446  ,  2.70971262,
        2.68842124])
Warren Weckesser
  • 110,654
  • 19
  • 194
  • 214
1

Take the z-transform of your filter, that gives you the values for the numerator b and denominator a:

       alpha
y(z) = ------------------ x(z)
       1 - (1-alpha) z^-1

So you run

import scipy.signal
x[0] /= alpha
y = scipy.signal.lfilter([alpha], [1, - 1 + alpha], x)

Which yields

array([ 101.1818    ,  101.176862  ,  101.16819314, ...,  120.9813121 ,
        120.92484874,  120.85786628])

Note I have scaled x[0] to account for the initial condition you wanted.

Nils Werner
  • 34,832
  • 7
  • 76
  • 98
  • Seems to be on the right track but something is off. The result `y` should equal `true_y` from above. I also get an error from setting `a[0]=0` i.e. `[0, 1 - alpha]` (`ValueError: BUG: filter coefficient a[0] == 0 not supported yet`) – Brad Solomon Aug 30 '17 at 22:03
  • 1
    The recurrence relation can be written `y[k] - (1 - a)*y[k-1] = a*x[k]`. Take the z-transform of *that* and solve for `Y(z)` to get the transfer function. – Warren Weckesser Aug 30 '17 at 22:49
  • I need to brush up my z Transform skills it seems :-D – Nils Werner Aug 30 '17 at 22:53
  • This works in addition to @WarrenWeckesser's answer. I would just suggest `lfilter([a], [1., -1. + a], np.insert(x[1:], 0, x[0] / a))` to avoid modifying `x` inplace. – Brad Solomon Aug 31 '17 at 00:35