Predictor de precios de mercado - General Motors - Parte II

David Cerezal Landa 15 minutos de lectura

Como hemos comentado en los posts anteriores, en este set de diferentes notebooks trataremos de hacer un análisis de la serie temporal de los precios de cierre de mercado de la compañía General Motors en la bolsa de NY. Como objetivo se fijará crear un modelo de referencia y tratar de mejorarlo usando redes secuenciales. El objeto de estudio de hoy es:

El segundo punto, Modelos de referencia, estudiaremos principalmente un modelo autorregresivo integrado de promedio móvil o ARIMA, dado que será el que mejor se ajuste a nuestra serie en función del pre-análisis del punto uno. También, se probarán otros métodos como Naïve method y random-forest para ver si nuestras premisas eran correctas.

Forecast: Modelos de Referencia

En este punto estudiaremos principalmente dos modelos, uno autorregresivo integrado de promedio móvil o ARIMA, y otro más simple, naïve methods que aparentemente no sea el que mejor se ajuste a las caracteristicas de nuestra serie en función del pre-análisis del punto uno. También, se probarán otros métodos como random-forest para ver si nuestras premisas eran correctas. EL modelo Naive es teóricamente el que mejor se puede ajustar a este tipo de series.

En el post de hoy trataremos:

1.- Modelo ARIMA

2.- Modelos Naïve methods

3.- Modelo random Forest

Modelo Arima

Vamos en este punto a realizar un estudio de los datos para pasar despues a entrenar un modelo univariante tradicional conocido como el modelo ARIMA. De manera teorica para el entrenamiento de un Modelo de este tipo tenemos que partir de un conocimiento de la tipologia de nuestra Secuencia, en cuanto a su tendencia (Trend) y su estacionalidad (Seasonal).

Teoricamente este tipo de modelos sólo se puede aplicar a series estacionarias por ellos tendremos que transformar la serie en ello.

En estadística y econometría, en particular en series temporales, ARIMA es un modelo autorregresivo integrado de promedio móvil o ARIMA (acrónimo del inglés autoregressive integrated moving average) es un modelo estadístico que utiliza variaciones y regresiones de datos estadísticos con el fin de encontrar patrones para la realizacion de predicciones en el futuro. Se trata de un modelo dinámico de series temporales, es decir, las estimaciones futuras vienen explicadas por los datos del pasado y no por variables independientes, es por tanto un modelo Univariante.

Fue desarrollado a finales de los sesenta del siglo XX. Box y Jenkins (1976) lo sistematizaron.

El modelo ARIMA necesita identificar los coeficientes y número de regresiones que se utilizarán. La metodología de Box y Jenkins para entrenar un modelo ARIMA o SARIMA( dependiendo de si nuestra serie presenta estacionaridad) se resume en 4 pasos que nos han parecido interesante enumerar y que en la medida de los posible hemos ido siguiendo.

  1. Identificar el posible modelo ARIMA a seguir. Para ello primeramente deberemos averiguar las transformaciones a realizar para que nuestra serie sea estacionaria. Una vez hecho esto, deberemos encontrar los posibles valores de p y q correspondientes a su estructura autorregresiva (AR) y de media móvil (MA)
  2. Seleccionado provisionalmente un modelo para la serie estacionaria, se pasa a la segunda etapa de estimación, donde los parámetros AR y MA del modelo se estiman por máxima verosimilitud y se obtienen sus errores estándar y los residuos del modelo
  3. Se comprueba que los residuos contienen un formato de ruido blanco
  4. Realización de predicciones con el modelo

1.- Identificar el posible modelo ARIMA a seguir.

Ya tenemos identificado el primer parametro referente a la diferenciacion (según nuestro punto anterior de análisis de datos), en nuestro caso de primer orden. Para definir el modeo arima necesitamos establecer el parametro del modelo AR(p)- Modelo Autorregresivo. Como vimos en las graficas anteriores no presentaba autocorrelaciones,todos los valores caian dentro de la zona de confianza, hemos querido establecer un modelo arima inicial .Igualmente formas establecemos unos valores inciales y deberemos de ir probando, hasta encontrar los parametros optimos. Y vemos como se comporta.

In [0]:
from statsmodels.tsa.arima_model import ARIMA

model_ar1 = ARIMA(data['4. close'].values, order=(1,1,0))
model_fit_ar1 = model_ar1.fit(disp=0)
print(model_fit_ar1.summary())
                             ARIMA Model Results                              
==============================================================================
Dep. Variable:                    D.y   No. Observations:                 2227
Model:                 ARIMA(1, 1, 0)   Log Likelihood               -1883.579
Method:                       css-mle   S.D. of innovations              0.564
Date:                Thu, 26 Sep 2019   AIC                           3773.158
Time:                        17:43:24   BIC                           3790.283
Sample:                             1   HQIC                          3779.411
                                                                              
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.0015      0.012      0.122      0.903      -0.022       0.025
ar.L1.D.y      0.0218      0.021      1.029      0.303      -0.020       0.063
                                    Roots                                    
=============================================================================
                  Real          Imaginary           Modulus         Frequency
-----------------------------------------------------------------------------
AR.1           45.8562           +0.0000j           45.8562            0.0000
-----------------------------------------------------------------------------

2 y 3.- Etapa de estimación

En esta etapa comprobamos que no existan autocorrelaciones con el Tests Ljung-Box y Box-Pierce y que nt se trata de ruido blanco.

In [0]:
import numpy as np
from statsmodels.stats.diagnostic import acorr_ljungbox

LBBP_df = pd.DataFrame(np.column_stack(acorr_ljungbox(model_fit_ar1.resid,boxpierce=True,  lags=14)),
             columns=['LB_statistic','LB_pvalue', 'BP_statistic','BP_pvalue'])
In [0]:
display(LBBP_df)
LB_statistic LB_pvalue BP_statistic BP_pvalue
0 0.000411 0.983823 0.000411 0.983834
1 0.808118 0.667605 0.806668 0.668089
2 1.706363 0.635520 1.702897 0.636290
3 2.549012 0.635881 2.543277 0.636904
4 6.050662 0.301320 6.033925 0.302932
5 7.484566 0.278347 7.462680 0.280168
6 7.708821 0.358969 7.686029 0.361092
7 7.768895 0.456365 7.745833 0.458684
8 7.789280 0.555513 7.766118 0.557877
9 7.912844 0.637350 7.889017 0.639677
10 8.406547 0.676489 8.379839 0.678928
11 8.464470 0.747862 8.437398 0.750084
12 8.706001 0.794768 8.677302 0.796855
13 11.001088 0.685951 10.955908 0.689496

Podemos observar que los demás valores presentan no decaen tan rápidamente después del primero como cabría esperar, por ello pobraremos a modificar estos valores.

In [0]:
residuals = pd.DataFrame(model_fit_ar1.resid)
residuals.plot()
plt.show()
residuals.plot(kind='kde')
plt.show()
print(residuals.describe())
                  0
count  2.226000e+03
mean  -6.641550e-07
std    5.639685e-01
min   -2.753549e+00
25%   -3.012649e-01
50%    1.292574e-02
75%    3.164330e-01
max    4.858969e+00

Como podemos ver, en la componente estacionaria, esta cercana al ruido blanco, con una media cercana a 0 y una distribucion mas o menos normal.

3B.- Búsqueda de metaparámetros.

Una vez tenemos definido que cumple con los puntos dos y tres vamos a probar a ajustar los parámetros de AR(p) y AM(q) para ver si obtenemos un modelo que ajuste mejor.

Para ellos vamos a seguir dos procesos, uno manual y otro automático. En el proceso manual iremos probando a ajustar cada vez un modelo, ver que cumple los requisitos y probar una predicción. En el proceso automático haremos uso de autoArima.

Proceso manual

Dado que el proceso es repetitivo, simplemente ejecutar todas las celdas anteriores y hacer el forecast, no pondremos en código todo el proceso de búsqueda de metaparámetros de forma manual. La lista de los modelos probados, cumpliendo los requisitos son:

  • Con el modelo (5,1,0) error (mse) de 0.4614938000026067

  • Con el modelo (5,1,1) error (mse) de 0.3895853732933729

  • Con el modelo (7,2,1) error (mse) de 0.4661530933833349

  • Con el modelo (1,1,0) error (mse) de 0.4601377978900837

  • Con el modelo (8,1,1) error (mse) de 0.4634372479003837

  • Con el modelo (8,1,0) error (mse) de 0.4940168038836873

