Thanks to visit codestin.com
Credit goes to github.com

Skip to content

BUG: wrong frequency wrapping in fft.fftfreq for even n #29162

Closed
@georgeef

Description

@georgeef

Describe the issue:

Problem

fft.fftfreq(n, ts) where

  • n is the number of samples
  • ts is the sampling time (step)

generates a vector of n frequencies in the range [-fs/2, fs/2), where fs=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 is fs/2 (which happens when n 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 when n is even.

def fftfreq(n, ts):
    m = (n - 1) // 2 + 1
    f = np.empty(n, dtype=int)
    # note: m - n == -(n//2)
    f[:m] = np.arange(0,     m, dtype=int)
    f[m:] = np.arange(m - n, 0, dtype=int)
    return f / (n * ts)

def fftfreq_fix(n, ts):
    m = n // 2 + 1
    f = np.empty(n, dtype=int)
    f[:m] = np.arange(0,     m, dtype=int)
    f[m:] = np.arange(m - n, 0, dtype=int)
    return f / (n * ts)

def test(n):
    # normalize such that frequencies are integer numbers
    ts = 1/n  # sampling time (step)
    fs = 1/ts # sampling frequency
    print('frequencies in [-fs/2, fs/2) :', fftfreq(n, ts))
    print('frequencies in (-fs/2, fs/2] :', fftfreq_fix(n, ts))

test(6)
  frequencies in [-fs/2, fs/2) : [ 0.  1.  2. -3. -2. -1.]
  frequencies in (-fs/2, fs/2] : [ 0.  1.  2.  3. -2. -1.]

Notice that the frequency fs/2 = 3 is wrapped to -3, but it shouldn't.

Theory

For an even n, a sequence of n real numbers is transformed by FFT to a sequence of n complex numbers, of which the last n/2 - 1 numbers are redundant because they are complex conjugates of corresponding number in the first part. The remaining n/2 + 1 numbers are all necessary to reconstruct the original real signal. They represent n + 2 real numbers, but the imaginary part of the first (index 0) and the last (index n/2) are always 0, so they actually represent n 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 represent n independent real numbers.

Impact

For an even n (which is the most common use case), when the FFT of a signal is plotted using fft.fftfreq(), the maximum frequency fs/2 is hidden. For a realistic sampling process, the sampled signal (e.g., audio) doesn't have frequency content at fs/2, since it has been filtered before sampling. However fft.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 by fft.fftfreq(). Notice that replacing cos by sin in the above example does not show the problem, because the signal is 0.

Reproduce the code example:

See description.

Error message:

Python and NumPy Versions:

numpy 2.3.0
python 3.13.3

Runtime Environment:

No response

Context for the issue:

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    00 - Bug57 - Close?Issues which may be closable unless discussion continued

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions