Periodic structure can disappear inside a long sequence of time-domain samples. SciPy converts an evenly sampled signal into frequency bins, exposing the tones that contribute most strongly to audio, sensor, or simulation data.
For real-valued input, rfft() returns the nonnegative half of the symmetric spectrum. Its companion rfftfreq() returns the matching bin centers in hertz when the sample spacing is expressed in seconds, so the frequency and amplitude arrays remain aligned.
Frequency resolution equals the sample rate divided by the sample count. The one-second, 200 Hz signal used here has 1 Hz spacing, placing its 30 Hz and 75 Hz components directly on bins; measurements with partial cycles may need a longer recording or a window to limit spectral leakage.
Steps to compute an FFT with SciPy:
- Create fft_compute.py with the imports and a real signal containing 30 Hz and 75 Hz components.
- fft_compute.py
import numpy as np from scipy.fft import rfft, rfftfreq sample_rate = 200.0 duration = 1.0 sample_count = int(sample_rate * duration) time = np.arange(sample_count) / sample_rate signal = ( 1.2 * np.sin(2.0 * np.pi * 30.0 * time) + 0.4 * np.sin(2.0 * np.pi * 75.0 * time) )
The two components complete an integer number of cycles inside the sampled second, so their energy falls on the corresponding FFT bins.
- Append the real-input transform and frequency-axis calculation below the signal definition.
spectrum = rfft(signal) frequencies = rfftfreq(sample_count, d=1.0 / sample_rate) amplitudes = (2.0 / sample_count) * np.abs(spectrum) amplitudes[0] /= 2.0 if sample_count % 2 == 0: amplitudes[-1] /= 2.0
The one-sided scaling doubles bins that represent both positive and negative frequencies, while the DC and even-length Nyquist bins remain unpaired.
- Append peak detection and a fail-capable frequency check below the amplitude calculation.
peak_indexes = np.flatnonzero(amplitudes > 0.1) detected_frequencies = frequencies[peak_indexes] expected_frequencies = np.array([30.0, 75.0]) matches_expected = np.allclose( detected_frequencies, expected_frequencies, atol=0.5, ) print(f"frequency_bins: {frequencies.size}") print(f"bin_spacing_hz: {frequencies[1] - frequencies[0]:.1f}") print("detected peaks:") for index in peak_indexes: print(f" {frequencies[index]:5.1f} Hz amplitude {amplitudes[index]:.3f}") print(f"matches_expected_frequencies: {matches_expected}") if not matches_expected: raise RuntimeError("FFT peaks did not match the known signal frequencies")
- Run fft_compute.py with the same Python environment that provides SciPy.
$ python3 fft_compute.py frequency_bins: 101 bin_spacing_hz: 1.0 detected peaks: 30.0 Hz amplitude 1.200 75.0 Hz amplitude 0.400 matches_expected_frequencies: True
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.