Esta notebook fue creada originalmente como un blog post por Raúl E. López Briega en Matemáticas, análisis de datos y python. El contenido esta bajo la licencia BSD.
Los datos obtenidos a partir de observaciones recogidas a lo largo del tiempo son extremadamente comunes. En los negocios, observamos las tasas de interés de la semana, los precios de cierre de las acciones diarios, los índices de precios mensuales, las cifras de ventas anuales, y así sucesivamente. En meteorología, observamos las temperaturas máximas y mínimas diarias, los índices anuales de precipitación y de sequía, y las velocidades del viento por hora. En la agricultura, registramos las cifras anuales de producción agrícola y ganadera, la erosión del suelo, y las ventas de exportación. En las ciencias biológicas, observamos la actividad eléctrica del corazón en intervalos de milisegundos. La lista de las áreas en las que se estudian las series de tiempo es prácticamente interminable.
Una serie de tiempo o serie temporal es una secuencia de datos, observaciones o valores, medidos en determinados momentos y ordenados cronológicamente. Los datos pueden estar espaciados a intervalos iguales o desiguales. Una vez que se captura una serie de tiempo, a menudo se realiza un análisis sobre ella para identificar patrones en los datos, en esencia, lo que se busca es entender que suceda a medida que el tiempo va avanzando. Ser capaz de procesar datos de series de tiempo es una habilidad esencial en el mundo moderno. Uno de los usos más habituales de las series de tiempo es su análisis para predicción y pronóstico (así se hace por ejemplo con los datos climáticos, las acciones de bolsa, o las series de datos demográficos). Resulta difícil imaginar una rama de las ciencias en la que no aparezcan datos que puedan ser considerados como series de tiempo.
Las características que hacen a las series de tiempo especiales y las diferencia de, por ejemplo, un problema de regresión son las siguientes:
Son dependientes del tiempo; por lo tanto el supuesto básico de los modelos de regresión de que las observaciones son independientes no se sostiene en este caso.
Suelen tener una tendencia; la mayoría de las series de tiempo suelen tener algún tipo de tendencia de estacionalidad, es decir, las variaciones propias de un período de tiempo determinado.
Suelen estar autocorrelacionadas; la mayoría de los procesos físicos presentan una inercia y no cambian tan rápidamente. Esto, combinado con la frecuencia del muestreo, a menudo hace que las observaciones consecutivas estén correlacionadas. Esta correlación entre observaciones consecutivas se llama autocorrelación. Cuando los datos están autocorrelacionados, la mayoría de los métodos estadísticos estándares basados en la suposición de observaciones independientes pueden arrojar resultados engañosos o incluso ser inútiles.
Un tipo muy importante de series de tiempo son las series de tiempo estacionarias. Una series de tiempo se dice que es estrictamente estacionaria si sus propiedades no son afectadas por los cambios a lo largo del tiempo. Es decir, que se deberían cumplir tres criterios básicos para poder considerar a una series de tiempo como estacionaria:
La razón por la que estas series son importantes es que la mayoría de los modelos de series de tiempo funcionan bajo el supuesto de que la serie es estacionaria. Intuitivamente, podemos suponer que si una serie tiene un comportamiento particular en el tiempo, hay una probabilidad muy alta de que se comportamiento continúe en el futuro. Además, las teorías relacionadas con las series estacionarias son más maduras y más fáciles de implementar en comparación con series no estacionarias. A pesar de que el supuesto de que la serie es estacionaria se utiliza en muchos modelos, casi ninguna de las series de tiempo que encontramos en la práctica son estacionarias. Por tal motivo la estadística tuvo que desarrollar varias técnicas para hacer estacionaria, o lo más cercano posible a estacionaria, a una serie.
Las principales librerías que nos ofrece Python para trabajar con series de tiempo son:
Veamos algunos ejemplos de como podemos manipular y analizar series de tiempo con la ayuda de Python. En este caso, vamos a jugar un poco con la información de los precios de las acciones de Weatherford (WFT) de este año.
In [1]:
# <!-- collapse=True -->
# importando modulos necesarios
import numpy as np
import pandas as pd
import pandas.io.data as web
import datetime as dt
from pydataset import data
import statsmodels.api as sm
# librerías de visualizaciones
import seaborn as sns
import matplotlib.pyplot as plt
# graficos incrustados
%matplotlib inline
# pandas solo 4 decimales
pd.set_option('precision', 4)
In [2]:
# Ejemplo serie de tiempo con Pandas
# Creando una serie de tiempo de las acciones de WFT desde yahoo finance
wft = web.DataReader("WFT", 'yahoo', '2016-1-1', '2016-9-30')
wft.head(5)
Out[2]:
In [3]:
# filtrando sólo del 2016-02-04 al 2016-02-18
wft['2016-02-04':'2016-02-18']
Out[3]:
In [4]:
# valores al 2016-02-16
wft.loc['2016-2-16']
Out[4]:
In [5]:
# valor de la columna Adj Close al 2016-2-16
wft['Adj Close']['2016-2-16']
Out[5]:
In [6]:
# filtrando todo febrero de 2016
wft['2016-2']
Out[6]:
In [7]:
# Valores al cierre de cada mes.
wft.asfreq('M', method='ffill')
Out[7]:
In [8]:
# Valores al cierre de cada mes (días laborales).
wft.asfreq('BM')
Out[8]:
In [9]:
# valores al cierre de cada trimestre
wft.asfreq('BQ')
Out[9]:
Una operación común en los datos de series de tiempo es desplazar los valores hacia atrás y adelante en el tiempo, como por ejemplo para calcular el cambio porcentual de una muestra a otra. En Pandas podemos utilizar el método .shift()
.
In [10]:
# desplazando el 1 dia el valor de cierre
desplazado = wft['Adj Close'].shift(1)
desplazado[:5]
Out[10]:
In [11]:
# calculando el porcentaje de variación del día.
variacion_diaria = wft['Adj Close'] / wft['Adj Close'].shift(1) - 1
wft['var_diaria'] = variacion_diaria
wft['var_diaria'][:5]
Out[11]:
In [12]:
# mismo resultado utilizando pct_change()
wft['Adj Close'].pct_change()[:5]
Out[12]:
In [13]:
# calculando rendimiento acumulado diario
rendimiento_diario = (1 + wft['Adj Close'].pct_change()).cumprod()
wft['rend_diario'] = rendimiento_diario
wft['rend_diario'][:5]
Out[13]:
Una operación fundamental para entender el comportamiento de una serie de tiempo y poder determinar si se trata de una serie estacionaria o no; es realizar gráficos de la misma. En Pandas esto lo podemos realizar en forma muy sencilla con el método .plot()
.
In [14]:
# graficando Adj Close
plot = wft['Adj Close'].plot(figsize=(10, 8))
In [15]:
# Aplicando el filtro Hodrick-Prescott para separar en tendencia y
# componente ciclico.
wft_ciclo, wft_tend = sm.tsa.filters.hpfilter(wft['Adj Close'])
wft['tend'] = wft_tend
In [16]:
# graficando la variacion del precio real con la tendencia.
wft[['Adj Close', 'tend']].plot(figsize=(10, 8), fontsize=12);
legend = plt.legend()
legend.prop.set_size(14);
In [17]:
# graficando rendimiento diario
plot = wft['var_diaria'].plot(figsize=(10, 8))
Pandas también nos ofrece una serie de funciones para calcular estadísticas móviles, en ellas la función estadística se calcula sobre una ventana de datos representados por un determinado período de tiempo y luego se desplaza la ventana de datos por un intervalo especificado, calculando continuamente la estadística, siempre y cuando la ventana este dentro de las fechas de la serie de tiempo. El ejemplo más utilizado es el de media móvil, que se usa comúnmente en el análisis de series de tiempo financieras para suavizar las fluctuaciones a corto plazo y poner de relieve las tendencias a largo plazo en los datos.
Otra técnica interesante que podemos intentar también es la descomposición. Esta es una técnica que trata de descomponer una serie de tiempo en su tendencia, su estacionalidad y sus factores residuales. Statsmodels viene con una función de descomposición que nos facilita en sobremanera el trabajo. Veamos unos ejemplos.
In [18]:
# Calculando promedios móviles cada 5 días
wft_ma = pd.rolling_mean(wft['Adj Close'], 5)
wft['prod_mov'] = wft_ma
plot = wft[['Adj Close', 'prod_mov']].plot(figsize=(10, 8), fontsize=12)
In [19]:
# Ejemplo de descomposición de serie de tiempo
descomposicion = sm.tsa.seasonal_decompose(wft['Adj Close'],
model='additive', freq=30)
fig = descomposicion.plot()
Como podemos observar en los gráficos que realizamos anteriormente, el comportamiento de la serie de tiempo con la que estamos trabajando parece ser totalmente aleatorio y las medidas móviles que calculamos tampoco parecen ser de mucha utilidad para acercar la serie a un comportamiento estacionario. De todas formas podemos intentar aplicar un modelo ARIMA sobre la serie y ver que tan bien nos va con el pronostico del modelo. El modelo ARIMA es similar a una regresión estadística pero aplicando los conceptos de las series de tiempo; por tanto, los pronósticos del modelo vienen explicadas por los datos del pasado y no por variables independientes.
In [20]:
# Modelo ARIMA sobre el valor de cierre de la acción.
modelo = sm.tsa.ARIMA(wft['Adj Close'].iloc[1:], order=(1, 0, 0))
resultados = modelo.fit(disp=-1)
wft['pronostico'] = resultados.fittedvalues
plot = wft[['Adj Close', 'pronostico']].plot(figsize=(10, 8))
Aquí el modelo parece ser bastante efectivo, las líneas en el gráfico son muy similares. Pero para armar el modelo hemos utilizado el valor de cierre de la acción, y la variación de precio en el día a día es muy pequeña en comparación al precio absoluto. Lo que realmente nos interesa predecir es la variación diaria del precio de la acción, por lo tanto deberíamos armar el modelo utilizando la columna de variación diaria que calculamos previamente.
In [21]:
# modelo ARIMA sobre variación diaria
modelo = sm.tsa.ARIMA(wft['var_diaria'].iloc[1:], order=(1, 0, 0))
resultados = modelo.fit(disp=-1)
wft['pronostico'] = resultados.fittedvalues
plot = wft[['var_diaria', 'pronostico']].plot(figsize=(10, 8))
En este gráfico podemos ver que es bastante obvio que el pronóstico esta muy lejos. Nuestro modelo predice variaciones muy pequeñas en comparación con lo que ocurre en la realidad del día a día. Este era un resultado esperado ya que solo aplicamos un modelo sencillo de promedios móviles a una serie no estacionaria; después de todo, si fuera tan fácil predecir el movimiento del mercado, todos seríamos millonarios!. No hay suficiente información en los días anteriores para poder predecir con exactitud lo que va a pasar al día siguiente.
Aquí concluye este paseo por el mundo de las series de tiempo; como vimos, Pandas y Statsmodels pueden ser de mucha ayuda para trabajar con ellas. Espero que lo hayan encontrado útil.
Saludos!
Este post fue escrito utilizando IPython notebook. Pueden descargar este notebook o ver su version estática en nbviewer.