En mi vida profesional como científico de datos, he encontrado series de tiempo varias veces. La mayor parte de mi conocimiento proviene de mi experiencia académica, específicamente mis cursos en econometría (tengo un título en economía), donde estudiamos propiedades estadísticas y modelos de series de tiempo.
Entre los modelos que estudié estaba Sarimaque reconoce el estacionalidad Sin embargo, de una serie temporal, nunca hemos estudiado cómo interceptar y reconocer los patrones de estacionalidad.
La mayoría de las veces tuve que encontrar patrones estacionales en los que me confié visual inspección de datos. Esto fue hasta que me topé Este video de YouTube en Transformaciones de Fourier y finalmente descubrí qué periodograma es.
En esta publicación de blog, explicaré y aplicaré conceptos simples que se convertirán en herramientas útiles que cada DS que estudia series de tiempo debería saber.
Tabla de contenido
- ¿Qué es una transformación de Fourier?
- Transformación de Fourier en Python
- Periodograma
Descripción general
Supongamos que tengo el siguiente conjunto de datos (Consumo de energía AEPLicencia CC0):
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("data/AEP_hourly.csv", index_col=0)
df.index = pd.to_datetime(df.index)
df.sort_index(inplace=True)
fig, ax = plt.subplots(figsize=(20,4))
df.plot(ax=ax)
plt.tight_layout()
plt.show()
Es muy claro, solo de una inspección visual, que Los patrones estacionales están jugando un papelsin embargo, podría ser trivial interceptarlos a todos.
Como se explicó anteriormente, el proceso de descubrimiento que solía realizar fue principalmente manualy podría haber buscado algo de la siguiente manera:
fig, ax = plt.subplots(3, 1, figsize=(20,9))
df_3y = df[(df.index >= '2006–01–01') & (df.index < '2010–01–01')]
df_3M = df[(df.index >= '2006–01–01') & (df.index < '2006–04–01')]
df_7d = df[(df.index >= '2006–01–01') & (df.index < '2006–01–08')]
ax[0].set_title('AEP energy consumption 3Y')
df_3y[['AEP_MW']].groupby(pd.Grouper(freq = 'D')).sum().plot(ax=ax[0])
for date in df_3y[[True if x % (24 * 365.25 / 2) == 0 else False for x in range(len(df_3y))]].index.tolist():
ax[0].axvline(date, color = 'r', alpha = 0.5)
ax[1].set_title('AEP energy consumption 3M')
df_3M[['AEP_MW']].plot(ax=ax[1])
for date in df_3M[[True if x % (24 * 7) == 0 else False for x in range(len(df_3M))]].index.tolist():
ax[1].axvline(date, color = 'r', alpha = 0.5)
ax[2].set_title('AEP energy consumption 7D')
df_7d[['AEP_MW']].plot(ax=ax[2])
for date in df_7d[[True if x % 24 == 0 else False for x in range(len(df_7d))]].index.tolist():
ax[2].axvline(date, color = 'r', alpha = 0.5)
plt.tight_layout()
plt.show()

Esta es una visualización más profunda de esta serie de tiempo. Como podemos ver, los siguientes patrones influyen en los datos: **- Un ciclo de 6 meses,
- un ciclo semanal,
- y un ciclo diario. **
Este conjunto de datos muestra el consumo de energía, por lo que estos patrones estacionales son fácilmente inferibles solo del conocimiento del dominio. Sin embargo, al confiar solo en una inspección manual podríamos perder información importante. Estos podrían ser algunos de los principales desventajas:
- Subjetividad: Podríamos perder los patrones menos obvios.
- Pérdida de tiempo : Necesitamos probar diferentes plazos uno por uno.
- Problemas de escalabilidad: Funciona bien para algunos conjuntos de datos, pero ineficiente para el análisis a gran escala.
Como científico de datos, sería útil tener una herramienta que nos brinde Comentarios inmediatos sobre las frecuencias más importantes que componen la serie temporal. Aquí es donde el Transformaciones de Fourier ven a ayudar.
1. ¿Qué es una transformación de Fourier?
La transformación de Fourier es una herramienta matemática que nos permite “cambiar de dominio”.
Por lo general, visualizamos nuestros datos en el dominio de tiempo. Sin embargo, usando una transformación de Fourier, podemos cambiar a la dominio de la frecuenciaque muestra las frecuencias que están presentes en la señal y su contribución relativa a la serie temporal original.
Intuición
Cualquier función de bienestar F (x) puede escribirse como una suma de sinusoides con diferentes frecuencias, amplitudes y fases. En términos simples, cada señal (Serie de tiempo) es solo un combinación de formas de onda simples.

Dónde:
- F (f) representa la función en el dominio de la frecuencia.
- f (x) es la función original en el dominio de tiempo.
- Exp (−i2πf (x)) es un exponencial complejo que actúa como un “filtro de frecuencia”.
De este modo, F (f) nos dice cuánto frecuencia F es presente en la función original.
Ejemplo
Consideremos una señal compuesta de tres ondas sinusoidales con frecuencias de 2 Hz, 3 Hz y 5 Hz:

Ahora, aplicemos una transformación de Fourier para extraer estas frecuencias de la señal:

