spectral_analysis_1
データファイル:eeg_bis_10_20_2.tsv
JupyterNotebook:spectral_analysis_1.ipynb
窓関数とマルチテーパ法
パワースペクトル(またはパワースペクトル密度の場合はPSD)として知られる、周波数関数としての信号内のパワー分布は、離散フーリエ変換(DFT)を使用して推定する。高速フーリエ変換アルゴリズム(FFT)を使用したパワースペクトルの単純な推定は、ピリオドグラムと呼ばれる。
ナイーブなピリドグラムによる推定では、解析に用いる時間制限信号内のすべてのサンプルがそのまま取得され(暗黙的に1倍)、この時間制限信号外のすべてのサンプルは無視される(暗黙的に0が乗算)。これは、信号が矩形窓(ボックスカー)ウィンドウでサンプルごとに乗算された場合に発生することと同様である。時間領域で信号をボックスカーウィンドウで乗算することは、周波数ドメインでボックスカーウィンドウのスペクトルで信号を解析することによる「スペクトル漏れ」を分析しながら、窓関数やマルチテーパ法の周波数特性について確認する。
・まずは、解析に必要となる以下のモジュール/関数をインポートする。
stats modelsは統計解析ライブラリ
nitimeは脳科学イメージングライブラリ
scipyは科学技術計算ライブラリ
matplotlibはグラフィックライブラリ
numpyは数学演算ライブラリー
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#統計ライブラリStatsModels: 自己回帰シークエンス導入のため from statsmodels.graphics.tsaplots import plot_pacf from statsmodels.graphics.tsaplots import plot_acf from statsmodels.tsa.arima_process import ArmaProcess from statsmodels.tsa.stattools import pacf from statsmodels.regression.linear_model import yule_walker from statsmodels.tsa.stattools import adfuller #脳神経科学ライブラリnitime: 自己回帰シークエンス周波数算定 from nitime.viz import plot_spectral_estimate import nitime.algorithms as tsa #科学演算ライブラリ from scipy.fftpack import fft, fftshift import scipy.signal as sig import scipy.stats.distributions as dist #グラフィックライブラリ import matplotlib.pyplot as plt #数学演算ライブラリ import numpy as np #SVGをファイル保存するためのライブラリ from IPython.display import set_matplotlib_formats set_matplotlib_formats('svg') %matplotlib inline |
出力図をSVGフォーマットでファイル保存するために、以下のコードを実行する。
1 2 |
from IPython.display import set_matplotlib_formats set_matplotlib_formats('svg') |
・時系列データ数128のうち、zero_fracで指定した比率で両端が0、それ以外は1となる矩形窓(ボックスカー窓)関数boxcar_windowを作成する。
周波数解析における窓関数の役割を理解するために、まず窓を明示的に用いない(=矩形窓)が
矩形窓(=窓なし)関数を作成し、矩形波を描く
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
time_point = 128 freq = np.linspace(0, 64, time_point) # 矩形窓(Boxcar window) boxcar_window = sig.boxcar(time_point) zero_frac = 0.15 #全体の時間に対するゼロ応答の割合 zeroed = int(time_point * zero_frac) boxcar_window[:zeroed] = boxcar_window[-zeroed:] = 0 label = 'Boxcar window - zero fraction=%.2f' % zero_frac plt.plot(boxcar_window, linewidth =0.5, label="Boxcar window") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig1.svg') |
・Scipyライブラリのfftpackのfft()関数と、ゼロ周波数を中心にシフトさせて相対的な周波数に対するパワースペクトルを求めるfftshift()関数を使って矩形窓(ボックスカー窓)自体のPSDを算定する。
1 2 3 4 5 6 7 8 9 10 |
Box = fft(boxcar_window, time_point) / (len(boxcar_window)/2.0) freq2 = np.linspace(-0.5, 0.5, len(Box)) response_box = 20 * np.log10(np.abs(fftshift(Box / abs(Box).max()))) plt.plot(freq2, response_box, linewidth =0.5, label="box window") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) #plt.yscale("log") #plt.xlim(0, 64) plt.grid() plt.savefig('spectral_analysis_fig2.svg') |
・Scipyライブラリのsignalモジュールを利用して、Hann, Hamming, Blackman窓を取得する。Hann, Blackmanはゼロで始まり、ゼロで終わる。Hammingはゼロに近づくがゼロではない特徴を持つ。
1 2 3 4 5 6 7 8 9 10 |
window1 = sig.hann(128) window2 = sig.hamming(128) window3 = sig.blackman(128) plt.plot(freq, boxcar_window, linewidth =0.5, label="box window") plt.plot(freq, window1, linewidth =0.5, label="Hann window") plt.plot(freq, window2, linewidth =0.5, label="Hamming window") plt.plot(freq, window3, linewidth =0.5, label="Blackman window") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig3.svg') |
・窓関数自体の周波数解析を行いPSDを求めて、矩形窓(=窓なし ボックスカー)と比較する。中心の周波数の両翼の周波数が抑制されることが、広いダイナミックレンジの構築に役立つことが理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Box = fft(boxcar_window, time_point) / (len(boxcar_window)/2.0) response_box = 20 * np.log10(np.abs(fftshift(Box / abs(Box).max()))) Win_hann = fft(window1, time_point) / (len(window1)/2.0) response_hann = 20 * np.log10(np.abs(fftshift(Win_hann / abs(Win_hann).max()))) Win_hamming = fft(window2, time_point) / (len(window2)/2.0) response_hamming = 20 * np.log10(np.abs(fftshift(Win_hamming / abs(Win_hamming).max()))) Win_blackman = fft(window3, time_point) / (len(window3)/2.0) response_blackman = 20 * np.log10(np.abs(fftshift(Win_blackman / abs(Win_blackman).max()))) plt.plot(freq2, response_box, linewidth =0.5, label="box window") plt.plot(freq2, response_hann, linewidth =0.5, label="Hann window") plt.plot(freq2, response_hamming, linewidth =0.5, label="Hamming window") plt.plot(freq2, response_blackman, linewidth =0.5, label="Blackman window") plt.legend(bbox_to_anchor=(1, 1), loc='lower right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.grid() plt.savefig('spectral_analysis_fig3_2.svg') |
・中心周波数部分を拡大してみることで、周波数解像度に影響を与える中心周波数周辺のスペクトルの幅(漏れ)を理解する。Blackman窓では、就寝周波数の幅はHann窓やHamming窓と比べて広く、その両翼の周波数は低い。つまり周波数の解像度を犠牲にして、ダイナミックレンジを広げていることが解る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Box = fft(boxcar_window, time_point) / (len(boxcar_window)/2.0) response_box = 20 * np.log10(np.abs(fftshift(Box / abs(Box).max()))) Win_hann = fft(window1, time_point) / (len(window1)/2.0) response_hann = 20 * np.log10(np.abs(fftshift(Win_hann / abs(Win_hann).max()))) Win_hamming = fft(window2, time_point) / (len(window2)/2.0) response_hamming = 20 * np.log10(np.abs(fftshift(Win_hamming / abs(Win_hamming).max()))) Win_blackman = fft(window3, time_point) / (len(window3)/2.0) response_blackman = 20 * np.log10(np.abs(fftshift(Win_blackman / abs(Win_blackman).max()))) plt.plot(freq2, response_box, linewidth =0.5, label="box window") plt.plot(freq2, response_hann, linewidth =0.5, label="Hann window") plt.plot(freq2, response_hamming, linewidth =0.5, label="Hamming window") plt.plot(freq2, response_blackman, linewidth =0.5, label="Blackman window") plt.legend(bbox_to_anchor=(1, 1), loc='lower right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.xlim(-0.05, 0.05) plt.ylim(-100, 5) plt.grid() plt.savefig('spectral_analysis_fig3_3.svg') |
マルチテーパ法による周波数解析
・マルチテーパ法による周波数解析について理解を進める。マルチテーパ法ライブラリmtspecのdpss()を導入する。 128データ数で、5つのSlepianシークエンスを求める。
1 2 3 4 5 6 |
from mtspec import dpss tapers, lamb, theta = dpss(128, 2.5, 5) for i in range(5): plt.plot(tapers[:, i]) plt.xlim(0, 128) plt.savefig('spectral_analysis_fig4.svg') |
・上記Slepian関数を矩形窓(ボックスカー)と同じスケールでグラフ化してみる。
1 2 3 4 5 6 7 8 |
plt.plot(boxcar_window, linewidth =0.5, label="box window") plt.plot(tapers[:, 0], linewidth =0.5, label="taper 1") plt.plot(tapers[:, 1], linewidth =0.5, label="taper 2") plt.plot(tapers[:, 2], linewidth =0.5, label="taper 3") plt.plot(tapers[:, 3], linewidth =0.5, label="taper 4") plt.plot(tapers[:, 4], linewidth =0.5, label="taper 5") plt.xlim(0, 128) plt.savefig('spectral_analysis_fig5.svg') |
・各Slepian関数の周波数解析を行い、矩形波のそれとPSDを比較する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
spec_mt1 = fft(tapers[:, 0], time_point) / (len(tapers[:, 0])/2.0) response_mt1 = 20 * np.log10(np.abs(fftshift(spec_mt1 / abs(spec_mt1).max()))) spec_mt2 = fft(tapers[:, 1], time_point) / (len(tapers[:, 1])/2.0) response_mt2 = 20 * np.log10(np.abs(fftshift(spec_mt2 / abs(spec_mt2).max()))) spec_mt3 = fft(tapers[:, 2], time_point) / (len(tapers[:, 2])/2.0) response_mt3 = 20 * np.log10(np.abs(fftshift(spec_mt3 / abs(spec_mt3).max()))) spec_mt4 = fft(tapers[:, 3], time_point) / (len(tapers[:, 3])/2.0) response_mt4 = 20 * np.log10(np.abs(fftshift(spec_mt4 / abs(spec_mt4).max()))) spec_mt5 = fft(tapers[:, 4], time_point) / (len(tapers[:, 4])/2.0) response_mt5 = 20 * np.log10(np.abs(fftshift(spec_mt5 / abs(spec_mt5).max()))) plt.plot(freq2, response_box, linewidth =0.5, label="box window") plt.plot(freq2, response_mt1, linewidth =0.5, label="taper 1") plt.plot(freq2, response_mt2, linewidth =0.5, label="taper 2") plt.plot(freq2, response_mt3, linewidth =0.5, label="taper 3") plt.plot(freq2, response_mt4, linewidth =0.5, label="taper 4") plt.plot(freq2, response_mt5, linewidth =0.5, label="taper 5") plt.legend(bbox_to_anchor=(1, 1), loc='lower right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.grid() plt.savefig('spectral_analysis_fig6.svg') |
・上記の中心部分を拡大すると、各Slepianの特性とマルチテーパという複数のテーパ関数の組み合わせにより、周辺エッジ部のスペクトルも補完されることが理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
spec_mt1 = fft(tapers[:, 0], time_point) / (len(tapers[:, 0])/2.0) response_mt1 = 20 * np.log10(np.abs(fftshift(spec_mt1 / abs(spec_mt1).max()))) spec_mt2 = fft(tapers[:, 1], time_point) / (len(tapers[:, 1])/2.0) response_mt2 = 20 * np.log10(np.abs(fftshift(spec_mt2 / abs(spec_mt2).max()))) spec_mt3 = fft(tapers[:, 2], time_point) / (len(tapers[:, 2])/2.0) response_mt3 = 20 * np.log10(np.abs(fftshift(spec_mt3 / abs(spec_mt3).max()))) spec_mt4 = fft(tapers[:, 3], time_point) / (len(tapers[:, 3])/2.0) response_mt4 = 20 * np.log10(np.abs(fftshift(spec_mt4 / abs(spec_mt4).max()))) spec_mt5 = fft(tapers[:, 4], time_point) / (len(tapers[:, 4])/2.0) response_mt5 = 20 * np.log10(np.abs(fftshift(spec_mt5 / abs(spec_mt5).max()))) plt.plot(freq2, response_box, linewidth =0.5, label="box window") plt.plot(freq2, response_mt1, linewidth =0.5, label="taper 1") plt.plot(freq2, response_mt2, linewidth =0.5, label="taper 2") plt.plot(freq2, response_mt3, linewidth =0.5, label="taper 3") plt.plot(freq2, response_mt4, linewidth =0.5, label="taper 4") plt.plot(freq2, response_mt5, linewidth =0.5, label="taper 5") plt.legend(bbox_to_anchor=(1, 1), loc='lower right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.xlim(-0.05, 0.05) plt.ylim(-100, 5) plt.grid() plt.savefig('spectral_analysis_fig6R.svg') |
自己回帰シークエンス由来の波形解析によるパワースペクトル解析
・自己回帰シークエンスにより真のパワースペクトル密度と、様々な解析方法による影響とを比較することで、利点、欠点を知ることができる。 ・dB変換する関数dB()を自作導入する。(10ではなくて20を乗じているのは、FFTで無視される波形のマイナス部分のパワーを算定するために2倍している)
1 2 3 4 5 6 |
def dB(x, out=None): if out is None: return 20 * np.log10(x) else: np.log10(x, out) np.multiply(out, 20, out) |
・自己回帰移動平均モデル y_t= 1+ -2.7607 * y_1 + 3.8106 * y_2 + -2.6535* y_3 + 0.9238** y_4 を導入する。
1 2 3 4 5 |
N=512 ar4 = np.array([1, -2.76, 3.8, -2.65, 0.92]) ma = np.array([1]) simulated_data = ArmaProcess(ar4, ma).generate_sample(nsample=N) |
・自己回帰移動平均モデルで生成された人工の波形を描画してみる。
1 2 3 4 |
fig, ax = plt.subplots(figsize=(8,4)) plt.plot(simulated_data) plt.title("Simulated Autoregression Process") plt.savefig('spectral_analysis_fig7.svg') |
・この人工波形の真のスペクトルをnitimeライブラリのfreq_response()関数で求める。結果は、複素数フーリエ変換式に基づいて、複素数で返される。そのためパワーを求めるために絶対値変換を行い、実数としてのパワーを求める。
1 2 3 |
fgrid, hz = tsa.freq_response(1.0, a=ar4, n_freqs=N) psd = (hz * hz.conj()).real dB(psd, psd) |
・自己回帰シークエンスの波形自体をnitimeライブラリのナイーブピリオドグラム関数periodogram()でピリオドグラムPSDを求めて、真のスペクトルと比較する。40Hz以上の領域でスペクトルのズレが確認できた。いわゆる矩形窓のエッジの不自然な処理からこの漏れが発生していると考えられる。
1 2 3 4 5 6 7 8 9 10 11 |
freq = np.linspace(0, 64, 257) freqs, d_psd = tsa.periodogram(simulated_data) dB(d_psd, d_psd) plt.plot(freq, psd, linewidth =0.5, label="True spectrum") plt.plot(freq, d_psd, linewidth =0.5, label="periodogram") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.ylim(-90, 90) plt.grid() plt.savefig('spectral_analysis_fig8.svg') |
・上記方法に、Hann窓関数を適応させてみる。前述の40Hz以上のズレが補正されてなくなることが解る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
window_n = np.hanning(N) acf = 1/(sum(window_n)/N) #Amplitude Correction Factorを計算 print(acf) freqs, d_psd_hann = tsa.periodogram(simulated_data*window_n) dB(acf*d_psd_hann, d_psd_hann) plt.plot(freq, psd, linewidth =0.5, label="True spectrum") #plt.plot(freq, d_psd, linewidth =0.5, label="periodogram") plt.plot(freq, d_psd_hann, linewidth =0.5, label="Hann") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.ylim(-90, 90) plt.grid() plt.savefig('spectral_analysis_fig9.svg') |
・Nitimeのmulti_taper_psd()関数を利用して、マルチテーパ方によるPSDを求める。得られたPSDは、真のPSDに近づくことが理解できる。
In [35]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
freq_mt, psd_mt, nu = tsa.multi_taper_psd( simulated_data, adaptive=False, jackknife=False ) dB(psd_mt, psd_mt) plt.plot(freq, psd, linewidth =0.5, label="True spectrum") #plt.plot(freq, d_psd, linewidth =0.5, label="periodogram") #plt.plot(freq, d_psd_hann, linewidth =0.5, label="Hann") plt.plot(freq, psd_mt, linewidth =0.5, label="multitaper") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.ylim(-90, 90) plt.grid() plt.savefig('spectral_analysis_fig10.svg') |
・χ二乗モデルによる95%信頼区間2 * Kmaxの自由度を計算します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
ln2db = dB(np.e) Kmax = nu[0] / 2 p975 = dist.chi2.ppf(.975, 2 * Kmax) p025 = dist.chi2.ppf(.025, 2 * Kmax) l1 = ln2db * np.log(2 * Kmax / p975) l2 = ln2db * np.log(2 * Kmax / p025) plt.plot(freq, psd, linewidth =0.5, label="True spectrum") plt.plot(freq, psd_mt, linewidth =0.5, label="multitaper") plt.fill_between(freq, psd_mt-l2, psd_mt+l2, alpha=.2) plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.ylim(-90, 90) plt.grid() plt.title("MT with :math:`\chi^{2}` 95% interval") plt.savefig('spectral_analysis_fig11.svg') |
・反復法([Thomson2007])を使用することで、特定の信号の実際のスペクトル濃度に応じて、さまざまなテーパーの重みを適応的に設定できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
freq_mt2, adaptive_psd_mt, nu = tsa.multi_taper_psd( simulated_data, adaptive=True, jackknife=False ) dB(adaptive_psd_mt, adaptive_psd_mt) p975 = dist.chi2.ppf(.975, nu) p025 = dist.chi2.ppf(.025, nu) l1 = ln2db * np.log(nu / p975) l2 = ln2db * np.log(nu / p025) plt.plot(freq, psd, linewidth =0.5, label="True spectrum") plt.plot(freq, psd_mt, linewidth =0.5, label="multitaper") plt.fill_between(freq, adaptive_psd_mt-l2, adaptive_psd_mt+l2, alpha=.2) plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.ylim(-90, 90) plt.grid() plt.title("MT with adaptive weighting and 95% interval") plt.savefig('spectral_analysis_fig12.svg') |
上で述べたように、スペクトル自体の推定に加えて、スペクトルの信頼区間の推定は、ジャックナイフ法を使用して生成できます。 関数内でオプション指定 jackknife=Trueとする。
ジャックナイフ法(リーブワンアウト測定のことで、分布内のすべてのサンプル(すべてのテーパースペクトル)から平均化されたパラメーター推定値に対して、 1つの測定値(1つのテーパースペクトル)を除外することから得られる:
simple sample estimate θ? =1/n?iYi (分布内のすべてのサンプル(すべてのテーパースペクトル)から平均化されたパラメーター推定値)
リーブワンアウト測定 θ?−i=1/(n−1)?k≠i Yk (これは推定値のグループを定義し、各サンプルは1つの測定値(1つのテーパースペクトル)を除外) pseudovalues θ?_i=nθ? −(n−1)θ?−i
ジャックナイフ推定量は次のように計算。 θ? =1/n?iθ?_i = nθ? −(n−1)/n ?iθ?−i
この推定量に関して、真のパラメータθについてほぼスチューデントのt分布として分布し、標準誤差は次のように定義される。 s^2=(n−1)/n?i(θ?_i−θ?)^2
そして、使用されるテーパーの数に依存する自由度:Kmax-1
1 2 3 4 5 6 7 8 9 10 11 12 13 |
_, _, jk_var = tsa.multi_taper_psd(simulated_data, adaptive=False, jackknife=True) jk_p = (dist.t.ppf(.975, Kmax - 1) * np.sqrt(jk_var)) * ln2db plt.plot(freq, psd, linewidth =0.5, label="True spectrum") plt.plot(freq, psd_mt, linewidth =0.5, label="multitaper") plt.fill_between(freq, adaptive_psd_mt-jk_p, adaptive_psd_mt+jk_p, alpha=.2) plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.ylim(-90, 90) plt.grid() plt.title("MT with JK 95% interval") plt.savefig('spectral_analysis_fig13.svg') |
・さらに、「適応」フラグがTrueに設定されている場合、スペクトルのバイアスを修正するために反復適応法を使用する。そして、重みの適応推定とジャックナイフ現象を組み合わせる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
_, _, adaptive_jk_var = tsa.multi_taper_psd( simulated_data, adaptive=True, jackknife=True ) # find 95% confidence limits from inverse of t-dist CDF jk_p = (dist.t.ppf(.975, Kmax - 1) * np.sqrt(adaptive_jk_var)) * ln2db plt.plot(freq, psd, linewidth =0.5, label="True spectrum") plt.plot(freq, psd_mt, linewidth =0.5, label="multitaper") plt.fill_between(freq, adaptive_psd_mt-jk_p, adaptive_psd_mt+jk_p, alpha=.2) plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) #plt.yscale("log") plt.ylim(-90, 90) plt.grid() plt.title("adaptive-MT with JK 95% interval") plt.savefig('spectral_analysis_fig14.svg') |
サイン波、コサイン波を用いた周波数解析の特徴理解
・5Hzの周期を持つ256データポイントのサイン波、コサイン波を作成する。
1 2 3 |
t = np.linspace(0, 2, 256) data_sin = np.sin(2*np.pi*5*t) data_cos = np.cos(2*np.pi*5*t) |
・サイン波を描画:ゼロで始まりゼロで終わる。
1 2 |
plt.plot(t, data_sin) plt.savefig('spectral_analysis_fig15.svg') |
・コサイン波を描画:1で始まり、1で終わる。
1 2 |
plt.plot(t, data_cos) plt.savefig('spectral_analysis_fig16.svg') |
・上記サイン・コサイン波の周波数解析でSPDを求める。両者は重なってほとんど差が無いように一見みえる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
N=128 A0_sin = fft(data_sin, 256) / (len(data_sin)/2.0) A0_cos = fft(data_cos, 256) / (len(data_cos)/2.0) freq = np.linspace(0, 128, 256) #freqs_data, psd_data = tsa.periodogram(data) fft_data_sin = abs(A0_sin) fft_data_cos = abs(A0_cos) plt.plot(freq, fft_data_sin, label="sin") plt.plot(freq, fft_data_cos, label="cos", alpha= 0.6) plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.axis([0, 64, -0.05, 1.1]) #plt.plot(freqs_data, psd_data) plt.savefig('spectral_analysis_fig17.svg') |
1 2 3 4 5 6 |
plt.plot(freq, fft_data_sin, label="sin") plt.plot(freq, fft_data_cos, label="cos") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.axis([0, 10, -0.05, 1.1]) #plt.plot(freqs_data, psd_data) plt.savefig('spectral_analysis_fig18.svg') |
・デシベル変換して、表示させてみると、両者の中心周波数外の差が理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 |
A0_dB_sin = 20 * np.log10(np.abs(A0_sin / abs(A0_sin).max())) A0_dB_cos = 20 * np.log10(np.abs(A0_cos/ abs(A0_cos).max())) plt.plot(freq, A0_dB_sin, label="sin") plt.plot(freq, A0_dB_cos, label="cos") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.axis([0, 10, -200.0, 0]) plt.title("Frequency response") plt.ylabel("Magnitude [uV^2]") plt.xlabel("Normalized frequency [cycles per sec]") plt.savefig('spectral_analysis_fig19.svg') |
・次に前述の以下の解析窓を導入してみる。まずは窓関数をグラフ表示させる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
window1 = sig.hann(256) window2 = sig.hamming(256) window3 = sig.blackman(256) plt.plot(window1, label="Hann") plt.plot(window2, label="Hamming") plt.plot(window3, label="Blackman") plt.title("windows") plt.ylabel("Amplitude") plt.xlabel("Sample") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig20.svg') |
・窓関数とマルチテーパ法をサイン波形にかけ合わせて、FFTを行いPSDを求める。窓関数により、中心周波数(5Hz)以外の領域の周波数パワーが抑制されることが理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
A0_sin = fft(data_sin, 256) / (len(window1)/2.0) freq = np.linspace(0, 128, len(A0_sin)) response_sin = 20 * np.log10(np.abs((A0_sin / abs(A0_sin).max()))) A0_1_sin = fft(window1*data_sin, 256) / (len(window1)/2.0) response1_sin = 20 * np.log10(np.abs((A0_1_sin / abs(A0_1_sin).max()))) A0_2_sin = fft(window2*data_sin, 256) / (len(window2)/2.0) response2_sin = 20 * np.log10(np.abs((A0_2_sin / abs(A0_2_sin).max()))) A0_3_sin = fft(window3*data_sin, 256) / (len(window3)/2.0) response3_sin = 20 * np.log10(np.abs((A0_3_sin / abs(A0_3_sin).max()))) freq_mt3_sin, psd_mt3_sin, nu = tsa.multi_taper_psd( data_sin, Fs=128, adaptive=False, jackknife=False, sides='twosided') response4_sin = 20 * np.log10(np.abs((psd_mt3_sin / abs(psd_mt3_sin).max()))) plt.plot(freq, response_sin, label="none") plt.plot(freq, response1_sin, label="Hann") plt.plot(freq, response2_sin, label="Hamming") plt.plot(freq, response3_sin, label="Blackman") plt.plot(freq, response4_sin, label="multitaper") plt.axis([-1, 64, -200, 10]) plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig21.svg') |
・窓関数とマルチテーパ法をコサイン波形にかけ合わせて、FFTを行いPSDを求める。窓関数により、中心周波数(5Hz)以外の領域の周波数パワーが抑制されることが理解できる。また上記サイン波とは異なることも解る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
A0_cos = fft(data_cos, 256) / (len(window1)/2.0) freq = np.linspace(0, 128, len(A0_cos)) response_cos = 20 * np.log10(np.abs((A0_cos / abs(A0_cos).max()))) A0_1_cos = fft(window1*data_cos, 256) / (len(window1)/2.0) response1_cos = 20 * np.log10(np.abs((A0_1_cos / abs(A0_1_cos).max()))) A0_2_cos = fft(window2*data_cos, 256) / (len(window2)/2.0) response2_cos = 20 * np.log10(np.abs((A0_2_cos / abs(A0_2_cos).max()))) A0_3_cos = fft(window3*data_cos, 256) / (len(window3)/2.0) response3_cos = 20 * np.log10(np.abs((A0_3_cos / abs(A0_3_cos).max()))) freq_mt3_cos, psd_mt3_cos, nu = tsa.multi_taper_psd( data_cos, Fs=128, adaptive=False, jackknife=False, sides='twosided') response4_cos = 20 * np.log10(np.abs((psd_mt3_cos / abs(psd_mt3_cos).max()))) plt.plot(freq, response_cos, label="none", alpha=1.0) plt.plot(freq, response1_cos, label="Hann", alpha=1.0) plt.plot(freq, response2_cos, label="Hamming", alpha=1.0) plt.plot(freq, response3_cos, label="Blackman", alpha=1.0) plt.plot(freq_mt3_cos, response4_cos, label="multitaper", alpha=1.0) |
・サイン波とコサイン波のPSDの違いを把握するため、単純なピリオドグラムによるPSDだけを比較してみる。
1 2 3 4 5 6 |
plt.axis([-1, 64, -200, 10]) plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1,1), loc='upper right', borderaxespad=2, fontsize=12) plt.savefig('spectral_analysis_fig22.svg') |
1 2 3 4 5 |
plt.plot(freq, response_sin, label="sin") plt.plot(freq, response_cos, label="cos") plt.axis([0, 50, -200.0, 0]) plt.legend(bbox_to_anchor=(1,1), loc='upper right', borderaxespad=2, fontsize=12) |
・サイン波PSDの5Hzの中心周波数部分を拡大表示してみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
freq = np.linspace(0, 128, 256) A0_sin = fft(data_sin, 256) / (len(data_sin)/2.0) window1 = sig.hann(256) A0_1_sin = fft(window1*data_sin, 256) / (len(data_sin)/2.0) window2 = sig.hamming(256) A0_2_sin = fft(window2*data_sin, 256) / (len(data_sin)/2.0) window3 = sig.blackman(256) A0_3_sin = fft(window3*data_sin, 256) / (len(data_sin)/2.0) freq_mt3_sin, psd_mt3_sin, nu = tsa.multi_taper_psd( data_sin, Fs=128, adaptive=False, jackknife=False) plt.figure() plt.axis([0, 10, -0.2, 1.2]) plt.plot(freq, abs(A0_sin), label="none") plt.plot(freq, abs(A0_1_sin), label="Hann") plt.plot(freq, abs(A0_2_sin), label="Hamming") plt.plot(freq, abs(A0_3_sin), label="Blackman") plt.plot(freq_mt3_sin, psd_mt3_sin, label="multitaper") plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [¨?V]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig23.svg') |
・コサイン波PSDの5Hzの中心周波数部分を拡大表示してみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
freq = np.linspace(0, 128, 256) A0_cos = fft(data_cos, 256) / (len(data_sin)/2.0) window1 = sig.hann(256) A0_1_cos = fft(window1*data_cos, 256) / (len(data_cos)/2.0) window2 = sig.hamming(256) A0_2_cos = fft(window2*data_cos, 256) / (len(data_cos)/2.0) window3 = sig.blackman(256) A0_3_cos = fft(window3*data_cos, 256) / (len(data_cos)/2.0) freq_mt3_cos, psd_mt3_cos, nu = tsa.multi_taper_psd( data_cos, Fs=128, adaptive=False, jackknife=False) plt.figure() plt.axis([0, 10, -0.2, 1.2]) plt.plot(freq, abs(A0_cos), label="none") plt.plot(freq, abs(A0_1_cos), label="Hann") plt.plot(freq, abs(A0_2_cos), label="Hamming") plt.plot(freq, abs(A0_3_cos), label="Blackman") plt.plot(freq_mt3_cos, psd_mt3_cos, label="multitaper") plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [¨?V]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig24.svg') |
・サイン波PSDの5Hzの中心周波数部分を拡大表示をデジベル変換で表示してみる。マルチテーパ法が中心周波数の解像度を犠牲にしていることが理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
A0_sin = fft(data_sin, 256) / (len(data_sin)/2.0) response_sin = 20 * np.log10(np.abs((A0_sin / abs(A0_sin).max()))) window1 = sig.hann(256) A0_1_sin = fft(window1*data_sin, 256) / (len(window1)/2.0) response1_sin = 20 * np.log10(np.abs((A0_1_sin / abs(A0_1_sin).max()))) window2 = sig.hamming(256) A0_2_sin = fft(window2*data_sin, 256) / (len(window2)/2.0) response2_sin = 20 * np.log10(np.abs((A0_2_sin / abs(A0_2_sin).max()))) window3 = sig.blackman(256) A0_3_sin = fft(window3*data_sin, 256) / (len(window3)/2.0) response3_sin = 20 * np.log10(np.abs((A0_3_sin / abs(A0_3_sin).max()))) freq_mt3_sin, psd_mt2_sin, nu = tsa.multi_taper_psd( data_sin, Fs=128, adaptive=False, jackknife=False) response4_sin = 20 * np.log10(np.abs((psd_mt2_sin / abs(psd_mt2_sin).max()))) plt.axis([0, 10, -120, 20]) plt.plot(freq, response_sin, label="none") plt.plot(freq, response1_sin, label="Han") plt.plot(freq, response2_sin, label="Hamming") plt.plot(freq, response3_sin, label="Blackman") plt.plot(freq_mt3_sin, response4_sin, label="multitaper") plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig25.svg') |
・コサイン波PSDの5Hzの中心周波数部分を拡大表示をデジベル変換で表示してみる。マルチテーパ法が中心周波数の解像度を犠牲にしていることが理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
A0_cos = fft(data_cos, 256) / (len(data_cos)/2.0) response_cos = 20 * np.log10(np.abs((A0_cos / abs(A0_cos).max()))) window1 = sig.hann(256) A0_1_cos = fft(window1*data_cos, 256) / (len(window1)/2.0) response1_cos = 20 * np.log10(np.abs((A0_1_cos / abs(A0_1_cos).max()))) window2 = sig.hamming(256) A0_2_cos = fft(window2*data_cos, 256) / (len(window2)/2.0) response2_cos = 20 * np.log10(np.abs((A0_2_cos / abs(A0_2_cos).max()))) window3 = sig.blackman(256) A0_3_cos = fft(window3*data_cos, 256) / (len(window3)/2.0) response3_cos = 20 * np.log10(np.abs((A0_3_cos / abs(A0_3_cos).max()))) freq_mt3_cos, psd_mt2_cos, nu = tsa.multi_taper_psd( data_cos, Fs=128, adaptive=False, jackknife=False) response4_cos = 20 * np.log10(np.abs((psd_mt2_cos / abs(psd_mt2_cos).max()))) plt.axis([0, 10, -120, 20]) plt.plot(freq, response_cos, label="none") plt.plot(freq, response1_cos, label="Han") plt.plot(freq, response2_cos, label="Hamming") plt.plot(freq, response3_cos, label="Blackman") plt.plot(freq_mt3_cos, response4_cos, label="multitaper") plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig26.svg') |
ノイズの載ったサイン波への周波数解析の影響
・サイン波に対して、ノイズデータを載せて、それに対して周波数解析を行うことでノイズへの影響を調べる。ノイズはnumpyのrandom.normal正規分布(平均値ゼロ、標準偏差0.1)で生成して、サイン波に付加。
1 2 3 4 5 6 7 |
dlen = 256 #ノイズデータのデータ長 mean = 0.0 #ノイズの平均値 std = 0.1 #ノイズの分散 t = np.linspace(0, 2, 256) noise = np.random.normal(mean, std, dlen) data_sin_noise = np.sin(2*np.pi*5*t) + noise #data_cos_noise = np.cos(2*np.pi*5*t) + noise |
・ノイズの載ったサイン波を生成した。
1 2 |
plt.plot(t, data_sin_noise) plt.savefig('spectral_analysis_fig27.svg') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
N=128 A0_sin_noise = fft(data_sin_noise, 256) / (len(data_sin_noise)/2.0) #A0_cos_noise = fft(data_cos_noise, 256) / (len(data_cos_noise)/2.0) #freqs_data, psd_data = tsa.periodogram(data) fft_data_sin_noise = abs(A0_sin_noise) #fft_data_cos_noise = abs(A0_cos_noise) plt.plot(freq, fft_data_sin_noise, label="sin+noise") #plt.plot(freq, fft_data_cos_noise, label="cos") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.xlim(-1, 64) #plt.plot(freqs_data, psd_data) plt.savefig('spectral_analysis_fig28.svg') |
・サイン波+ノイズを単純に周波数解析してPSDを求めたものの5Hz周辺の拡大図
1 2 3 4 5 6 |
plt.plot(freq, fft_data_sin_noise, label="sin") #plt.plot(freq, fft_data_cos_noise, label="cos") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.axis([0, 10, -0.1, 1.1]) #plt.plot(freqs_data, psd_data) plt.savefig('spectral_analysis_fig29.svg') |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
A0_dB_sin_noise = 20 * np.log10(np.abs(A0_sin_noise / abs(A0_sin_noise).max())) #A0_dB_cos_noise = 20 * np.log10(np.abs(A0_cos_noise/ abs(A0_cos_noise).max())) plt.figure() plt.plot(freq, A0_dB_sin_noise, label="sin+noise") #plt.plot(freq, A0_dB_cos_noise, label="cos") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.axis([0, 10, -50.0, 10]) plt.title("Frequency response") plt.ylabel("Magnitude [uV^2]") plt.xlabel("Normalized frequency [cycles per sec]") plt.savefig('spectral_analysis_fig30.svg') |
・次にサイン波+ノイズに窓関数を当てはめて、PSDを求める。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
window1 = sig.hann(256) window2 = sig.hamming(256) window3 = sig.blackman(256) plt.plot(window1, label="Hann") plt.plot(window2, label="Hamming") plt.plot(window3, label="Blackman") plt.title("windows") plt.ylabel("Amplitude") plt.xlabel("Sample") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig31.svg') |
・サイン波+ノイズに対して、矩形窓、窓関数を当てはめた周波数解析に基づくPSDを比較する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
A0_sin_noise = fft(data_sin_noise, 256) / (len(window1)/2.0) freq = np.linspace(-0.5, 0.5, len(A0_sin_noise)) response_sin_noise = 20 * np.log10(np.abs(fftshift(A0_sin_noise / abs(A0_sin_noise).max()))) A0_1_sin_noise = fft(window1*data_sin_noise, 256) / (len(window1)/2.0) freq = np.linspace(-0.5, 0.5, len(A0_1_sin_noise)) response1_sin_noise = 20 * np.log10(np.abs(fftshift(A0_1_sin_noise / abs(A0_1_sin_noise).max()))) A0_2_sin_noise = fft(window2*data_sin_noise, 256) / (len(window2)/2.0) freq = np.linspace(-0.5, 0.5, len(A0_2_sin_noise)) response2_sin_noise = 20 * np.log10(np.abs(fftshift(A0_2_sin_noise / abs(A0_2_sin_noise).max()))) A0_3_sin_noise = fft(window3*data_sin_noise, 256) / (len(window3)/2.0) freq = np.linspace(-0.5, 0.5, len(A0_3_sin_noise)) response3_sin_noise = 20 * np.log10(np.abs(fftshift(A0_3_sin_noise / abs(A0_3_sin_noise).max()))) plt.plot(freq, response_sin_noise, label="none", alpha=0.7) plt.plot(freq, response1_sin_noise, label="Hann", alpha=0.7) plt.plot(freq, response2_sin_noise, label="Hamming", alpha=0.7) plt.plot(freq, response3_sin_noise, label="Blackman", alpha=0.7) plt.axis([-0.5, 0.5, -50, 10]) plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig32.svg') |
・サイン波+ノイズに対して、矩形窓、窓関数、さらにマルチテーパ法を当てはめた周波数解析に基づくPSDを比較する。マルチテーパ法では、他の窓関数よりも広いダイナミックレンジを確保していることが理解できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
freq = np.linspace(0, 128, 256) A0_sin_noise = fft(data_sin_noise, 256) / (len(window1)/2.0) #freq = np.linspace(-0.5, 0.5, len(A0)) response_sin_noise = 20 * np.log10(np.abs((A0_sin_noise / abs(A0_sin_noise).max()))) A0_1_sin_noise = fft(window1*data_sin_noise, 256) / (len(window1)/2.0) #freq = np.linspace(-0.5, 0.5, len(A0_1)) response1_sin_noise = 20 * np.log10(np.abs((A0_1_sin_noise / abs(A0_1_sin_noise).max()))) A0_2_sin_noise = fft(window2*data_sin_noise, 256) / (len(window2)/2.0) #freq = np.linspace(-0.5, 0.5, len(A0_2)) response2_sin_noise = 20 * np.log10(np.abs((A0_2_sin_noise / abs(A0_2_sin_noise).max()))) A0_3_sin_noise = fft(window3*data_sin_noise, 256) / (len(window3)/2.0) #freq = np.linspace(-0.5, 0.5, len(A0_3)) response3_sin_noise = 20 * np.log10(np.abs((A0_3_sin_noise / abs(A0_3_sin_noise).max()))) freq_mt3_sin_noise, psd_mt3_sin_noise, nu = tsa.multi_taper_psd( data_sin_noise, Fs=128, adaptive=False, jackknife=False, sides='twosided') response4_sin_noise = 20 * np.log10(np.abs((psd_mt3_sin_noise / abs(psd_mt3_sin_noise).max()))) plt.plot(freq, response_sin_noise, label="none", alpha=1.0) plt.plot(freq, response1_sin_noise, label="Hann", alpha=1.0) plt.plot(freq, response2_sin_noise, label="Hamming", alpha=1.0) plt.plot(freq, response3_sin_noise, label="Blackman", alpha=1.0) plt.plot(freq_mt3_sin_noise, response4_sin_noise, label="multitaper", alpha=1.0) #plt.axis([-0.5, 0.5, -80, 10]) plt.axis([-1, 64, -80, 10]) plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1,1), loc='middle right', borderaxespad=2, fontsize=12) plt.savefig('spectral_analysis_fig33.svg') |
・サイン波+ノイズに対して、矩形窓、窓関数、さらにマルチテーパ法を当てはめた周波数解析に基づくPSDを比較する。5Hz周辺の拡大表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
freq = np.linspace(0, 128, 256) A0_sin_noise = fft(data_sin_noise, 256) / (len(data_sin_noise)/2.0) window1 = sig.hann(256) A0_1_sin_noise = fft(window1*data_sin_noise, 256) / (len(data_sin_noise)/2.0) window2 = sig.hamming(256) A0_2_sin_noise = fft(window2*data_sin_noise, 256) / (len(data_sin_noise)/2.0) window3 = sig.blackman(256) A0_3_sin_noise = fft(window3*data_sin_noise, 256) / (len(data_sin_noise)/2.0) freq_mt3_sin_noise, psd_mt3_sin_noise, nu = tsa.multi_taper_psd( data_sin_noise, Fs=128, adaptive=False, jackknife=False) plt.figure() plt.axis([0, 10, -0.2, 1.2]) plt.plot(freq, abs(A0_sin_noise), label="none") plt.plot(freq, abs(A0_1_sin_noise), label="Hann") plt.plot(freq, abs(A0_2_sin_noise), label="Hamming") plt.plot(freq, abs(A0_3_sin_noise), label="Blackman") plt.plot(freq_mt3_sin_noise, psd_mt3_sin_noise, label="multitaper") plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [¨?V]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig34.svg') |
・サイン波+ノイズに対して、矩形窓、窓関数、さらにマルチテーパ法を当てはめた周波数解析に基づくPSDを比較する。5Hz周辺の拡大デシベル表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
A0_sin_noise = fft(data_sin_noise, 256) / (len(data_sin_noise)/2.0) response_sin_noise = 20 * np.log10(np.abs((A0_sin_noise / abs(A0_sin_noise).max()))) window1 = sig.hann(256) A0_1_sin_noise = fft(window1*data_sin_noise, 256) / (len(window1)/2.0) response1_sin_noise = 20 * np.log10(np.abs((A0_1_sin_noise / abs(A0_1_sin_noise).max()))) window2 = sig.hamming(256) A0_2_sin_noise = fft(window2*data_sin_noise, 256) / (len(window2)/2.0) response2_sin_noise = 20 * np.log10(np.abs((A0_2_sin_noise / abs(A0_2_sin_noise).max()))) window3 = sig.blackman(256) A0_3_sin_noise = fft(window3*data_sin_noise, 256) / (len(window3)/2.0) response3_sin_noise = 20 * np.log10(np.abs((A0_3_sin_noise / abs(A0_3_sin_noise).max()))) freq_mt3_sin_noise, psd_mt2_sin_noise, nu = tsa.multi_taper_psd( data_sin_noise, Fs=128, adaptive=False, jackknife=False) response4_sin_noise = 20 * np.log10(np.abs((psd_mt2_sin_noise / abs(psd_mt2_sin_noise).max()))) plt.axis([0, 10, -80, 10]) plt.plot(freq, response_sin_noise, label="none") plt.plot(freq, response1_sin_noise, label="Han") plt.plot(freq, response2_sin_noise, label="Hamming") plt.plot(freq, response3_sin_noise, label="Blackman") plt.plot(freq_mt3_sin_noise, response4_sin_noise, label="multitaper") plt.title("Frequency response of windows") plt.ylabel("Normalized magnitude [dB]") plt.xlabel("Normalized frequency [cycles per sec]") plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=1, fontsize=12) plt.savefig('spectral_analysis_fig35.svg') |