Por ello nos quedaremos con el modelo ARIMA (5,1,1)

In [0]:
from statsmodels.tsa.arima_model import ARIMA

model_ar1 = ARIMA(data['4. close'].values, order=(5,1,1))
model_fit_ar1 = model_ar1.fit(disp=0)
print(model_fit_ar1.summary())
                             ARIMA Model Results                              
==============================================================================
Dep. Variable:                    D.y   No. Observations:                 2226
Model:                 ARIMA(5, 1, 1)   Log Likelihood               -1879.565
Method:                       css-mle   S.D. of innovations              0.563
Date:                Wed, 25 Sep 2019   AIC                           3775.131
Time:                        19:26:25   BIC                           3820.794
Sample:                             1   HQIC                          3791.807
                                                                              
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.0014      0.012      0.117      0.906      -0.021       0.024
ar.L1.D.y     -0.3424      0.318     -1.078      0.281      -0.965       0.280
ar.L2.D.y     -0.0115      0.023     -0.488      0.625      -0.057       0.035
ar.L3.D.y     -0.0273      0.023     -1.179      0.239      -0.073       0.018
ar.L4.D.y      0.0124      0.023      0.534      0.594      -0.033       0.058
ar.L5.D.y     -0.0356      0.024     -1.474      0.141      -0.083       0.012
ma.L1.D.y      0.3659      0.317      1.153      0.249      -0.256       0.988
                                    Roots                                    
=============================================================================
                  Real          Imaginary           Modulus         Frequency
-----------------------------------------------------------------------------
AR.1           -1.5530           -0.0000j            1.5530           -0.5000
AR.2           -0.7251           -1.7891j            1.9305           -0.3113
AR.3           -0.7251           +1.7891j            1.9305            0.3113
AR.4            1.6760           -1.4298j            2.2030           -0.1124
AR.5            1.6760           +1.4298j            2.2030            0.1124
MA.1           -2.7328           +0.0000j            2.7328            0.5000
-----------------------------------------------------------------------------

Proceso automático

Dado que la búsqueda de estos parámetros puede ser algo tedioso, se creo una librería para buscar por fuerza bruta y algunos indicadores el mejor modelo. Aunque en nuestro caso cabe decir que no ha sido capaz de encontrarlo, nos ha sugerido modelos con peores resultados que el encontrado de forma manual.

In [0]:
# Ejercicio Construir una función que divida la serie original
#en training y test sets, con un percentaje dado como input
def training_test_split(series, perc):
    #print("samples total=", len(series))
    n_train = int(len(series) * perc)
    #print("samples train=", n_train)
    n_test = len(series) - n_train
    #print("samples test", int(n_test))
    train_s = list(series[:n_train])
    test_s = list(series[n_train:])
    return train_s, test_s

Utilizaremos la librería de pmdarima.

In [0]:
!pip install -q pmdarima
     |████████████████████████████████| 1.1MB 5.0MB/s 

Probamos sin componente estacional

In [0]:
import pmdarima as pm
train, test = training_test_split(data['4. close'].values, 0.90)

model = pm.auto_arima(train, test='adf',max_p=30, max_q=10 , seasonal=False,  
                      trace=True,error_action='ignore', suppress_warnings=True, stepwise=True)

print(model.summary())
Fit ARIMA: order=(2, 1, 2); AIC=3358.078, BIC=3391.692, Fit time=0.999 seconds
Fit ARIMA: order=(0, 1, 0); AIC=3366.343, BIC=3377.548, Fit time=0.004 seconds
Fit ARIMA: order=(1, 1, 0); AIC=3367.032, BIC=3383.839, Fit time=0.029 seconds
Fit ARIMA: order=(0, 1, 1); AIC=3366.997, BIC=3383.804, Fit time=0.025 seconds
Fit ARIMA: order=(1, 1, 2); AIC=nan, BIC=nan, Fit time=nan seconds
Fit ARIMA: order=(3, 1, 2); AIC=3367.738, BIC=3406.955, Fit time=1.258 seconds
Fit ARIMA: order=(2, 1, 1); AIC=3370.043, BIC=3398.055, Fit time=0.345 seconds
Fit ARIMA: order=(2, 1, 3); AIC=nan, BIC=nan, Fit time=nan seconds
Fit ARIMA: order=(1, 1, 1); AIC=3368.887, BIC=3391.296, Fit time=0.292 seconds
Fit ARIMA: order=(3, 1, 3); AIC=3370.390, BIC=3415.209, Fit time=1.471 seconds
Total fit time: 4.744 seconds
                             ARIMA Model Results                              
==============================================================================
Dep. Variable:                    D.y   No. Observations:                 2003
Model:                 ARIMA(2, 1, 2)   Log Likelihood               -1673.039
Method:                       css-mle   S.D. of innovations              0.557
Date:                Wed, 25 Sep 2019   AIC                           3358.078
Time:                        18:12:14   BIC                           3391.692
Sample:                             1   HQIC                          3370.419
                                                                              
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.0009      0.013      0.072      0.943      -0.024       0.026
ar.L1.D.y     -0.7381      0.004   -165.687      0.000      -0.747      -0.729
ar.L2.D.y     -0.9843      0.004   -229.727      0.000      -0.993      -0.976
ma.L1.D.y      0.7523      0.002    461.690      0.000       0.749       0.756
ma.L2.D.y      1.0000      0.002    409.784      0.000       0.995       1.005
                                    Roots                                    
=============================================================================
                  Real          Imaginary           Modulus         Frequency
-----------------------------------------------------------------------------
AR.1           -0.3749           -0.9356j            1.0080           -0.3107
AR.2           -0.3749           +0.9356j            1.0080            0.3107
MA.1           -0.3762           -0.9266j            1.0000           -0.3114
MA.2           -0.3762           +0.9266j            1.0000            0.3114
-----------------------------------------------------------------------------

Si probamos con componente estacional

In [0]:
model = pm.auto_arima(train, test='adf',max_p=30, max_q=10 , seasonal=True,  
                      trace=True,error_action='ignore', suppress_warnings=True, stepwise=True)

print(model.summary())
Fit ARIMA: order=(2, 1, 2) seasonal_order=(0, 0, 0, 1); AIC=3366.647, BIC=3400.261, Fit time=3.778 seconds
Fit ARIMA: order=(0, 1, 0) seasonal_order=(0, 0, 0, 1); AIC=3366.343, BIC=3377.548, Fit time=0.217 seconds
Fit ARIMA: order=(1, 1, 0) seasonal_order=(0, 0, 0, 1); AIC=3367.032, BIC=3383.839, Fit time=0.113 seconds
Fit ARIMA: order=(0, 1, 1) seasonal_order=(0, 0, 0, 1); AIC=3366.997, BIC=3383.804, Fit time=0.333 seconds
Fit ARIMA: order=(1, 1, 1) seasonal_order=(0, 0, 0, 1); AIC=3368.887, BIC=3391.297, Fit time=1.435 seconds
Total fit time: 5.896 seconds
                           Statespace Model Results                           
==============================================================================
Dep. Variable:                      y   No. Observations:                 2004
Model:               SARIMAX(0, 1, 0)   Log Likelihood               -1681.172
Date:                Wed, 25 Sep 2019   AIC                           3366.343
Time:                        18:12:20   BIC                           3377.548
Sample:                             0   HQIC                          3370.457
                               - 2004                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
intercept      0.0009      0.013      0.073      0.942      -0.024       0.026
sigma2         0.3137      0.005     57.338      0.000       0.303       0.324
===================================================================================
Ljung-Box (Q):                       39.80   Jarque-Bera (JB):              1852.83
Prob(Q):                              0.48   Prob(JB):                         0.00
Heteroskedasticity (H):               1.14   Skew:                             0.32
Prob(H) (two-sided):                  0.09   Kurtosis:                         7.67
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Como los modelos sugerido ARIMA(0,1,0) (serie random walk) y ARIMA(2,1,2) nos da peores resultados (en predicción error MSE) que el encontrado manualmente, ARIMA(5,1,1) nos quedaremos con este.

