I have done my best to write Welch method implementation for python for multidimensional time series and still in the case of one dimensional time series I am getting inconsistent response compared to original Welch method. I have added comments and tried to be clear. Can anybody guide me about the mistake that I might have in implementation?
from scipy.fftpack import fft,fftfreq
import numpy as np
from math import ceil,floor
import sys
from scipy.signal import welch,get_window
from matplotlib import pyplot as plt
import warnings
from scipy.lib.six import string_types
def win_sig(x,nperseg):
"""A function just to cut a multidimensional time series into pieces of specific length (nperseg) """
#checking whether the size of time series are smaller than the window lenght
if nperseg >= x.shape[-1]:
N = nperseg
win_num = 1
elif nperseg/2 == nperseg/2.:
N = nperseg/2+1
win_num = ceil(x.shape[-1]/float(N-1))
else:
N = nperseg/2
win_num = ceil(x.shape[-1]/float(N))
#index set manipulation for generating the splitted version of the signals faster
idx_temp=np.indices((win_num,nperseg))
idx_temp=np.tile((idx_temp[0]*N+idx_temp[1]).T,x.shape[0]).T.reshape((x.shape[0],win_num,-1))
#padding zeros for the last window when the last window is longer than the remaining of signal
pad_len=(win_num-1)*N+nperseg-x.shape[-1]
idx_mat=np.arange(0,x.shape[0])[...,None,None]*(idx_temp.flatten()[-1]+1)+idx_temp
pad_mat=np.zeros((x.shape[0],pad_len))
x=np.concatenate((x,pad_mat),axis=-1)
return x.flatten()[idx_mat].reshape((x.shape[0],win_num,-1))
def ndim_welch(x,nperseg,window = 'hanning',scaling = 'density',fs = 1.0,axis = -1):
if x.shape[-1] < nperseg:
warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
'nperseg = x.shape[%d]'
% (nperseg, axis, x.shape[axis], axis))
nperseg = x.shape[-1]
#setting the window as is done in original scipy.signal
if isinstance(window, string_types) or type(window) is tuple:
win = get_window(window, nperseg)
else:
win = np.asarray(window)
if len(win.shape) != 1:
raise ValueError('window must be 1-D')
if win.shape[0] > x.shape[-1]:
raise ValueError('window is longer than x.')
nperseg = win.shape[0]
#setting the scale as is done in original scipy.signal
if scaling == 'density':
scale = 1.0 / (fs * (win*win).sum())
elif scaling == 'spectrum':
scale = 1.0 / win.sum()**2
else:
raise ValueError('Unknown scaling: %r' % scaling)
#turning the multidimensional time series into multiple time series of windowed sections using the function 'win_seg'
windowed_sig = win_sig(x,nperseg)
windowed_sig = np.multiply(win,windowed_sig)
#calculating the fourier transform
windowed_seg_fft = fft(windowed_sig)
windowed_fft = np.mean(windowed_seg_fft,axis=1).T
#returning the spectral density with calcualting outerproducts to get the crossspectrum matrix and also returning the frequenct set
spec_density = np.einsum('...i,...j->...ij',windowed_fft,windowed_fft.conjugate())
spec_density *= scale
spec_freq = fftfreq(nperseg)
return spec_freq,np.squeeze(spec_density)
b=np.random.randn(5)
segment = 5
a = (b)[None,...]
print ndim_welch(a,nperseg=segment)[1].real,welch(b,nperseg=segment)[1]