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

</>
Copy
y = scipy.fftpack.fft(x, n=None, axis=-1, overwrite_x=False)
Parameter Required/
Optional
 [datatype] Description
xRequired[array] Array on which FFT has to be calculated.
nOptional[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].
axisOptional[int] Axis along which the fft’s are computed; the default is over the last axis (i.e., axis=-1).
overwrite_xOptional[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

</>
Copy
from scipy.fft import fft

y = fft(x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, plan=None)
ArgumentMeaning in scipy.fft.fft
xInput array or sequence to transform.
nNumber of transform points. Smaller values truncate the input; larger values zero-pad the input.
axisArray axis along which the one-dimensional FFT is computed. The default is the last axis.
normNormalization mode. Common values are None, "backward", "forward", and "ortho".
overwrite_xAllows SciPy to overwrite input data when possible. Keep it False unless you are optimizing memory use and do not need the original input.
workersNumber of worker threads for eligible FFT computations.
planReserved for passing a precomputed vendor-specific FFT plan. In most beginner examples, leave it as None.

Example 1 – SciPy FFT

scipy-example.py

</>
Copy
# 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.

</>
Copy
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.

</>
Copy
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.

</>
Copy
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.

</>
Copy
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.

</>
Copy
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() or rfftfreq().
  • Ignoring the sampling rate: The frequency scale depends on the sampling interval d.
  • Plotting complex values directly: Use np.abs() for magnitude or np.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 functionUse 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.fft while the original scipy.fftpack example 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 d so readers can interpret hertz correctly.
  • Ensure output-only blocks use the output class and Python examples use the language-python class.
  • Review the FAQ answers for SciPy-specific terms such as fft, rfft, fftfreq, and ifft.