-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
ENH: Add second-order sections filtering #3717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Changes Unknown when pulling 1f08fd4 on Eric89GXL:sosic into * on scipy:master*. |
For compatibility with python 3 use xrange from scipy.lib.six, or just use range? |
numpy.pad seems to have been added in 1.7.0 but scipy currently supports 1.5.1+ |
run_stops = numpy.where(diffs < 0)[0] | ||
|
||
# Sort each run by their imaginary parts | ||
for i in xrange(len(run_starts)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@argriffing You mean here? This can just be range
, sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixing the travis
errors now...
Changes Unknown when pulling 0288b4d on Eric89GXL:sosic into * on scipy:master*. |
The weather in the cloud seems to be causing the reported error https://status.heroku.com/incidents/636. Or that's what I guess, because the TravisCI status page reports Heroku problems, and the builds do not usually exceed the 50 min limit. |
Changes Unknown when pulling 01f30bb on Eric89GXL:sosic into * on scipy:master*. |
Changes Unknown when pulling e4f4aa0 on Eric89GXL:sosic into * on scipy:master*. |
I think having tf2sos is good because even though the coefficients are not optimal, implementing it as sos is still better than implementing it as a single big filter? also for compatibility with octave and matlab Octave and Matlab both include a scaling/gain vector also, like
http://www.mathworks.com/help/signal/ref/tf2sos.html Matlab also lets you sort the sections by distance from the unit circle, but that could also be added later. |
b = [1.] | ||
a = [1.] | ||
n_sections = sos.shape[0] | ||
for stage in range(n_sections): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this should be for section in range
now
If the filter is already in I was avoiding the scale-factor stuff because I wasn't sure if it was necessary here. I thought it was mostly for cases where you needed to stay within a particular range (fixed-point), but wasn't too useful for floating-point arithmetic. It simplifies a the code in a few places not to have to deal with it. That being said, if it seems more future-compatible to put it back in, I can. |
Thinking about it more, I think you're probably right about the |
See also | ||
-------- | ||
zpk2sos, sos2zpk | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring of this function would benefit from an example, especially one that shows a problem where you need sos
format to get good numerical precision.
From a quick browse, this looks promising. I agree with your comments about |
Actually I guess you're right. Blue is sosfilt(tf2sos(ba)), green is lfilter(ba):
Yes, I think that's the main purpose, which is why it's not used in matlab's
freqz doesn't support sos yet, though, so how to show this?
or could show that the impulse response blows up for the ba form? |
The one problem I'm hitting with the
Adding
This isn't great, since then users must specify
The disadvantage is it's inconsistent with the other filtering functions, where you'd do |
Well, does it need to be in sosfilt if that's a floating-point implementation? Or should sosfilt accept object arrays, etc, too? (Oh it does already.) Matlab and octave's sosfilt don't have the k argument. Maybe make it the last argument? |
No, I suppose not. We can add a note to Come to think of it, how about we just make it so that there's a function to extract the overall gain from or apply an overall gain to a given |
I think would be good to follow matlab's options for zpk2sos, except that
so for example:
and yes, leave out the k/g term from sosfilt and the docstring for zpk2sos could say Or add it to the end of sosfilt for the unusual cases that need it:
? |
Those options all make sense. However, it just occurred to me that adding parameters for |
Yes, that's what I meant by "can always be added later" :) The only exception is g parameter in sosfilt, but if it's placed as the last parameter, then that could be left for another PR too, if it's even needed. |
Yeah, I can't think of a better place for it to go than last. I suspect it |
Comments addressed, added the following example showing >>> [b, a] = butter(6, [0.10, 0.105], 'band', output='ba')
>>> sos = butter(6, [0.10, 0.105], 'band', output='sos')
>>> x = zeros(1500)
>>> x[0] = 1.
>>> y_tf = lfilter(b, a, x)
>>> y_sos = sosfilt(sos, x)
>>> plot(y_tf, 'r')
>>> plot(y_sos, 'k') |
Changes Unknown when pulling d30f250 on Eric89GXL:sosic into * on scipy:master*. |
if len(z) > len(p): | ||
raise ValueError('Cannot have more zeros than poles') | ||
if len(z) == len(p) == 0: | ||
return array([[1., 0., 0., 1., 0., 0.]]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this try to output the same dtype as the input array? I see things like this elsewhere but I'm not sure when it matters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the first element b0 should be k?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zpk2sos
ensures that the resulting filter coefficients are real by pairing up complex conjugates. It's possible that z
, p
, or k
could be a zero-length complex type, though, which means if we use their datatype instead of just using the default np.float64
, we could end up with a complex-type SOS filter here.
This is probably not right:
The first is just the second times z^-1/z^-1, so they're equivalent, but the extra 0s should probably not be there. |
I'm not sure the best way to deal with that. On the one hand, you might expect |
No I don't see a conflict with zeroflag other than thinking about the name and both options being about "pairing" in some way. Maybe "nearest" instead of "simple"? |
I'm okay with |
Or should it also be a Boolean flag? keepodd = true, false? |
I'm okay with the current API -- I think that string names for the algorithms are a good way to go for now. If we want to expand to Last amended to change the algorithm name from |
I guess I'm more or less ok with that, too. With my previous comment I meant that if "nearest" is the default algorithm (there will never be a But combining pairing flags into a single parameter is ok, too. It could even be updated to something like I'm not trying to be a pain; it just seems difficult to change an API once it's been created. :) |
Making API changes is indeed a pain, and I agree it's important to do our due diligence in these design choices, so I do appreciate the discussion and progress we've made. At the same time, we have missed one release cycle (0.15) by debating these and related issues, so I'm starting to think that it's likely that the feedback we will get through people actually using this tool will out-weigh our ability to accurately predict what API or practical changes will be most useful to end users. I am thus a bit wary of spending more time tweaking the API, assuming that we are agreed that the API is pretty future compatible, or at least that there isn't a better formulated proposal available... |
Yes, that's why I wanted to leave pairing/scaling/ordering for other PRs, since the filters mostly work fine without them. But yes, I'm happy with it the way it is. A single parameter called |
Nothing from my end. If you're happy, then we just need to convince @WarrenWeckesser :) |
Consider me convinced. I'll have a couple follow-up PRs, but this PR has cooked enough. |
ENH: Add second-order sections filtering
@Eric89GXL and @endolith: Thanks for all the hard work, and for your patience. :) |
Thanks for so many rounds of review, they were useful. Feel free to ping me for review / comments on follow-up or other signals-related PRs. |
Yay! Thanks @Eric89GXL and @WarrenWeckesser |
Great job guys! This must be one of the most thoroughly hashed out PRs in this repo |
First follow-up: #4447 |
Adding |
I found a problem with the handling of the |
Actually, I just realized this isn't just a matter of pairing of roots/ordering of stages. It's also the coefficient position:
this stage is b = [1, 0, 0], a = [1, 1, 0] → H(s) = s^2/(s^2+s) = s/(s+1) which is not the same as above. For digital filters, trailing zeros don't matter, but for analog filters, leading zeros don't matter. The correct result should be (I wanted to use this to evaluate high-order analog filters accurately, like an So docstring should probably say it's not meant for analog at all for now? |
With @endolith, we've put together a working version of second-order sections. Closes #2444.
This PR contains:
sosfilt
, which performs filtering using second-order sections.zpk2sos
/tf2sos
to convert tosos
format. We might actually want to removetf2sos
. I'm worried that users might have filters intf
form and think that going tosos
will reduce numerical error. Forcing them to dozp2sos(tf2zpk(b, a))
might make them think twice about why there isn't atf2sos
function. I'm interested to hear other opinions on this...?cplxpair
andcplxreal
, which are helper functions for dealing with complex conjugate pairings.Implementing
filtfilt
-equivalent functionality should be very easy, but I think we should tackle that with #3259 as opposed to adding another function here.One other thing missing is
lfiltic
-equivalent functionality. This will require some state-space gymnastics that I haven't managed to solve (despite some effort). I'll try to figure it out, but I think that could be left to a subsequent PR, especially since this one is already pretty big.