-
-
Notifications
You must be signed in to change notification settings - Fork 11k
BUG: wrong frequency wrapping in fft.fftfreq for even n #29162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It's a matter of (documented) convention (consult also this comment): Due to the periodicity of sampled spectra, the FFT values at In [1]: import sympy as sy
In [2]: n = sy.symbols('n', integer=True)
In [3]: [sy.simplify(sy.exp(sy.I*2*sy.pi*k/n)) for k in (n/2, -n/2)]
Out[3]: [-1, -1] This convention probably goes back to Matlab: Adapted from an example from Matlab's fft page (using Octave Online): > L = 6; Fs = 6
Fs = 6
> Fs/L*(-L/2:L/2-1)
ans =
-3 -2 -1 0 1 2 Note that this ambiguity can bite you, when utilizing the FFT for resampling signals. Consult the example section of SciPy's resample for a short discussion. |
Thank you for the link, seems that this issue was spotted long time ago but it was not fixed. Yes, you can think of the frequency at In this common use, the point In this respect, it is not a matter of convention. Of course one can easily plot the spectrum including the missing point, either with further frequency wrapping or re-calculating the frequencies points of interest (which is pretty easy), but then what is the whole purpose of [Out of topic: in most cases the signal is real, but when it is complex, I personally prefer to plot the spectrum from 0 to |
@seberg |
If you are interested only in the non-negative frequencies, just use rfftreq (and rfft)—it returns In [1]: import numpy as np
In [2]: n = 6
...: x = np.ones(n) # n-sample input signal
In [3]: X = np.fft.fft(x) # two-sided FFT
...: f_onesided = np.fft.rfftfreq(n, 1/n)
...: X_onesided = X[:len(f_onesided)] # only non-negative frequency bins
In [4]: f_onesided
Out[4]: array([0., 1., 2., 3.]) The problem is that if you want to change the behavior of |
Thanks, I create the frequencies myself anyway (it is 1-2 lines of code), but I was surprised by the example in https://docs.scipy.org/doc/scipy/tutorial/fft.html. It should use |
Uh oh!
There was an error while loading. Please reload this page.
Describe the issue:
Problem
fft.fftfreq(n, ts)
wheren
is the number of samplests
is the sampling time (step)generates a vector of
n
frequencies in the range[-fs/2, fs/2)
, wherefs=1/ts
is the sampling frequency. The generated frequencies should be in the range(-fs/2, fs/2]
, i.e., if one of the frequencies isfs/2
(which happens whenn
is even), it should have a positive sign.Fix
The following code gives the original source code (with cosmetic changes), the fixed code, and an example where the two functions give different result. The fix is in the calculation of
m
(first line in each function), which differs whenn
is even.Notice that the frequency
fs/2 = 3
is wrapped to-3
, but it shouldn't.Theory
For an even
n
, a sequence ofn
real numbers is transformed by FFT to a sequence ofn
complex numbers, of which the lastn/2 - 1
numbers are redundant because they are complex conjugates of corresponding number in the first part. The remainingn/2 + 1
numbers are all necessary to reconstruct the original real signal. They representn + 2
real numbers, but the imaginary part of the first (index 0) and the last (indexn/2
) are always 0, so they actually representn
independent real numbers.For an odd
n
, the last(n-1)/2
transformed numbers are redundant, and of the remaining(n+1)/2
only the first always has zero imaginary part, so in total they representn
independent real numbers.Impact
For an even
n
(which is the most common use case), when the FFT of a signal is plotted usingfft.fftfreq()
, the maximum frequencyfs/2
is hidden. For a realistic sampling process, the sampled signal (e.g., audio) doesn't have frequency content atfs/2
, since it has been filtered before sampling. Howeverfft.fftfreq()
should not make any assumption about the conditioning of the original signal.Example: plot the FFT as in the documentation examples, with an even number of samples, and a signal which contains the following tone
cos(2*np.pi*(fs/2)*t) = cos(np.pi*t/ts) = [+1, -1, +1, ..., -1]
which is an alternating signal (after sampling). The plot should show a tone at
fs/2
, but this tone is removed byfft.fftfreq()
. Notice that replacingcos
bysin
in the above example does not show the problem, because the signal is 0.Reproduce the code example:
Error message:
Python and NumPy Versions:
numpy 2.3.0
python 3.13.3
Runtime Environment:
No response
Context for the issue:
No response
The text was updated successfully, but these errors were encountered: