Fourier transform- Use of linspace for frequency spacing

Hi,
I am currently studying “Master the Fourier transform and its applications/” and while studying Matlab and Python examples, I have noticed that “linspace” function have used inconsistently.
In some places,
% compute frequencies vector
hz = linspace(0,srate/2,floor(pnts/2)+1);

and in some others;
hz = linspace(0,srate,length(time));

the first one is true hz = linspace(0,srate/2,floor(pnts/2)+1) or hz = linspace(0,srate,length(time)+1). Both in Matlab and Python, linspace includes endpoints.

I noticed it in zero-padding of signal and their Fourier spectra (Master the Fourier transform and its applications, chapter 6). Fourier spectra did not give same results for close frequency points, unless correct frequency spacing is used.

Hi Omer. That can indeed get confusing. I explain it at least once in the course, although I couldn’t tell you which video it is :confused:

The positive frequencies go from 0 to Nyquist in N/2+1 steps. That’s the first line of code you wrote.

The second line of code you wrote is correct up until Nyquist and incorrect above Nyquist. This vector can be used as a convenient plotting trick. As long as you don’t interpret the frequencies above Nyquist, it’s OK for plotting.

actually my point is not nyquist, spacing between frequency values. If linspace is used, number of points is +1 (should be)

from the course example
## zero-padding in fft function
%matplotlib notebook

# create the signal
signal = [ 4, 6, -1, 0, 5, -4 ]

# number of zeros to add after signal
n2pad = [ 0, 10, 100 ]

for zi in range(len(n2pad)):
    
    # total length of signal
    zeropadN = len(signal)+n2pad[zi]
    
    # FFT and amplitude
    sigampl   = np.abs( scipy.fftpack.fft(signal,zeropadN) )
    
    # one of the two normalization steps
    sigampl = sigampl / len(signal)
    
    frequnits = np.linspace(0,1,zeropadN)
    
    # and plot
    plt.plot(frequnits[:-1],sigampl,'s-',label='%g-point FFT' %zeropadN)


# add some plot extras

plt.legend()
plt.xlabel('Frequency (.5 = Nyquist)')
plt.ylabel('Amplitude (a.u.)')
plt.show()

ın this, frequnits = np.linspace(0,1,zeropadN+1 ) gives correct plots

In the video also, wrong output is shown (Master the Fourier transform and its applications, chapter 6). Fourier spectra did not give same results for close frequency points

False output (given in the video material also)
false

True Output -
true

Hi Omer. Thank you for clarifying. This is actually a tricky point about the discrete Fourier transform that I really should have gone into more detail about in the course. I’m pretty sure this issue has come up in the past, but I probably should have a dedicated video about it.

You are correct about this example, and I should have noticed it myself given the plots.

But you are not correct in general – it turns out that whether the frequencies are N/2 or N/2+1 depends on whether the signal has an odd or even number of points. Consider the following code in MATLAB (I’ll paste the Python code later):

srate = 1000;
time = (0:srate)/srate;
signal = sin(15*2*pi*time);

hz1 = linspace(0,srate,length(signal)+1);
hz2 = linspace(0,srate,length(signal));
x = abs(fft(signal)) / length(signal);

plot(hz1(1:end-1),x,'bs-', hz2,x,'rs-')
axis([14.9 15.1 .49 .51])

Clearly, this is a signal at exactly 15 Hz, so its amplitude spectrum should have a peak at 15.00000 Hz. This produces the following graph:


The red line is correct while the blue line is incorrect. But the blue line follows your code, which you clearly showed was correct in my example!

Now let’s try a small modification to make the signal have an even length:
time = (0:srate-1)/srate;
That gives the following graph:


Now the blue line (your suggestion) is correct while the red line is incorrect.

In practical applications, this rarely (if ever) matters, because the signals tend to be relatively long, so the frequencies for N/2 vs. N/2+1 are the same within several degrees of precision, typically a much higher precision than is necessary in the application. In the little example code that you pasted, the effect is really observable because the signal has only 6 points.

But anyway, your larger point is correct, which is that this is a subtle but important part of the discrete Fourier transform and I don’t discuss it in the course. I should really make a video about this point. And I’ll also fix the code in that example and re-upload.

Thanks again for the detailed post!

Oops, I forgot to include the Python code. You can comment out the second ‘time’ line to see the difference.

srate = 1000
time = np.arange(0,srate+1)/srate
time = np.arange(0,srate+1-1)/srate
signal = np.sin(15*2*np.pi*time)

hz1 = np.linspace(0,srate,len(signal)+1)
hz2 = np.linspace(0,srate,len(signal))
x   = np.abs(scipy.fftpack.fft(signal)) / len(signal)

plt.plot(hz1[:-1],x,'bs-')
plt.plot(hz2,x,'rs-')
plt.xlim([14.9,15.1])
plt.ylim([.49,.51])
plt.show()

Hi

actually I would also say this but forgot :slight_smile: In any case, the discrepancy was big in the video, hence I wanted to clarify it so that it would be corrected.

Regards,

I wanted to add these links I encountered recently;


https://nl.mathworks.com/help/matlab/ref/fft.html

There is also this function I learned recently; scipy.fftpack.fftfreq

https://docs.scipy.org/doc/scipy/reference/generated/scipy.fftpack.fftfreq.html#scipy.fftpack.fftfreq