Piszę tę dodatkową odpowiedź, aby wyjaśnić pochodzenie dyfuzji skoków podczas korzystania z FFT, a zwłaszcza omówić samouczek scipy.fftpack, z którym w pewnym momencie się nie zgadzam.
W tym przykładzie czas nagrywania tmax=N*T=0.75
. Sygnał jest sin(50*2*pi*x) + 0.5*sin(80*2*pi*x)
. Sygnał częstotliwości powinien zawierać dwa skoki o częstotliwościach 50
i 80
amplitudach 1
i 0.5
. Jeśli jednak analizowany sygnał nie ma całkowitej liczby okresów, może wystąpić dyfuzja z powodu obcięcia sygnału:
- Pik 1:
50*tmax=37.5
=> częstotliwość 50
nie jest wielokrotnością 1/tmax
=> Obecność dyfuzji z powodu obcięcia sygnału na tej częstotliwości.
- Pik 2:
80*tmax=60
=> częstotliwość 80
jest wielokrotnością 1/tmax
=> Brak dyfuzji z powodu obcięcia sygnału na tej częstotliwości.
Oto kod analizujący ten sam sygnał co w tutorialu ( sin(50*2*pi*x) + 0.5*sin(80*2*pi*x)
), ale z niewielkimi różnicami:
- Oryginalny przykład scipy.fftpack.
- Oryginalny przykład scipy.fftpack z całkowitą liczbą okresów sygnału (
tmax=1.0
zamiast w 0.75
celu uniknięcia dyfuzji obcięcia).
- Oryginalny przykład scipy.fftpack z całkowitą liczbą okresów sygnału oraz datami i częstotliwościami zaczerpniętymi z teorii FFT.
Kod:
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()
Wynik:
Jak to może być tutaj, nawet przy użyciu całkowitej liczby okresów nadal istnieje pewne rozproszenie. To zachowanie jest spowodowane złym ustawieniem dat i częstotliwości w samouczku scipy.fftpack. Stąd w teorii dyskretnych przekształceń Fouriera:
- sygnał należy oceniać w datach, w
t=0,T,...,(N-1)*T
których T oznacza okres próbkowania, a całkowity czas trwania sygnału wynosi tmax=N*T
. Zauważ, że zatrzymujemy się na tmax-T
.
- powiązane częstotliwości to
f=0,df,...,(N-1)*df
gdzie df=1/tmax=1/(N*T)
jest częstotliwość próbkowania. Wszystkie harmoniczne sygnału powinny być wielokrotnością częstotliwości próbkowania, aby uniknąć dyfuzji.
W powyższym przykładzie widać, że użycie arange
zamiast linspace
pozwala uniknąć dodatkowej dyfuzji w widmie częstotliwości. Co więcej, używanie tej linspace
wersji prowadzi również do przesunięcia kolców, które znajdują się na nieco wyższych częstotliwościach niż powinny, jak widać na pierwszym zdjęciu, gdzie kolce znajdują się trochę po prawej stronie częstotliwości 50
i 80
.
Wnioskuję tylko, że przykład użycia należy zastąpić następującym kodem (który moim zdaniem jest mniej mylący):
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()
Wyjście (drugi skok nie jest już rozproszony):
Myślę, że ta odpowiedź nadal przynosi dodatkowe wyjaśnienia dotyczące prawidłowego stosowania dyskretnej transformaty Fouriera. Oczywiście, moja odpowiedź jest zbyt długa i nie zawsze jest więcej rzeczy do powiedzenia ( ewerlopes rozmawiałem krótko o aliasing na przykład i wiele można powiedzieć o okienkowy ), więc będę się zatrzymać.
Myślę, że bardzo ważne jest, aby dogłębnie zrozumieć zasady dyskretnej transformaty Fouriera podczas jej stosowania, ponieważ wszyscy wiemy, że tak wiele osób dodaje czynniki tu i tam, stosując ją, aby uzyskać to, czego chcą.