Sampled measurements often contain repeating components that are easier to read by frequency than by time. NumPy can calculate a fast Fourier transform from a numeric array and identify which frequency bin carries the strongest part of a real-valued signal.
For real-valued input, np.fft.rfft() returns the non-negative side of the discrete Fourier transform. Pair it with np.fft.rfftfreq() built from the same sample count and sample spacing so each magnitude lines up with a frequency bin in hertz.
One second of synthetic data sampled at 16 Hz with a 3 Hz sine wave gives a known peak for the smoke test. Measured data can use the same calculation once signal contains evenly spaced samples and sample_rate matches the acquisition rate; windowing, detrending, and filtering are separate analysis choices.
Related: Create linearly spaced values
Related: Create an array
import numpy as np sample_rate = 16 frequency_hz = 3 samples = np.arange(sample_rate) / sample_rate signal = np.sin(2 * np.pi * frequency_hz * samples) spectrum = np.fft.rfft(signal) frequencies = np.fft.rfftfreq(signal.size, d=1 / sample_rate) magnitudes = np.abs(spectrum) dominant_index = np.argmax(magnitudes[1:]) + 1 dominant_frequency = frequencies[dominant_index] print("frequency bins:", frequencies) print("magnitudes:", np.round(magnitudes, 3)) print("dominant frequency:", dominant_frequency) print("matches expected:", np.isclose(dominant_frequency, frequency_hz))
np.argmax(magnitudes[1:]) + 1 skips the zero-frequency term so a DC offset does not become the selected peak.
$ python3 fft-calculate.py frequency bins: [0. 1. 2. 3. 4. 5. 6. 7. 8.] magnitudes: [0. 0. 0. 8. 0. 0. 0. 0. 0.] dominant frequency: 3.0 matches expected: True
The strongest non-DC magnitude is at 3 Hz, matching the sine wave used to generate the samples.
Keep signal.size and d=1 / sample_rate paired with the data being transformed. Use np.fft.fft() with np.fft.fftfreq() when the input values are complex.