Application of Parsevals theorem with Welch method
RMS value


  Firstly I am getting a 0Hz peak even after removing the mean at the start. The only way I can remove the 0Hz peak is to take the mean away after I have segmented and multiplied the window.


The mean doesn't have to remain at 0 when you apply a window. Changing the weight of a particular sample would shift the mean accordingly (e.g. a weight greater than 1 on a positive sample would increase the mean). If you must remove the 0Hz peak, the only guaranteed way to do so is right before the FFT (or right after the FFT, but that second option wouldn't remove the spectral leakage to adjacent bins).

Parseval's relationship


  My second issue is that I cant get the Parseval's theorem to hold. Is there something I'm doing wrong? Is it necessary for the Parseval's theory to hold? Please let me know if my theory is wrong


Since you are dealing with discrete time signals, you should be using the specific formulation of Parseval's theorem for discrete time signals. This formulation is derived directly from the definition of the Discrete Fourier Transform. It then has to hold between the input and output data to the fft call. Note however that different implementations may use different normalization which can affect the exact formulation by a constant scaling factor. With the normalization used in numpy.fft.fft, you should have the following relationship:

$$
  \sum_{n=0}^{N-1} |x_n|^2 = \frac{1}{N} \sum_{k=0}^{N-1} |X_k|^2
$$

Note however that your code currently incorrectly throws away the imaginary parts of your computed spectrum on the following line:

Pxx = np.real(fft_data)/(nseg)


You final computation should instead make use of squared-magnitudes. Since you are later taking the square of each frequency bin value, you may take the absolute value:

Pxx = np.abs(fft_data)/(nseg)


Your will also need to remove the frequency step part in your final integration of FFT_energy and stick with the straight summation given in the discrete time formulation of Parseval's theorem given above.

for i in range(0,len(Pxx)):
    FFT_energy = FFT_energy + np.power(Pxx[i],2)
FFT_energy = FFT_energy / len(Pxx)

for i in range(0,len(data)):
    data_energy = data_energy + np.power(abs(data[i]),2)