# <!-- 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)

# 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)

# filtrando sólo del 2016-02-04 al 2016-02-18
wft['2016-02-04':'2016-02-18']

# valores al 2016-02-16
wft.loc['2016-2-16']

# valor de la columna Adj Close al 2016-2-16
wft['Adj Close']['2016-2-16']

# filtrando todo febrero de 2016
wft['2016-2']

# Valores al cierre de cada mes.
wft.asfreq('M', method='ffill')

# Valores al cierre de cada mes (días laborales).
wft.asfreq('BM')

# valores al cierre de cada trimestre
wft.asfreq('BQ')

"""
Explanation: Series de tiempo con Python
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.
<img alt="Series de tiempo con Python" title="Series de tiempo con Python" src="https://relopezbriega.github.io/images/timeSeries.png" high=400px width=600px>
Introducción
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.
¿Qué es una serie de tiempo?
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. 
¿Qué las hace especiales?
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.


Series de tiempo estacionarias
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 media de la serie no debe ser una función de tiempo; sino que debe ser constante. La siguiente imagen muestra una serie que cumple con esta condición y otra que no la cumple.
<img alt="Series de tiempo con Python" title="media estacionaria - no estacionaria" src="https://relopezbriega.github.io/images/Mean_stationary.png" high=300px width=500px>
La varianza de la serie no debe ser una función del tiempo. El siguiente gráfico representa una serie cuya varianza no esta afectada por el tiempo (es estacionaria) y otra que no cumple con esa condición.
<img alt="Series de tiempo con Python" title="media estacionaria - no estacionaria" src="https://relopezbriega.github.io/images/Var_stationary.png" high=300px width=500px>
La covarianza de la serie no debe ser una función del tiempo. En el gráfico de la derecha, se puede observar que la propagación de la serie se va encogiendo a medida que aumenta el tiempo. Por lo tanto, la covarianza no es constante en el tiempo para la serie roja.
<img alt="Series de tiempo con Python" title="media estacionaria - no estacionaria" src="https://relopezbriega.github.io/images/Cov_stationary.png" high=300px width=500px>

¿Por qué son importantes las series de tiempo estacionarias?
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.
Series de tiempo con Python
Las principales librerías que nos ofrece Python para trabajar con series de tiempo son:


Statsmodels: Esta librería contiene muchos objetos y funciones de suma utilidad para el análisis de series de tiempo. Algunos de los modelos que están cubiertos por Statsmodels incluyen: el modelo autorregresivo (AR); el modelo autorregresivo de vectores (VAR); y el modelo autorregresivo de media móvil (ARMA).  También incluye funciones de estadística descriptiva de series de tiempo, como por ejemplo la autocorrelación, así como las correspondientes propiedades teóricas de ARMA o procesos relacionados. Por último, también ofrece las pruebas estadísticas relacionadas y algunas funciones auxiliares muy útiles.


Pandas: Pandas proporciona un amplio soporte para trabajar con datos de series de tiempo. Generalmente cuando trabajamos con series de tiempo realizamos un amplio abanico de tareas, como: convertir fechas, estandarizar el tiempo de acuerdo a la zona horaria, crear secuencias a determinados intervalos o frecuencias, identificar datos faltantes, desplazar las fechas hacia atrás o hacia adelante por un determinado valor, calcular resúmenes agregados de valores a medida que el tiempo cambia, etc. Pandas nos brinda las herramientas para poder realizar estas y muchas otras tareas en forma muy sencilla.


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.
Manipulando la serie de tiempo con pandas
End of explanation
"""


# desplazando el 1 dia el valor de cierre
desplazado = wft['Adj Close'].shift(1)
desplazado[:5]

# 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]

# mismo resultado utilizando pct_change()
wft['Adj Close'].pct_change()[:5]

# calculando rendimiento acumulado diario
rendimiento_diario = (1 + wft['Adj Close'].pct_change()).cumprod()
wft['rend_diario'] = rendimiento_diario
wft['rend_diario'][:5]

"""
Explanation: Desplazando los valores de la serie
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().
End of explanation
"""


# graficando Adj Close
plot = wft['Adj Close'].plot(figsize=(10, 8))

# 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

# 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);

# graficando rendimiento diario
plot = wft['var_diaria'].plot(figsize=(10, 8))

"""
Explanation: Visualizando las series de tiempo
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().
End of explanation
"""


# 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)

# Ejemplo de descomposición de serie de tiempo
descomposicion = sm.tsa.seasonal_decompose(wft['Adj Close'],
                                                  model='additive', freq=30)  
fig = descomposicion.plot()

"""
Explanation: Promedios móviles y descomposición
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.
End of explanation
"""


# 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))  

"""
Explanation: Pronosticando la serie con ARIMA
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.
End of explanation
"""


# 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)) 

"""
Explanation: 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.
End of explanation
"""


