How do I plot an fft in python using scipy and modify the frequency range so that it shows the two peaks frequencies in the center?

Question:

The following Python code uses numpy to produce a frequency plot of a sinusoid graph :

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N//2)

fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N//2]))
plt.show()

enter image description here

Based on the code above , we are plotting a sine wave with two frequencies, one at 50Hz and another at 80 Hz. You can clearly see the Fourier transform plot shows peaks at those two frequencies.

My question: How do I modify the above code so that the x-axis ranges from 0-100Hz ?

If I change

xf = np.linspace(0.0, 1.0/(2.0*T), N//2)

to

xf = np.linspace(0.0, 100, N//2)

Then my graph looks like:

enter image description here

But the graph now shows my peaks at around 11 and 20Hz, which is not correct. When I change my axis, the peak values should not change.

What am I doing wrong?

Asked By: user1068636

||

Answers:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N//2)

fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N//2]))
ax.set(
    xlim=(0, 100)
)
plt.show()

simply add the xlim

enter image description here

Answered By: Max Pierini

An alternate solution is to plot the appropriate range of values.

When performing a FFT, the frequency step of the results, and therefore the number of bins up to some frequency, depends on the number of samples submitted to the FFT algorithm and the sampling rate. So there is a simple calculation to perform when selecting the range to plot, e.g the index of bin with center f is:

idx = ceil(f * t.size / sr)

This code works:

from numpy import arange, sin, pi, abs
from math import ceil
import matplotlib.pyplot as plt
from scipy.fft import rfft, rfftfreq

sr = 800 # Sampling rate = 800Hz
N = 600 # Number of samplepoints (duration = 0.75s)
t = arange(0, N/sr, 1/sr) # Sampling times

# Sampled values
s = sin(50 * 2*pi*t) + 0.5*sin(80 * 2*pi*t)

# FFT and bin centers
y = rfft(s)
f = rfftfreq(t.size, 1/sr)

# Index of 100Hz bin
f_hi = 100
idx_hi = ceil(f_hi * t.size / sr)

# Plot spectrum
fig, ax = plt.subplots()
ax.plot(f[:idx_hi], abs(y[:idx_hi]))
plt.show()

and produces:

enter image description here


As you only work with the real signal, you can cut computing time using rfft instead of fft which works with complex values. To get the frequency bin centers, you can use rfftfreq

Answered By: mins
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.