El gráfico anterior representa nuestra señal expresada en el dominio de frecuencia en lugar del dominio de tiempo clásico. A partir de la gráfica resultante, podemos ver que nuestra señal se descompone en 3 elementos de frecuencia de 2 Hz, 3 Hz y 5 Hz como se esperaba de la señal de inicio.
Como se dijo anteriormente, cualquier función de bienestar se puede escribir como una suma de sinusoides. Con la información que tenemos hasta ahora es posible descomponer nuestra señal en tres sinusoides:

La señal original (en azul) se puede obtener sumando las tres ondas (en rojo). Este proceso se puede aplicar fácilmente en cualquier serie de tiempo para evaluar las principales frecuencias que componen la serie temporal.
2 transformación de Fourier en Python
Dado que es bastante fácil cambiar entre el dominio del tiempo y el dominio de frecuencia, echemos un vistazo a la serie temporal de consumo de energía AEP que comenzamos a estudiar al comienzo del artículo.
Python proporciona la biblioteca “numpy.fft” para calcular la transformación de Fourier para señales discretas. FFT significa transformación rápida de Fourier, que es un algoritmo utilizado para descomponer una señal discreta en sus componentes de frecuencia:
from numpy import fft
X = fft.fft(df['AEP_MW'])
N = len(X)
frequencies = fft.fftfreq(N, 1)
periods = 1 / frequencies
fft_magnitude = np.abs(X) / N
mask = frequencies >= 0
# Plot the Fourier Transform
fig, ax = plt.subplots(figsize=(20, 3))
ax.step(periods[mask], fft_magnitude[mask]) # Only plot positive frequencies
ax.set_xscale('log')
ax.xaxis.set_major_formatter('{x:,.0f}')
ax.set_title('AEP energy consumption - Frequency-Domain')
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude')
plt.show()

Esta es la visualización del dominio de frecuencia del consumo de energía AEP_MW. Cuando analizamos el gráfico, ya podemos ver que a ciertas frecuencias tenemos una mayor magnitud, lo que implica una mayor importancia de tales frecuencias.
Sin embargo, antes de hacerlo, agregamos una teoría más que nos permitirá construir un periodogramaque nos dará una mejor visión de las frecuencias más importantes.
3. Periogrograma
El periodograma es una representación de dominio de frecuencia del densidad espectral de potencia (Psd) de una señal. Mientras que la transformación de Fourier nos dice qué frecuencias están presentes en una señal, el periodograma cuantifica la potencia (o intensidad) de esas frecuencias. Este pasaje es útil como esto reduce el ruido de frecuencias menos importantes.
Matemáticamente, el periodograma está dado por:

Dónde:
- P (f) es la densidad espectral de potencia (PSD) a la frecuencia F,
- X (f) es la transformación de Fourier de la señal,
- N es el número total de muestras.
Esto se puede lograr en Python de la siguiente manera:
power_spectrum = np.abs(X)**2 / N # Power at each frequency
fig, ax = plt.subplots(figsize=(20, 3))
ax.step(periods[mask], power_spectrum[mask])
ax.set_title('AEP energy consumption Periodogram')
ax.set_xscale('log')
ax.xaxis.set_major_formatter('{x:,.0f}')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Power')
plt.show()

A partir de este periodograma, ahora es posible extraer conclusiones. Como podemos ver las frecuencias más poderosas en:
- 24 Hz, correspondiente a 24h,
- 4.380 Hz, correspondiente a 6 meses,
- y a 168 Hz, correspondiente al ciclo semanal.
Estos tres son los mismos Estacionalidad Componentes que encontramos en el ejercicio manual realizado en la inspección visual. Sin embargo, utilizando esta visualización, podemos ver otros tres ciclosmás débil en poder, pero presente:
- un ciclo de 12 Hz,
- un ciclo de 84 Hz, correspondiente a media semana,
- Un ciclo de 8.760 Hz, correspondiente a un año completo.
También es posible usar la función “Periodograma” presente en SciPy para obtener el mismo resultado.
from scipy.signal import periodogram
frequencies, power_spectrum = periodogram(df['AEP_MW'], return_onesided=False)
periods = 1 / frequencies
fig, ax = plt.subplots(figsize=(20, 3))
ax.step(periods, power_spectrum)
ax.set_title('Periodogram')
ax.set_xscale('log')
ax.xaxis.set_major_formatter('{x:,.0f}')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Power')
plt.show()
Conclusiones
Cuando estamos tratando con series temporales, uno de los componentes más importantes a considerar es la temporada de temporada.
En esta publicación de blog, hemos visto cómo Descubre fácilmente los valios de temporada Dentro de una serie de tiempo usando un periodograma. Proporcionándonos una herramienta de implicación simple que será extremadamente útil en el proceso exploratorio.
Sin embargo, este es solo un punto de partida de las posibles implementaciones de la transformación de Fourier de la que podríamos beneficiarnos, ya que hay muchos más:
- Espectrograma
- Codificación de características
- Descomposición de series de tiempo
- …
Por favor, deje algunos aplausos si disfrutó del artículo y no dude en comentar, ¡se agradece cualquier sugerencia y retroalimentación!
_Aquí puede encontrar un cuaderno con el código de esta publicación de blog._