Aunque estos modelos de Autoarima mejoran el AIC no significa que siempre sea un mejor modelo. [https://otexts.com/fpp2/non-seasonal-arima.html]

Por ello nos quedaremos con el modelo encontrado de forma manual ARIMA(5,1,1)

4.- Forecast

In [0]:
model = ARIMA(data['4. close'].values, order=(1,1,0))
              #order=(5,1,1))
model_fit = model.fit(disp=0)
model_fit.plot_predict(dynamic=False)

plt.show()

Cuando establece dynamic = False, los valores rezagados en la muestra se utilizan para la predicción.

Es decir, el modelo se entrena hasta el valor anterior para hacer la siguiente predicción. Esto puede hacer que el pronóstico ajustado y los datos reales se vean artificialmente buenos.

In [0]:
from sklearn.metrics import mean_squared_error
train, test = training_test_split(data['4. close'].values, 0.90)
forecast_l =[]
history = [x for x in train]
predictions = list()

for t in range(len(test)):
    model = ARIMA(history, order=(5,1,1))
    model_fit = model.fit(disp=0)
    output = model_fit.forecast()
    yhat = output[0]
    predictions.append(yhat)
    obs = test[t]
    history.append(obs)
    if t%10==0:
      print('predicted=%f, expected=%f' % (yhat, obs))
    
predicted=35.934749, expected=36.250000
predicted=35.749847, expected=35.680000
predicted=38.547199, expected=36.520000
predicted=34.862544, expected=34.930000
predicted=32.222661, expected=33.330000
predicted=38.166648, expected=38.610000
predicted=38.773528, expected=38.930000
predicted=39.122499, expected=39.530000
predicted=39.255089, expected=39.280000
predicted=38.012640, expected=38.270000
predicted=37.770615, expected=37.810000
predicted=39.577509, expected=39.660000
predicted=38.911742, expected=38.750000
predicted=37.075892, expected=37.370000
predicted=34.847589, expected=34.820000
predicted=35.666647, expected=36.020000
predicted=38.117795, expected=38.320000
predicted=38.423563, expected=39.210000
predicted=40.722297, expected=40.770000
predicted=40.107882, expected=39.610000
predicted=37.256076, expected=36.060000
predicted=38.736773, expected=39.580000
predicted=37.462775, expected=37.240000
In [0]:
from matplotlib import pyplot
from math import sqrt

error = mean_squared_error(test, predictions)
RMSE = sqrt(error)
print('Test MSE: %.3f' % error)
print('Test RMSE: %.3f' % RMSE)
# plot
pyplot.plot(test)
pyplot.plot(predictions, color='red')
pyplot.show()
Test MSE: 0.361
Test RMSE: 0.600

Naïve methods

En algunas series temporales de precios, la mejor predicción es la última que tenemos; esto se produce por ejemplo cuando la serie sigue un modelo de Random Walk, como probablemente sea nuestro caso.[https://otexts.com/fpp2/, Capítulo 3.1]. Este tipo de algoritmo, aún a pesar de ser muy simples son extremadamente eficaces en algunas ocasiones, también de ahí su nombre de Naïve methods.

Para los modelos simples se puede hacer una persistencia diaria, semanal o mensual.

In [0]:
from sklearn.metrics import *
from math import sqrt

def smape_kun(y_true, y_pred):
	mse = mean_squared_error(y_true, y_pred)
	rmse = sqrt(mean_squared_error(y_true, y_pred))
	mape = mean_absolute_error(y_true, y_pred)
	smape = mean_squared_log_error(y_true, y_pred)
	print('MSE: %.2f'% (mse))
	print('RMSE: %.2f'% (rmse))
	print('MAPE: %.2f %% \nSMAPE: %.2f'% (mape,smape), "%")
    
def evaluate_model(model_func, train, test):
	history = [x for x in train]
	predictions = list()
	for i in range(len(test)):
		yhat_sequence = model_func(history)
		predictions.append(yhat_sequence)
		history.append(test[i])
	smape_kun(test, predictions)
	return predictions

def daily_persistence(history):
	return history[-1]
 
def weekly_persistence(history):
	return history[-7]
 
def month_persistence(history):
	return history[-30]
In [0]:
models = dict()
models['daily'] = daily_persistence
models['weekly'] = weekly_persistence
models['monthly'] = month_persistence

predictions = dict()

train_s, test_s = training_test_split(data['4. close'], 0.8)
for name, func in models.items():
	print('---->',name)
	prediction = evaluate_model(func, train_s, test_s)
	predictions[name] = prediction
----> daily
MSE: 0.46
RMSE: 0.68
MAPE: 0.48 % 
SMAPE: 0.00 %
----> weekly
MSE: 3.21
RMSE: 1.79
MAPE: 1.40 % 
SMAPE: 0.00 %
----> monthly
MSE: 10.38
RMSE: 3.22
MAPE: 2.64 % 
SMAPE: 0.01 %

Podemos observar que en la persistencia diaría, el error de MSE y RMSE no son tan bajos como en ARIMA pero no estan tan alejados.

Esto significa que la serie probablemente se acerque a Random Walk pero bajo algunos ARIMAS concretos es capaz de mejorarla. Si siguierámos ejecutando el modelo ARIMA durante una ventana de tiempo, su error probablemente se acabaría acercando a este o empeorándolo.

Random Forest

Este algoritmo desarrollado por Leo Breiman y Adele Cutler consiste en una combinación de árboles predictores con una distribución común, en el cual cada uno de estos depende de un vector aleatorio. El error general de este modelo converge hasta conseguir el número de árboles máximo o un conjunto demasiado grande.

El uso de una selección aleatoria de características para dividir cada nodo produce tasas de error que son usualmente muy favorables frente a otros modelos, por ello se ha convertido en uno de los modelos de referencia y más utilizados actualmente. Estas ideas son aplicables tanto a clasificación como a regresión.

Este algoritmo ha demostrado ser robusto a la hora de hacer predicciones en precios de mercado, sobre todo cuando estas tienen diferentes ruidos, por ello la utilizaremos como modelo de referencia para nuestra comparativa.

Random Forest sin normalizar

Para el modelo de random forest, dado que no es modelo que trabaja sólo con series temporales conservaremos los datos de entrada de volumen, precio min. La variable objetivo será predecir el precio del día siguiente.

In [0]:
from sklearn.model_selection import train_test_split
from sklearn.metrics import *
from math import sqrt

#Crear la variable objetivo del precio del día siguiente
x_data = data
x_data = x_data.drop('date', axis=1)
y_data = pd.DataFrame(x_data['4. close']).shift(-1)
y_data = y_data.ffill(axis = 0) 
x_train, x_valid, y_train, y_valid = train_test_split(x_data, y_data, test_size=0.33, shuffle=False)

x_train = x_train.to_numpy()
x_valid = x_valid.to_numpy()
y_train = y_train.to_numpy()
y_valid = y_valid.to_numpy()

#Usamos en la entrada X todas las variables de entrada, como volumen precio máximo, mínimo.
print('Xtrain_dim:', x_train.shape)
print('Ytrain_dim:', y_train.shape)

print(x_train)
Xtrain_dim: (1493, 5)
Ytrain_dim: (1493, 1)
[[3.500000e+01 3.599000e+01 3.389000e+01 3.419000e+01 4.570443e+08]
 [3.415000e+01 3.450000e+01 3.311000e+01 3.426000e+01 1.078420e+08]
 [3.420000e+01 3.448000e+01 3.381000e+01 3.408000e+01 3.665060e+07]
 ...
 [3.178000e+01 3.186000e+01 3.139000e+01 3.175000e+01 8.077600e+06]
 [3.158000e+01 3.217000e+01 3.148000e+01 3.204000e+01 1.197570e+07]
 [3.218000e+01 3.308000e+01 3.215000e+01 3.298000e+01 2.023230e+07]]
In [0]:
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression

regressor = RandomForestRegressor(
    random_state=0,
    n_estimators=300,
    max_features='auto',
    max_depth=20,
    min_samples_split=2,
    min_samples_leaf=4,
    bootstrap=True
)
regressor.fit(x_train, y_train)

print('MSE en entrenamiento es: ', mean_squared_error(y_train, regressor.predict(x_train)))
print('MSE en entrenamiento es: ', mean_squared_error(y_valid, regressor.predict(x_valid)))

y_predict = regressor.predict(x_valid)
print('MSE: ', mean_absolute_error(y_valid, y_predict))
print('RMSE: ', sqrt(mean_squared_error(y_valid, y_predict)))

print('Variance: ', explained_variance_score(y_valid, y_predict))
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:13: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples,), for example using ravel().
  del sys.path[0]
MSE en entrenamiento es:  0.13582510423004251
MSE en entrenamiento es:  1.3704929623412196
MSE:  0.7353237259704154
RMSE:  1.1706805552076192
Variance:  0.8821017779087192
Max Error:  5.448614301115555
In [0]:
import numpy as np

plt.figure(figsize=(12, 6))
plt.plot(y_valid, label='real data')
plt.plot(y_predict, label='pred data') 
Out[0]:
[<matplotlib.lines.Line2D at 0x7f45bc31d080>]

Podemos ver a simple vista que los resultados estan muy lejos de los modelos de referencia anteriores, parace que bajo algunos valores no es capaz de predecir el precio. Probemos a normalizar estos datos en busca de obtener alguna mejoría.

Random Forest normalizado

Hacemos uso del Standar Scaler para normalizar estos datos.

In [0]:
from sklearn.preprocessing import *

scaler = StandardScaler()
x_train_est = scaler.fit_transform(x_train)
y_train_est = scaler.fit_transform(y_train)
In [0]:
regressor_standarized = RandomForestRegressor(
    random_state=0,
    n_estimators=300,
    max_features='auto',
    max_depth=20,
    min_samples_split=2,
    min_samples_leaf=4,
    bootstrap=True
)
regressor_standarized.fit(x_train_est, y_train_est)

x_test_est = scaler.fit_transform(x_valid)
y_test_est = scaler.fit_transform(y_valid)

print('MSE en entrenamiento es: ', mean_squared_error(y_train_est, regressor.predict(x_train_est)))
print('MSE en validación es: ', mean_squared_error(y_test_est, regressor.predict(x_test_est)))

x_predict = regressor_standarized.predict(x_test_est)
y_predict_trans = scaler.inverse_transform(x_predict)
y_valid_trans = scaler.inverse_transform(y_test_est)

print('MSE: ', mean_squared_error(y_valid_trans, y_predict_trans))
print('RMSE: ',  sqrt(mean_squared_error(y_valid_trans, y_predict_trans)))

print('Variance: ', explained_variance_score(y_valid_trans, y_predict_trans))
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:10: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples,), for example using ravel().
  # Remove the CWD from sys.path while we load stuff.
