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.
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.
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.
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")
$ 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