Escribo esta respuesta adicional para explicar los orígenes de la difusión de los picos al usar fft y especialmente discutir el tutorial de scipy.fftpack con el que no estoy de acuerdo en algún momento.
En este ejemplo, el tiempo de grabación tmax=N*T=0.75
. La señal es sin(50*2*pi*x)+0.5*sin(80*2*pi*x)
. La señal de frecuencia debe contener 2 picos en las frecuencias 50
y 80
con amplitudes 1
y 0.5
. Sin embargo, si la señal analizada no tiene un número entero de periodos, puede aparecer difusión debido al truncamiento de la señal:
- Pike 1:
50*tmax=37.5
=> la frecuencia 50
no es un múltiplo de 1/tmax
=> Presencia de difusión debido al truncamiento de la señal en esta frecuencia.
- Pike 2:
80*tmax=60
=> frecuencia 80
es un múltiplo de 1/tmax
=> No hay difusión debido al truncamiento de la señal en esta frecuencia.
Aquí hay un código que analiza la misma señal que en el tutorial ( sin(50*2*pi*x)+0.5*sin(80*2*pi*x)
) pero con las ligeras diferencias:
- El ejemplo original de scipy.fftpack.
- El ejemplo original de scipy.fftpack con un número entero de períodos de señal (en
tmax=1.0
lugar de 0.75
evitar la difusión del truncamiento).
- El ejemplo original de scipy.fftpack con un número entero de periodos de señal y donde las fechas y frecuencias se toman de la teoría FFT.
El código:
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()
Salida:
Como puede ser aquí, incluso con el uso de un número entero de períodos, todavía queda cierta difusión. Este comportamiento se debe a un mal posicionamiento de fechas y frecuencias en el tutorial scipy.fftpack. Por lo tanto, en la teoría de las transformadas discretas de Fourier:
- la señal debe evaluarse en fechas
t=0,T,...,(N-1)*T
donde T es el período de muestreo y la duración total de la señal es tmax=N*T
. Tenga en cuenta que nos detenemos en tmax-T
.
- las frecuencias asociadas son
f=0,df,...,(N-1)*df
donde df=1/tmax=1/(N*T)
está la frecuencia de muestreo. Todos los armónicos de la señal deben ser múltiplos de la frecuencia de muestreo para evitar la difusión.
En el ejemplo anterior, puede ver que el uso de en arange
lugar de linspace
permite evitar una difusión adicional en el espectro de frecuencias. Además, el uso de la linspace
versión también conduce a un desplazamiento de los picos que se encuentran en frecuencias ligeramente más altas de lo que deberían ser, como se puede ver en la primera imagen donde los picos están un poco a la derecha de las frecuencias 50
y 80
.
Concluiré que el ejemplo de uso debe reemplazarse por el siguiente código (que es menos engañoso en mi opinión):
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()
Salida (el segundo pico ya no se difunde):
Creo que esta respuesta todavía trae algunas explicaciones adicionales sobre cómo aplicar correctamente la transformada discreta de Fourier. Obviamente, mi respuesta es demasiado larga y siempre hay cosas adicionales que decir (@ewerlopes habló brevemente sobre aliasing, por ejemplo, y se puede decir mucho sobre las ventanas ) así que me detendré. Creo que es muy importante comprender profundamente los principios de la transformada discreta de Fourier al aplicarla porque todos conocemos a mucha gente que agrega factores aquí y allá al aplicarla para obtener lo que quieren.