MSE en entrenamiento es:  377.11620808353877
MSE en validación es:  377.1162080835388
MSE:  0.45509604559313466
RMSE:  0.6746080681352208
Variance:  0.955510600290243
Max Error:  4.801055852022287
In [0]:
plt.figure(figsize=(12, 6))

x_predict_trans = scaler.inverse_transform(x_predict)
y_predict_trans = scaler.inverse_transform(y_test_est)
plt.plot(y_predict_trans, label='real data')
plt.plot(x_predict_trans, label='pred data') 
Out[0]:
[<matplotlib.lines.Line2D at 0x7f2814c35eb8>]

Los resultado mejoran y nos arrojan unos errores de MSE 0.45 Y RMSE 0.67, que no estan tan alejados de aquellos errores el ARIMA de 0.36 y 0.63. El modelo de Random Forest no mejora el resultado, pero queda claro que es un modelo a tener en cuenta en el estudio de series temporales. Como antes comentabámos este tipo de modelos son muy robustos frente series temporales ya que muchas veces presentan diferentes ruidos.

Resumen

Entre los diferentes modelos probados sin duda el ARIMA ha sido el que mejores resultados ha tenido, pero los demás tampoco han resultado ser mucho peores a la hora de esta predicción, probablemente dado porque la serie temporal se parece mucho a un random - walk, y por ello NaÏve methods y random forest tienen tan buen desempeño.

  • ARIMA (5,1,1) => 0.36 (MSE), 0.63 (RMSE)
  • NAÏVE DAILY => 0.46 (MSE), 0.68 (RMSE)
  • RANDOM FOREST ESTANDARIZADO => 0.45 (MSE), 0.67 (RMSE)

Nos quedaremos entonces con el modelos de ARIMA(5,1,1) para intentar mejorarlo con redes secuenciales

Comentar