pythonnumpymatplotlibfft

How do I turn-off this horizontal line in the FFT plot


I am trying to show the trend in observation data and the corresponding fft . A horizontal line keeps appearing in the fft, part that I do not need to show in the chart.

I give a MWE (pseudo data) below.

import numpy as np
from scipy.fft import fft, fftfreq
import matplotlib.pyplot as plt

observations = np.array([ 3.78998207e-01,  3.05199629e-01,  2.29614343e-01,  1.86568613e-01,
                         1.83449462e-01,  1.77892746e-01,  1.66237352e-01,  1.81950778e-01,
                         9.88351226e-02,  1.29674430e-01,  7.08703360e-02,  3.64963487e-02,
                         2.75641060e-02,  6.21573753e-02,  8.51043646e-02,  5.32184940e-02,
                         6.47005530e-02, -6.41628893e-02, -1.86618020e-01, -4.08624200e-02,
                        -2.71649960e-02, -8.22041576e-03,  9.13242105e-03,  1.67080717e-01,
                        -1.37465317e-01,  2.74977101e-04,  4.47602122e-02,  8.27649668e-02,
                        -5.60661808e-02, -2.26248880e-01, -1.54768403e-01, -4.46428484e-02,
                        4.57611677e-02,  9.83215698e-02,  9.22357256e-02, -1.23436114e-01,
                      -2.76981909e-01, -1.98824586e-01, -2.33452893e-01, -2.57550630e-01,
                      -9.13919527e-02,  2.64029442e-02, -5.44394568e-02,  4.02010984e-01,
                        3.27256645e-01,  2.14259077e-01,  5.08021357e-01,  5.55141121e-01,
                        6.11203693e-01,  5.34086779e-01,  2.19652659e-01,  1.71635054e-01,
                        1.30867565e-01,  1.25133212e-01,  1.02010973e-01,  1.16727950e-02,
                        2.84545455e-02, -1.73553706e-02, -1.33998184e-01, -1.36456573e-01,
                      -1.68706794e-01, -1.28378379e-01, -1.43710423e-01, -2.02454545e-01,
                      -4.30164457e-01, -5.19982175e-01, -3.74452537e-01, -3.64076796e-01,
                      -3.20950700e-01, -2.34052515e-01, -1.37158482e-01,  2.80797054e-02,
                        7.04379682e-02,  1.13920696e-01,  1.26391389e-01,  9.31688808e-02,
                        1.46000000e-01,  1.18380338e-01,  5.18909438e-02,  1.11584791e-01,
                        6.43582617e-02, -6.36856386e-02, -9.16134931e-02, -1.02616820e-01,
                      -4.43179890e-01, -1.28223431e+00, -1.86160058e+00, -1.43772912e+00,
                      -1.21047880e+00, -7.21282278e-01, -1.65349241e-01,  4.58791266e-02,
                        2.42897190e-01,  3.26587994e-01,  3.15827382e-01, -5.29090909e-02,
                        8.97887313e-03,  2.61194000e-02, -2.24566234e-01, -9.18572710e-02])

observed_fft = fft(observations)

fs = 100
n = observations.size
fft_fre = fftfreq(n, d=1/fs)
x_time = np.arange(len(observations))

fig, axs = plt.subplots(2)
axs[0].plot(x_time, observations)
axs[1].plot(fft_fre, np.abs(observed_fft))

Output:

enter image description here

I know this horizontal line appears because the FFT frequencies in fft_fre include both positive and negative frequencies, thus a symmetrical plot around zero frequency (and I needed to show both the +ve and ve frequencies). But isn't there a workaround to turn-off the line connecting the last negative frequency to the first positive frequency?


Solution

  • Yes, there is a workaround.

    Shift the FFT Output (This makes it easier to deal with the data in sequential manner):

    fft_fre_shifted = fftshift(fft_fre)
    fft_magnitude_shifted = np.abs(fftshift(observed_fft))
    

    Then determine the midpoint:

    if n % 2 == 0:
        midpoint = n // 2
    else:
        midpoint = (n // 2) + 1
    

    Then insert a Nan at the midpoint to break the data so the first positive and last negative frequency are not connected:

    fft_fre_plot = np.insert(fft_fre_shifted, midpoint, np.nan)
    fft_magnitude_plot = np.insert(fft_magnitude_shifted, midpoint, np.nan)
    

    You should get this:

    New plot without horizontal line

    If adding the extra element to the array is an issue then you can plot the positive and negative frequency arrays separately as follows:

    # Separate negative and positive frequencies
    neg_freqs = fft_fre_shifted[:midpoint]
    neg_magnitude = fft_magnitude_shifted[:midpoint]
    
    pos_freqs = fft_fre_shifted[midpoint:]
    pos_magnitude = fft_magnitude_shifted[midpoint:]
    

    Then plot as:

    # FFT plot without connecting negative and positive frequencies
    axs[1].plot(neg_freqs, neg_magnitude, color='green', label='Negative Frequencies')
    axs[1].plot(pos_freqs, pos_magnitude, color='red', label='Positive Frequencies')
    axs[1].set_title('FFT Magnitude Spectrum')
    axs[1].set_xlabel('Frequency (Hz)')
    axs[1].set_ylabel('Magnitude')
    axs[1].legend()
    axs[1].grid(True)
    

    To achieve this:

    Separate plotting for negative and positive

    Then you can continue with the positive and negative arrays unaffected by the NaN. the green and red is just for explaining, you can make them both the same colour as you like.