Я пишу цю додаткову відповідь, щоб пояснити походження дифузії шипів при використанні fft, і особливо обговорюю підручник із scipy.fftpack, з яким я в якийсь момент не погоджуюсь.
У цьому прикладі час запису tmax=N*T=0.75
. Сигнал є sin(50*2*pi*x)+0.5*sin(80*2*pi*x)
. Частотний сигнал повинен містити 2 спайки на частотах 50
і 80
з амплітудами 1
і 0.5
. Однак, якщо аналізований сигнал не має цілого числа періодів, може з'явитися дифузія через скорочення сигналу:
- Щука 1:
50*tmax=37.5
=> частота 50
не кратна 1/tmax
=> Наявність дифузії внаслідок усічення сигналу на цій частоті.
- Pike 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
щоб уникнути дифузії усічення).
- Оригінальний приклад 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 positionning 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()
Результат (другий спайк більше не розсіюється):
Я думаю, що ця відповідь все ж приносить деякі додаткові пояснення щодо того, як правильно застосовувати дискретне перетворення Фур'є. Очевидно, що моя відповідь занадто довга, і завжди є що сказати (@ewerlopes коротко говорив про, наприклад, псевдонім, і багато чого можна сказати про вікна ), тому я зупинюсь . Я думаю, що дуже важливо глибоко зрозуміти принципи дискретного перетворення Фур'є, застосовуючи його, тому що всі ми знаємо стільки людей, які додають фактори тут і там, застосовуючи його, щоб отримати бажане.