Bandpass butterworth filter frequencies in scipy I'm designing a bandpass filter in scipy following the cookbook. However, if I decrecrease the filtering frequencies too much I end up with garbage at high order filters. What am I doing wrong?

from scipy.signal import butter, lfilter

def butter_bandpass(lowcut, highcut, fs, order=5):
    nyq = 0.5 * fs
    low = lowcut / nyq
    high = highcut / nyq
    b, a = butter(order, [low, high], btype='band')
    return b, a

if __name__ == "__main__":
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.signal import freqz  
    # Sample rate and desired cutoff frequencies (in Hz).
    fs = 25
    # Plot the frequency response for a few different orders.
    plt.figure(1)
    plt.clf()
    for order in [1, 3, 5, 6, 9]:
        b, a = butter_bandpass(0.5, 4, fs, order=order)
        w, h = freqz(b, a, worN=2000)#np.logspace(-4, 3, 2000))
        plt.semilogx((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)  
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain')
    plt.grid(True)
    plt.legend(loc='best')

    plt.figure(2)
    plt.clf()
    for order in [1, 3, 5, 6, 9]:
        b, a = butter_bandpass(0.05, 0.4, fs, order=order)
        w, h = freqz(b, a, worN=2000)#np.logspace(-4, 3, 2000))
        plt.semilogx((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)  
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain')
    plt.grid(True)
    plt.legend(loc='best')

    plt.show()
You're probably running into numerical precision issues on the higher-order filters with sharp cutoffs. It could be a limitation of the butter function in SciPy, or it could be due to the filter structure that it's using when evaluating the frequency response. A better way of implementing high-order IIR filters is using a second-order sections (SOS) structure.

A little Googling suggests that SciPy doesn't yet support converting a filter to its SOS representation. Furthermore, there are apparently some issues with how filter parameters are handled internally that cause bad results with high-order filters. That sounds like what you're describing. These are apparently in the process of being addressed.

With that said, I came across this example that shows how you might implement an SOS structure using SciPy. I haven't tested the code at all, so there's no warranty, but it may be worth a look.