FFT-based filtering in SciPy - Time & Space Complexity
When using FFT-based filtering, we want to know how the time to process data changes as the data size grows.
We ask: How does the filtering time increase when the input signal gets longer?
Analyze the time complexity of the following code snippet.
import numpy as np
from scipy.fft import fft, ifft
def fft_filter(signal, filter_kernel):
n = len(signal) + len(filter_kernel) - 1
signal_fft = fft(signal, n)
filter_fft = fft(filter_kernel, n)
filtered_fft = signal_fft * filter_fft
filtered_signal = ifft(filtered_fft)
return np.real(filtered_signal)
This code filters a signal by transforming both the signal and filter to frequency space, multiplying them, and then transforming back.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The FFT and inverse FFT computations, which internally perform repeated calculations over the input arrays.
- How many times: Each FFT processes an array of size approximately n (sum of input lengths), doing about n log n steps.
As the input size n grows, the number of operations grows a bit faster than n but much slower than n squared.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 x log₂(10) ≈ 33 |
| 100 | About 100 x log₂(100) ≈ 664 |
| 1000 | About 1000 x log₂(1000) ≈ 9966 |
Pattern observation: Operations grow faster than the input size but much slower than if we did every pairwise multiplication.
Time Complexity: O(n log n)
This means the time to filter grows a bit faster than the input size but remains efficient even for large inputs.
[X] Wrong: "FFT-based filtering takes time proportional to n squared because it multiplies all pairs of points."
[OK] Correct: FFT uses a clever method to reduce repeated calculations, so it runs much faster than checking every pair.
Understanding FFT time complexity shows you can analyze efficient algorithms that handle large data quickly, a useful skill in many data science tasks.
"What if we used a direct convolution instead of FFT-based filtering? How would the time complexity change?"
Practice
Solution
Step 1: Understand FFT transformation
FFT converts data from time domain to frequency domain, revealing frequency components.Step 2: Purpose of filtering in frequency domain
Filtering in frequency domain allows easy removal of unwanted noise or frequencies.Final Answer:
To transform data into frequency domain for easier noise removal -> Option AQuick Check:
FFT filtering = frequency domain noise removal [OK]
- Confusing FFT with sorting or averaging
- Thinking FFT changes data to text
- Assuming FFT directly cleans data without filtering
data using scipy?Solution
Step 1: Identify correct scipy FFT function
The scipy FFT module usesfft()to compute the Fast Fourier Transform.Step 2: Check syntax correctness
The correct syntax isscipy.fft.fft(data), other options are invalid function names.Final Answer:
fft_data = scipy.fft.fft(data) -> Option CQuick Check:
Correct FFT call = scipy.fft.fft(data) [OK]
- Using incorrect function names like fft_transform
- Omitting the .fft part after scipy.fft
- Confusing with numpy.fft functions
filtered_signal?
import numpy as np from scipy.fft import fft, ifft data = np.array([1, 2, 3, 4]) fft_data = fft(data) mask = np.array([1, 0, 0, 1]) filtered_fft = fft_data * mask filtered_signal = ifft(filtered_fft).real print(np.round(filtered_signal, 2))
Solution
Step 1: Compute FFT and apply mask
FFT of data is computed, then multiplied by mask [1,0,0,1], zeroing middle frequencies.Step 2: Compute inverse FFT and round
Inverse FFT of masked data gives filtered signal; rounding to 2 decimals yields [2. 0. 0. 2.].Final Answer:
[2. 0. 0. 2.] -> Option DQuick Check:
Mask zeros middle frequencies, inverse FFT smooths signal [OK]
- Forgetting to multiply FFT by mask
- Not taking real part after inverse FFT
- Rounding incorrectly or not at all
import numpy as np from scipy.fft import fft, ifft data = np.array([1, 2, 3, 4]) fft_data = fft(data) mask = np.array([1, 0, 0]) filtered_fft = fft_data * mask filtered_signal = ifft(filtered_fft).real print(filtered_signal)
Solution
Step 1: Check mask and FFT data lengths
FFT of data has length 4, but mask length is 3, causing shape mismatch error.Step 2: Identify error cause
Multiplying arrays of different lengths raises an error; mask must match FFT length.Final Answer:
Mask length does not match FFT data length -> Option BQuick Check:
Mask length must equal FFT length [OK]
- Using mask shorter than FFT result
- Confusing ifft with fft usage
- Assuming data must be complex initially
signal. You want to remove frequencies above 100 Hz using FFT-based filtering. Which code snippet correctly implements this filter?Solution
Step 1: Calculate frequency bins correctly
Usenp.fft.fftfreq(N, 1/1000)to get frequencies in Hz for sampling rate 1000 Hz.Step 2: Create mask to keep frequencies ≤ 100 Hz
Mask should benp.abs(freqs) <= 100to keep low frequencies and remove higher ones.Step 3: Apply mask and inverse FFT
Multiply FFT by mask, then apply inverse FFT and take real part for filtered signal.Final Answer:
mask = np.abs(freqs) <= 100 with freqs = np.fft.fftfreq(N, 1/1000) -> Option AQuick Check:
Mask keeps frequencies ≤ 100 Hz [OK]
- Ignoring sampling rate in frequency calculation
- Using wrong comparison operators in mask
- Not taking absolute value of frequencies
Ready to prove it?
Pass the test and this pattern moves unknown
