Bartlett周期图的Python实现

时间:2017-10-23 05:09:24

标签: python scipy

我试图根据Bartlett's method中的描述在Python中实现Periodogram,并将结果与​​Scipy的结果进行比较,通过设置overlap = 0,使用window ='boxcar'(矩形窗口)。但是,我的结果是一些比例因素。有人能指出我的代码有什么问题吗?感谢

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal


def my_bartlett_periodogram(x, fs, nperseg, nfft):    
    nsegments = len(x) // nperseg
    psd = np.zeros(nfft)
    for segment in x.reshape(nsegments, nperseg):
        psd += np.abs(np.fft.fft(segment))**2 / nfft
    psd[0] = 0   # important!!
    psd /= nsegments
    psd = psd[0 : nfft//2]
    freq = np.linspace(0, fs/2, nfft//2)
    return freq, psd

def plot_output(t, x, f1, psd1, f2, psd2):
    fig, axs = plt.subplots(3,1, figsize=(12,15))
    axs[0].plot(t[:300], x[:300])
    axs[1].plot(freq1, psd1)
    axs[2].plot(freq2, psd2)
    axs[0].set_title('Input (len=8192, fs=512)')
    axs[1].set_title('Bartlett Periodogram (nfft=512, zero-overlap, no-window)')
    axs[2].set_title('Scipy Periodogram (nfft=512, zero-overlap, no-window)')
    axs[0].set_xticks([])
    axs[2].set_xlabel('Freq (Hz)')
    plt.show()

# Run
fs = nfft = nperseg = 512
t = np.arange(8192) / fs
x = np.sin(2*np.pi*50*t) + np.sin(2*np.pi*100*t) + np.sin(2*np.pi*150*t)
freq1, psd1 = my_bartlett_periodogram(x, fs, nperseg, nfft)
freq2, psd2 = signal.welch(x, fs, nperseg=nperseg, nfft=nfft, window='boxcar', noverlap=0)
plot_output(t, x, freq1, psd1, freq2, psd2)

Output

1 个答案:

答案 0 :(得分:2)

TL; DR:

代码没有错。但是welch会返回功率谱密度,这是功率谱时间fs,它可以通过乘以2来补偿削减一半频谱。

要补偿,psd2 * fs / 2应与psd非常相似。

根据Wikipediapsd的计算似乎是正确的:

  
      
  1. 将原始N点数据段拆分为K(非重叠)数据段,每个数据段长度为M
  2.   
  3. 对于每个段,通过计算离散傅立叶变换(DFT版本不除以M)计算周期图,然后计算结果的平方幅度并将其除以M.
  4.   
  5. 平均上述K个数据段的周期图的结果。
  6.   

那么我们更信任谁,维基百科还是scipy?我倾向于后者,但我们可以找到自己。根据{{​​3}},平方信号上的积分应与sqared FFT幅度上的积分相同。由于周期图是从平方FFT获得的,因此该定理应保持近似。

print(np.mean(y**2))  # 1.499727698431174
print(np.mean(psd))  # (1.4999999999999991+0j)
print(np.mean(psd2))  # 0.0058365758754863788

这对psd足够接近,所以让我们假设它是正确的。但我拒绝相信scipy应该如此明显错误!让我们仔细看看Parseval's theorem,看看他们对scaling论证(强调我的)有什么看法:

  

在计算Pxx具有 V**2/Hz 单位的功率谱密度('密度')和计算功率谱('频谱')之间进行选择,其中Pxx的单位为V ** 2,如果x用V测量,fs用Hz测量。默认为'密度'

嗯! welch的结果是功率谱密度,这意味着它具有每Hz功率单位。但是,我们将它与信号功率进行了比较。如果我们将psd2与采样率相乘以除去1 / Hz单位,则它与psd相同。好吧,除了因子2.这个因素是为了补偿切掉一半的光谱。如果我们设置return_onesided=False以获得完整频谱,那么该因素就会消失。