1

I have been tasked with applying a "1-2-1" filter to a numpy array and returning an array of the filtered data. Without using for loops while loops or list comprehension.

The "1-2-1" filter maps each point of data to the average of itself twice and its neighbors. For example, if at some point the data contained ...1, 4, 3... then after applying the "1-2-1" filter the 4 would be replaced with (1 + 4 + 4 + 3) / 4 = 12 / 4 = 3.

For example the numpy array [1, 1, 4, 3, 2]
Would after the filter is applied produce a numpy array [1. 1.75 3. 3. 2. ]

Since the end points of the data do not have two neighbors the 1-2-1 filter is only applied to the internal len(data) - 2 points, leaving the end points unchanged.

Essentially I need to access the values before and after a given point during numpy array vectorization. For a array that could be of any length. Which as much as I have googled I cannot work out.

2 Answers 2

1

Pandas solution

s = pd.Series(l)
>>> s.rolling(3, center=True).sum().add(s).div(4).fillna(s).values
array([1.  , 1.75, 3.  , 3.  , 2.  ])

Step by step:

>>> s.rolling(3, center=True).sum().values
array([nan,  6.,  8.,  9., nan])

>>> s.rolling(3, center=True).sum().add(s).values
array([nan,  7., 12., 12., nan])

>>> s.rolling(3, center=True).sum().add(s).div(4).values
array([ nan, 1.75, 3.  , 3.  ,  nan])

>>> s.rolling(3, center=True).sum().add(s).div(4).fillna(s).values
array([1.  , 1.75, 3.  , 3.  , 2.  ])

Numpy solution

a = np.array(l)
>>> np.concatenate([a[:1], np.convolve(a, [1, 2, 1], mode="valid") / 4, a[-1:]])

Step by step:

>>> np.convolve(a, [1, 2, 1], mode="valid")
array([ 7, 12, 12])

>>> np.convolve(a, [1, 2, 1], mode="valid") / 4
array([1.75, 3.  , 3.  ])

>>> np.concatenate([a[:1], np.convolve(a, [1, 2, 1], mode="valid") / 4, a[-1:]])
array([1.  , 1.75, 3.  , 3.  , 2.  ])

Performance

%timeit s.rolling(3, center=True).sum().add(s).div(4).fillna(s).values
706 µs ± 4.62 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit np.concatenate([a[:1], np.convolve(a, [1, 2, 1], mode="valid") / 4, a[-1:]])
10.8 µs ± 22.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Sign up to request clarification or add additional context in comments.

1 Comment

Sadly I think I need to use numpy I’ll give it a shot when I get home. Thanks heaps though
1

You can try something like this:

import numpy as np
from scipy.linalg import circulant

x = np.array([1, 1, 4, 3, 2])

val = np.array([1, 2, 1])
offsets = np.array([0, 1, 2])
col0 = np.zeros(len(x))
col0[offsets] = val

C = circulant(col0).T[:-(len(val) - 1)]
print(C)

C is essentially a circulant matrix which looks like this:

array([[1., 2., 1., 0., 0.],
       [0., 1., 2., 1., 0.],
       [0., 0., 1., 2., 1.]])

Now you can simply compute the filtered output as follows:

y = (C @ x) / 4
print(y)
# array([1.75, 3.  , 3.  ])

2 Comments

I can only use numpy but thanks heaps that seems like a smarter solution then what my quiz is asking for.
I guess if you can figure out a clever way of constructing the circulant matrix using numpy, that will do it!

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.