for a school project, we were supposed to filter out 4 rogue cosine waves of a given frequency. I created a filter of my own by choosing zeroes and poles by hand. Here, I made 4 poles and 4 zeroes (and a conjugated pair to each one):enter image description here

I put these zeroes in one array, all these poles into another, and by using np.poly on both of these arrays, I made the B and A coefficients of a filter. The resulting filter frequency response was what I expected:

enter image description here

However, this filter was not perfect enough, so I made a different one using scipy.buttord and scipy.butter. I made four separate bandstop filters, one for each rogue cosine wave. Each bandstop filter had 4 zeroes on the same place and 4 poles around the same-place zeroes (they had their conjugate parts on the other side of the unit circle) like this:

enter image description here

What I wanted to do next was combine these four filters into one. So what I did was concatenate the four arrays of zeroes into one, then concatenate four different arrays of poles into one. Then, using np.poly, I thought I would receive the coefficients of the resulting filter. However, the frequency response looks like this (obviously wrong):

enter image description here

I did some reading and I've been unable to see why this happens. If combining multiple filters like this was not possible, why did it work the first time in my handmade filter? If it is possible, why does it not work here, for the generated filter?


This is how the frequency response of a single filter looks like. The remaining three look the same, except the zero is at different frequencies.

enter image description here

  • $\begingroup$ how is this "obviously" wrong? Does it not have nulls at the frequencies you need to suppress? I honestly don't understand why that amplitude response should be wrong, could you please elaborate? $\endgroup$ Dec 27, 2021 at 14:38
  • $\begingroup$ @MarcusMüller The frequency response should look very similar to the one I made by hand. It should be 1 everywhere except the frequencies I need to filter out (875, 1750, 2625, 3500) where it should be 0. $\endgroup$ Dec 27, 2021 at 14:39
  • $\begingroup$ hm, to me it looks much better at being constant over most of the range than your manual design. And again, from the plot I really can't say whether your four frequencies are suppressed or not – it's simply not a useful visualization for that, it probably doesn't sample the frequency response at these points, exactly $\endgroup$ Dec 27, 2021 at 14:41
  • $\begingroup$ @MarcusMüller I edited my question, I added the response of a single filter. $\endgroup$ Dec 27, 2021 at 14:41
  • $\begingroup$ @MarcusMüller I thought it should be 0 at the frequencies I need to remove (875, 1750, 2625, 3500), whereas from the combined frequency response it is apparent that it does not supress the 1750Hz one, it even amplifies it. $\endgroup$ Dec 27, 2021 at 14:43

1 Answer 1


Then, using np.poly, I thought I would receive the coefficients of the resulting filter.

I'm guessing, that's your problem. With 16 poles this becomes a very high order polynomial which is numerically challenging. Try implementing the filter as cascaded second order sections instead.

If you want to eliminate steady state sine waves with constant frequency, a notch filter is probably a better choice than a bandstop.

  • $\begingroup$ Hmm, this might be the root (no pun intended) of the problem. What do you mean by cascaded second order sections? I've done some googling but was unable to find a precise answer. I assume you mean filtering by each of the four filters in sequence? $\endgroup$ Dec 27, 2021 at 14:49
  • $\begingroup$ @ampersander: It means to use a cascade of second-order filters, each representing one complex pole and zero pair. Use poles and zeros that are closest to each other. $\endgroup$
    – Matt L.
    Dec 27, 2021 at 14:57
  • $\begingroup$ Take a look at docs.scipy.org/doc/scipy/reference/generated/…. There are three ways to represent filters. Transfer function (b,a); zeroes+poles+gain (zpk) and second order section (sos). Transfer function is generally a bad idea, especially for higher order filters or filters with poles close to the unit circle. If that doesn't ring a bell, ask separate question. $\endgroup$
    – Hilmar
    Dec 27, 2021 at 15:01
  • $\begingroup$ @Hilmar Thank you for your answer. It makes sense. However, I asked my Signals and Systems professor about this, and he told me that all I needed to do was convolve all the filters together and the result would be the final filter. I am pretty unsure, because no one mentioned it here. $\endgroup$ Dec 27, 2021 at 15:07
  • $\begingroup$ Can you post your poles ? How do you calculate the frequency response from the polynomials? $\endgroup$
    – Hilmar
    Dec 27, 2021 at 16:23

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.