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
ENH: Improve performance of lfilter / filtfilt by using scipy.ndimage.convolve1d #17080
Comments
Interesting - I would have thought np.convolve is C-level and unlikely to be much (if at all) slower. Does Some timing results would be good. Could you attach the |
|
I get these timings for different orders and inputs lengths (see script at end). I was a little surprised as the low order result, but I ran it again and got much the same figure. ndimage is slowest for low order and large input length, but fastest for intermediate and high(-ish?) order. As input length and order increase the difference become small. ndimage is about 3 times faster for short inputs and higher order filters, but the absolute times in that case are smaller (under 1ms). import sys
import timeit
import numpy as np
import scipy
import matplotlib.pyplot as plt
from scipy import signal, ndimage
print(f'{sys.version = !s}\n{np.__version__ = !s}\n{scipy.__version__ = !s}')
def firfilter(b, u):
return ndimage.convolve1d(u, b, mode="constant", cval=0, origin=-(len(b)//2))
def firfilter2(b, u):
return np.convolve(b, u)[:len(u)-(len(b)-1)]
stats = []
orders = [2, 12, 22]
nus = 10**np.arange(3,8)
for order in orders:
b = signal.firwin(order, 0.2)
ostats = []
for nu in nus:
print(order, nu, flush=True)
u = np.random.normal(size=nu)
ostats.append(timeit.Timer('signal.lfilter(b, 1, u)', globals=globals()).autorange())
ostats.append(timeit.Timer('firfilter(b, u)', globals=globals()).autorange())
ostats.append(timeit.Timer('firfilter2(b, u)', globals=globals()).autorange())
stats.append(ostats)
astats = np.array(stats)
# axes are order, nu, func
times = np.reshape(astats[...,1] / astats[...,0], [len(orders),-1,3])
fig, ax = plt.subplots(1, 3, sharey='row')
for i in range(3):
ax[i].loglog(nus, times[i])
ax[i].legend(['lflt','ndimg','npconv'])
ax[i].set_xlabel('nu')
ax[i].set_title(f'FIR order {orders[i]}')
ax[0].set_ylabel('time [s]')
fig.tight_layout()
fig.savefig('firfilt-time.png') |
But you compare application on a single 1d array. The point is that the scipy filter functions use convolve1d supports |
Ah, I see. This is a much simpler example demonstrating the problem:
This uses this definition of def firfilter(b, u, axis=-1):
return ndimage.convolve1d(u, b, mode="constant", cval=0, origin=-(len(b)//2), axis=axis)
|
When looking at the documentation for On the other hand, according to the documentation for |
Is your feature request related to a problem? Please describe.
The scipy.signal.lfilter uses
np.apply_along_axis
withnp.convolve
scipy/scipy/signal/_signaltools.py
Line 2052 in 2e5883e
In my simple tests, this is much slower than what seems to be exactly equivalent:
Describe the solution you'd like.
Switch to using
convolve1d
if these calls are really equivalent?Describe alternatives you've considered.
No response
Additional context (e.g. screenshots, GIFs)
No response
The text was updated successfully, but these errors were encountered: