この追加の回答を書いて、FFTを使用するときのスパイクの拡散の原因を説明し、特に、ある時点で同意できないscipy.fftpackチュートリアルについて説明します。
この例では、録音時間tmax=N*T=0.75
。信号はsin(50*2*pi*x) + 0.5*sin(80*2*pi*x)
です。周波数信号には、周波数50
と80
振幅1
およびの2つのスパイクが含まれている必要があり0.5
ます。ただし、分析された信号に整数の周期がない場合、信号の切り捨てにより拡散が発生する可能性があります。
- パイク1:
50*tmax=37.5
=>周波数50
は1/tmax
=>この周波数での信号の切り捨てによる拡散の存在の倍数ではありません。
- パイク2:
80*tmax=60
=>周波数80
は1/tmax
=>この周波数での信号の切り捨てによる拡散なしの倍数です。
チュートリアル(sin(50*2*pi*x) + 0.5*sin(80*2*pi*x)
)と同じ信号を分析するコードを次に示しますが、わずかな違いがあります。
- 元のscipy.fftpackの例。
- 整数個の信号周期を持つ元のscipy.fftpackの例(切り捨ての拡散を回避する
tmax=1.0
代わりに0.75
)。
- 整数個の信号周期を持ち、日付と周波数がFFT理論から取得された元のscipy.fftpackの例。
コード:
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
N = 600
tmax = 3/4
T = tmax / N
x1 = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x1) + 0.5*np.sin(80.0 * 2.0*np.pi*x1)
yf1 = scipy.fftpack.fft(y1)
xf1 = np.linspace(0.0, 1.0/(2.0*T), N//2)
tmax = 1
T = tmax / N
x2 = np.linspace(0.0, N*T, N)
y2 = np.sin(50.0 * 2.0*np.pi*x2) + 0.5*np.sin(80.0 * 2.0*np.pi*x2)
yf2 = scipy.fftpack.fft(y2)
xf2 = np.linspace(0.0, 1.0/(2.0*T), N//2)
tmax = 1
T = tmax / N
x3 = T * np.arange(N)
y3 = np.sin(50.0 * 2.0*np.pi*x3) + 0.5*np.sin(80.0 * 2.0*np.pi*x3)
yf3 = scipy.fftpack.fft(y3)
xf3 = 1/(N*T) * np.arange(N)[:N//2]
fig, ax = plt.subplots()
ax.plot(xf1, 2.0/N * np.abs(yf1[:N//2]), label='fftpack tutorial')
ax.plot(xf2, 2.0/N * np.abs(yf2[:N//2]), label='Integer number of periods')
ax.plot(xf3, 2.0/N * np.abs(yf3[:N//2]), label='Correct positioning of dates')
plt.legend()
plt.grid()
plt.show()
出力:
ここにあるように、整数の期間を使用しても、ある程度の拡散が残っています。この動作は、scipy.fftpackチュートリアルでの日付と頻度の配置が不適切なためです。したがって、離散フーリエ変換の理論では、次のようになります。
- 信号は
t=0,T,...,(N-1)*T
、Tがサンプリング期間であり、信号の合計持続時間がである日付で評価する必要がありtmax=N*T
ます。で停止することに注意してくださいtmax-T
。
- 関連する周波数は次のとおりです。
f=0,df,...,(N-1)*df
ここdf=1/tmax=1/(N*T)
で、はサンプリング周波数です。信号のすべての高調波は、拡散を避けるためにサンプリング周波数の倍数である必要があります。
上記の例では、のarange
代わりにをlinspace
使用すると、周波数スペクトルでの追加の拡散を回避できることがわかります。さらに、このlinspace
バージョンを使用すると、スパイクが周波数50
との右側に少しある最初の図に見られるように、本来あるべき周波数よりもわずかに高い周波数にあるスパイクのオフセットも発生し80
ます。
使用例は次のコードに置き換える必要があると結論付けます(私の意見では誤解を招きにくいです)。
import numpy as np
from scipy.fftpack import fft
N = 600
T = 1.0 / 800.0
x = T*np.arange(N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = 1/(N*T)*np.arange(N//2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N//2]))
plt.grid()
plt.show()
出力(2番目のスパイクはもう拡散されません):
この答えは、正しく離散フーリエ変換を適用する方法について、まだいくつかの追加の説明をもたらすと思います。明らかに、私の答えは長すぎると(言うために追加のものが常にあるewerlopesを簡単に話した約エイリアシング例えば、ロットはについて語ったことができ窓掛け私が停止されますので、は、)。
離散フーリエ変換を適用する際には、その原理を深く理解することが非常に重要だと思います。なぜなら、必要なものを得るために適用するときに、あちこちで因子を追加する人がたくさんいるからです。