# Is it correct to subtract a low-pass filtered signal from the original signal and use the result as a “high-pass”?

I am having a hard time trying to find documentation to implement band-pass or high-pass filter with python/scipy/numpy.

I can easily create and apply a low-pass filter, though, so I ask:

Would it be conceptually correct to low-pass-filter a signal, then subtract the result from the original signal, in order to get just the high-frequencies?

Also, if anyone has a simple example of a naive bandpass filter in Python (preferrably using the numpy and scipy libraries), I'd be very thankful.

What I look for is something like:

filtered_signal = band_pass(original_signal, rate, low=20, high=500)


Thanks for any help!

EDIT: with scipy, I'm using this as low-pass, with good results:

import numpy, scipy.signal

def firfilt(interval, freq, sampling_rate):
nfreq = freq/(0.5*sampling_rate)
taps =  sampling_rate + 1
a = 1
b = scipy.signal.firwin(taps, cutoff=nfreq)
firstpass = scipy.signal.lfilter(b, a, interval)
## second pass to compensate phase delay
secondpass = scipy.signal.lfilter(b, a, firstpass[::-1])[::-1]
return secondpass

• The documentation for scipy.signal.firwin tells how to make low-pass, high-pass, band-pass, band-stop, and multi-band filters. Did you try firwin(taps, cutoff=nfreq, pass_zero=False)? – endolith Aug 23 '12 at 18:10
• See sound.whsites.net/articles/derived-xovers.htm for use of this technique in speakers – endolith Feb 10 '18 at 5:46

In theory you can do this, but in practice it is difficult to do because the time and phase alignment must be pretty good for it to work. If the alignment is good you will get the destructive interference that you are seeking. If they aren't, you will get some constructive interference. Even worse, whether they are destructively or constructively interfering will depend on the frequency- i.e. you can get both constructive and destructive interference at the same time. It can work, though, if you are only filtering out fairly low frequencies, since their timing requirements are the loosest because they change so slowly.

Short story- it's possible to do but is difficult enough that it generally makes sense to just do a high-pass filter.

A relatively straightforward way to create a bandpass filter is to create a low-pass filter, and then modulate it to the center frequency you want by multiplying it with a sinusoid of that frequency.

• I tend to pass the filter twice, one of them in reverse, to compensate the phase shift. – heltonbiker Aug 23 '12 at 13:33
• @heltonbiker Then you should be able to do it that way, provided you get the timing lined up right and make sure the low-pass filter gain is 1. – Jim Clay Aug 23 '12 at 15:19

You can design the different filter types directly with the scipy.signal functions. There are three main functions for creating finite impulse response filters with the scipy.signal package.

1. signal.remez
2. signal.firwin
3. signal.firwin2

The remez function, as arguments, takes the number of taps (order+1), the "bands", and the "desired" gain. The "bands" is in Hz. This funciton is a little odd that the "Hz" parameter defines the sample rate in Hz. An example would be:

from scipy import signal
b = signal.remez(64, [0, 80, 100, 200, 220, 500], [0, 1, 0], Hz=1000)
plot(20*log10(abs(fft.fft(b, 4096).)))


Note: I cheated a little bit and used a higher order FFT to make the plot look a little nicer (interpolated the points for visualization only).

Lowpass and highpass examples:

bl = signal.remez(64, [0, 248, 252, 500], [1, 0], Hz=1000) #lowpass
bh = signal.remez(64, [0, 248, 252, 500], [0, 1], Hz=1000) #highpass


The firwin function takes the number of taps again and the cutoff as arguments. The cutoff can be multiple values as a list to define bandpass and stopband filters. The default units for the cutoff is normalized frequency where the nyquist cutoff is 1 and the sample rate would be 2. This can be modified by setting /nyq/. Using the examples above the firwin would be called as:

b = signal.firwin(64, [100, 200], pass_zero=False, nyq=500)


The firwin2 is closer to the remez funcion. But instead of passing gains for the bands you pass gains at the cutoffs.

b = signal.firwin2(64, [0, 100, 200, 500], [0, 1, 1, 0], nyq=500)


More examples available here

• firwin, and its wrapper butter are what I'm using now. Thanks! – heltonbiker Aug 28 '12 at 16:57
• butter isn't a wrapper for firwin. Butter is an IIR filter design method. The iirdesign function is a general purpose IIR filter design tool. Where butter, cheby, etc. are Matlab'ish functions. More info on the IIR filter functions here, bit.ly/JPS4Zs – Christopher Felton Aug 28 '12 at 17:05
• I guess I misunderstood what I was doing. Gonna check it out, thanks (since I definitely want a FIR filter). – heltonbiker Aug 28 '12 at 17:34

You indicated that you're having trouble figuring out how to design a suitable highpass filter. One method is to first design a lowpass filter prototype, then apply a transform that warps the filter's response into a filter of another type (such as a highpass or bandpass filter). This is done by substituting an expression for $z^{-1}$ into the transfer function of the prototype lowpass filter. Here are some links to information on the topic:

Specifically, for a lowpass-to-highpass transformation, you can apply the following substitution:

$$z^{-1}=\frac{\alpha + z^{-1}}{1+\alpha z^{-1}},$$ $$\alpha = \frac{\cos\left(\frac{1}{2}\left(\omega_c - \omega_c'\right)\right)} {\cos\left(\frac{1}{2}\left(\omega_c + \omega_c'\right)\right)}$$

where $\omega_c$ is the cutoff frequency of the lowpass filter prototype and $\omega_c'$ is the resulting cutoff frequency in the transformed highpass filter. There are some examples given in the MATLAB documentation shown in the first link.; there may be similar functions available in SciPy. With that said, much of the filter design functions in that library follow MATLAB's example closely, and they are capable of designing filters of all of the major types (lowpass, highpass, etc.) with very little effort.

• Thanks for your attention, but I have to admit that I don't have enough background to digest the overly technical/mathematical information you posted, I am from bio-sciences field and was expecting a more straightforward answer. If the effort to create a highpass is relatively little, would it be possible to post some small working code, or link to an example? – heltonbiker Aug 23 '12 at 1:32
• What types of filters have you been using? Did you design them using a SciPy library function? – Jason R Aug 23 '12 at 2:19
• I'll edit the question with the low-pass I'm using – heltonbiker Aug 23 '12 at 12:21
• See endolith's comment above on your question. As he indicated, the function that you're using to make your filter appears to be capable of designing highpass filters as well. – Jason R Aug 24 '12 at 3:39