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:
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?
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:
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:
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.