SciPy FFT in Python
SciPy FFT is used to compute the Discrete Fourier Transform (DFT) of a sequence. In practical Python programs, it is commonly used to move sampled data from the time domain to the frequency domain so that you can inspect frequency components, magnitudes, and phase.
The older examples in this page use scipy.fftpack.fft. For new SciPy code, prefer the newer scipy.fft namespace because it is the current FFT interface in SciPy. The old fftpack import is still useful when you maintain older code, so both forms are explained below.
What scipy.fft.fft returns for a Python array
The FFT result is a complex array. Each complex value represents one frequency bin. To inspect the strength of a frequency component, use np.abs() on the FFT result. To find the actual frequency values for those bins, use scipy.fft.fftfreq() with the sampling interval.
fft(x)returns complex DFT values.np.abs(fft(x))returns magnitudes.fftfreq(len(x), d=sampling_interval)returns the matching frequency bins.ifft(fft(x))can reconstruct the original signal, apart from small floating-point rounding errors.
scipy.fftpack.fft syntax used in older SciPy examples
y = scipy.fftpack.fft(x, n=None, axis=-1, overwrite_x=False)
| Parameter | Required/ Optional | [datatype] Description |
| x | Required | [array] Array on which FFT has to be calculated. |
| n | Optional | [int] Length of the Fourier transform. If n < x.shape[axis], x is truncated. If n > x.shape[axis], x is zero-padded. The default results in n = x.shape[axis]. |
| axis | Optional | [int] Axis along which the fft’s are computed; the default is over the last axis (i.e., axis=-1). |
| overwrite_x | Optional | [boolean] If True, the contents of x can be destroyed; the default is False. |
| y | [Returned value] | [complex ndarray] Discrete Fourier Transform of x. |
Values provided for the optional arguments are default values.
Recommended scipy.fft.fft syntax for new Python code
from scipy.fft import fft
y = fft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, plan=None)
| Argument | Meaning in scipy.fft.fft |
|---|---|
x | Input array or sequence to transform. |
n | Number of transform points. Smaller values truncate the input; larger values zero-pad the input. |
axis | Array axis along which the one-dimensional FFT is computed. The default is the last axis. |
norm | Normalization mode. Common values are None, "backward", "forward", and "ortho". |
overwrite_x | Allows SciPy to overwrite input data when possible. Keep it False unless you are optimizing memory use and do not need the original input. |
workers | Number of worker threads for eligible FFT computations. |
plan | Reserved for passing a precomputed vendor-specific FFT plan. In most beginner examples, leave it as None. |
Example 1 – SciPy FFT
scipy-example.py
# import numpy
import numpy as np
# import fft
from scipy.fftpack import fft
# numpy array
x = np.array([1.0, 2.0, 1.0, 2.0, -1.0])
print("x : ",x)
# apply fft function on array
y = fft(x)
print("fft(x) : ",y)
Output
x : [ 1. 2. 1. 2. -1.]
fft(x) : [ 5.00000000+0.j -1.11803399-2.2653843j 1.11803399-2.71441227j
1.11803399+2.71441227j -1.11803399+2.2653843j ]
The first value in the FFT output is the zero-frequency component. The remaining values are complex frequency components. For many analysis tasks, you will look at the magnitude using np.abs(y) instead of printing the complex numbers directly.
Example 2 – Find the dominant frequency with scipy.fft and fftfreq
The next example creates a sampled 5 Hz sine wave, computes its FFT, and uses fftfreq() to map FFT bins to frequency values. This is the usual pattern when you want meaningful frequency labels instead of only FFT array indexes.
import numpy as np
from scipy.fft import fft, fftfreq
sample_rate = 100
duration = 2
t = np.arange(0, duration, 1 / sample_rate)
x = np.sin(2 * np.pi * 5 * t)
y = fft(x)
freq = fftfreq(len(x), d=1 / sample_rate)
positive = freq > 0
dominant_frequency = freq[positive][np.argmax(np.abs(y[positive]))]
print("Samples:", len(x))
print("Dominant frequency:", dominant_frequency, "Hz")
Output
Samples: 200
Dominant frequency: 5.0 Hz
Here, d=1 / sample_rate tells SciPy the time gap between two samples. Without this value, the frequency bins are returned in cycles per sample, not in hertz.
Example 3 – Use scipy.fft.rfft for real input signals
When the input signal is real-valued, rfft() is often more convenient than fft(). It returns only the non-negative frequency terms, so you do not have to manually remove the negative-frequency half of the spectrum.
import numpy as np
from scipy.fft import rfft, rfftfreq
sample_rate = 100
duration = 2
t = np.arange(0, duration, 1 / sample_rate)
x = np.sin(2 * np.pi * 5 * t)
y = rfft(x)
freq = rfftfreq(len(x), d=1 / sample_rate)
dominant_frequency = freq[np.argmax(np.abs(y))]
print("Dominant frequency from rfft:", dominant_frequency, "Hz")
Output
Dominant frequency from rfft: 5.0 Hz
Example 4 – Reconstruct a signal with scipy.fft.ifft
The inverse FFT converts frequency-domain data back to the original sample domain. In numerical code, compare the reconstructed values with np.allclose() because floating-point operations may introduce very small differences.
import numpy as np
from scipy.fft import fft, ifft
x = np.array([1.0, 2.0, 1.0, 2.0, -1.0])
y = fft(x)
x_back = ifft(y)
print(np.round(x_back.real, 6))
print(np.allclose(x, x_back.real))
Output
[ 1. 2. 1. 2. -1.]
True
Plotting SciPy FFT magnitudes against frequency bins
When plotting an FFT, avoid plotting only the raw complex result. Plot frequency bins on the x-axis and magnitudes on the y-axis. For real signals, using rfft() and rfftfreq() keeps the plot focused on the non-negative frequency range.
import matplotlib.pyplot as plt
import numpy as np
from scipy.fft import rfft, rfftfreq
sample_rate = 100
t = np.arange(0, 2, 1 / sample_rate)
x = np.sin(2 * np.pi * 5 * t)
y = rfft(x)
freq = rfftfreq(len(x), d=1 / sample_rate)
plt.plot(freq, np.abs(y))
plt.xlabel("Frequency (Hz)")
plt.ylabel("Magnitude")
plt.show()
How n and axis change SciPy FFT results
The n argument controls the number of points in the transform. If n is smaller than the input length, SciPy truncates the input along the selected axis. If n is larger, SciPy zero-pads the input. Zero-padding can make the displayed spectrum smoother, but it does not add new measured information to the signal.
The axis argument matters for two-dimensional and higher-dimensional arrays. For example, if each row of a matrix is one signal, use axis=1. If each column is one signal, use axis=0.
import numpy as np
from scipy.fft import fft
x = np.array([
[1.0, 2.0, 1.0, 2.0],
[0.0, 1.0, 0.0, 1.0]
])
row_wise_fft = fft(x, axis=1)
column_wise_fft = fft(x, axis=0)
print(row_wise_fft.shape)
print(column_wise_fft.shape)
Output
(2, 4)
(2, 4)
Common SciPy FFT mistakes with frequency and magnitude
- Using array indexes as frequencies: FFT indexes are not the same as hertz. Use
fftfreq()orrfftfreq(). - Ignoring the sampling rate: The frequency scale depends on the sampling interval
d. - Plotting complex values directly: Use
np.abs()for magnitude ornp.angle()for phase. - Reading both halves of a real-signal FFT as separate information: For real input, the negative-frequency half mirrors the positive-frequency half. Use
rfft()when you need only the non-negative frequencies. - Expecting zero-padding to improve measurement accuracy: Zero-padding changes the FFT grid, but it does not create new samples.
When to use fft, rfft, ifft, and fftfreq in SciPy
| SciPy FFT function | Use it when |
|---|---|
fft() | You need the one-dimensional FFT of real or complex input. |
rfft() | Your input is real-valued and you only need the non-negative frequency spectrum. |
ifft() | You want to convert a full complex FFT result back to the sample domain. |
fftfreq() | You need frequency bins for the result of fft(). |
rfftfreq() | You need frequency bins for the result of rfft(). |
fftshift() | You want to shift the zero-frequency component to the center of the spectrum for visualization. |
Official SciPy FFT references
For more details, refer to the SciPy FFT tutorial, the scipy.fft.fft reference, and the older scipy.fftpack.fft reference.
FAQs on SciPy FFT in Python
Should I use scipy.fft or scipy.fftpack for FFT in Python?
Use scipy.fft for new code. Use scipy.fftpack mainly when you are reading or maintaining older SciPy examples that already use that namespace.
Why does scipy.fft.fft return complex numbers?
The DFT represents each frequency component with magnitude and phase. A complex number can store both parts. Use np.abs() to get magnitude and np.angle() to get phase.
How do I get frequency values in Hz from a SciPy FFT result?
Use fftfreq(n, d), where n is the number of samples and d is the time interval between samples. If your sample rate is 100 samples per second, then d is 1 / 100.
When should I use rfft instead of fft in SciPy?
Use rfft() when the input signal contains only real numbers and you need the non-negative frequency components. It is easier to interpret for common sampled signals such as sensor readings and audio samples.
Does zero-padding with n improve the original signal?
No. A larger n zero-pads the input before the FFT and creates a denser frequency grid. It can help with plotting and interpolation-style inspection, but it does not add new measured information to the original data.
Editorial QA checklist for this SciPy FFT tutorial
- Verify that new examples use
scipy.fftwhile the originalscipy.fftpackexample remains unchanged. - Check that every FFT example explains whether the printed values are complex output, magnitude, frequency bins, or reconstructed samples.
- Confirm that frequency examples include the sampling interval
dso readers can interpret hertz correctly. - Ensure output-only blocks use the
outputclass and Python examples use thelanguage-pythonclass. - Review the FAQ answers for SciPy-specific terms such as
fft,rfft,fftfreq, andifft.
TutorialKart.com