%matplotlib inline

import os
import json
import time
import pickle
import requests
import numpy as np
import pandas as pd

import seaborn as sns
import matplotlib.pyplot as plt
import yellowbrick as yb
sns.set_palette('RdBu', 10)

"""
Explanation: Regularization Models
Dataset Information:
Number of Features: 11
Number of Instances: 4492 (1-minute interval)  
Table of Contents<a name='table of contents'></a>

Data Ingestion
Features & Target Arrays
Ridge Regression
    a. Residuals Plot
    b. Prediction Error Plot  
RidgeCV
    a. Residuals Plot
    b. Prediction Error Plot  
LASSO
ElasticNet 
Save Model
End of explanation
"""


URL = 'https://raw.githubusercontent.com/georgetown-analytics/classroom-occupancy/master/models/sensor_data_ml.csv'

def fetch_data(fname='sensor_data_ml.csv'):
    response = requests.get(URL)
    outpath  = os.path.abspath(fname)
    with open(outpath, 'wb') as f:
        f.write(response.content)
    
    return outpath

# Defining fetching data from the URL
DATA = fetch_data()

# Import sensor data
df = pd.read_csv('sensor_data_ml.csv', index_col='datetime', parse_dates=True)

# Rename columns
df.columns = ['temp', 'humidity', 'co2', 'light', 'light_st',
              'noise', 'bluetooth', 'images', 'door', 'occupancy_count', 'occupancy_level']

df.info()
df.head()

"""
Explanation: Data Ingestion<a name='data ingestion'></a>
End of explanation
"""


# Drop 'occupancy_level' column
df.drop('occupancy_level', axis=1, inplace=True)

# Create feature and target arrays
X = df.drop('occupancy_count', axis=1).values
y = df['occupancy_count']

from sklearn.model_selection import TimeSeriesSplit

tscv = TimeSeriesSplit(n_splits=12)

for train_index, test_index in tscv.split(X):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

"""
Explanation: Features & Target Arrays<a name='features and target arrays'></a>
End of explanation
"""


from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error as mse, r2_score

# Create a ridge regression object: ridge
ridge = Ridge().fit(X_train, y_train)

# Predict on the test data: y_pred
y_pred = ridge.predict(X_test)

print('Ridge Model')
print('Root Mean Squared Error: {:.3f}'.format(np.sqrt(mse(y_test, y_pred))))
print('Mean Squared Error: {:.3f}'.format(mse(y_test, y_pred)))
print('Coefficient of Determination: {:.3f}'.format(r2_score(y_test, y_pred)))

print('ridge.coef_: {}'.format(ridge.coef_))
print('ridge.intercept_: {:.3f}'.format(ridge.intercept_))

from sklearn.model_selection import cross_val_score

# Compute 12-fold cross-validation scores: cv_scores
cv_scores = cross_val_score(ridge, X_train, y_train, cv=tscv, scoring='neg_mean_squared_error')

# Print ridge mean absolute error
print('Mean Absolute Error: {:.4f}'.format(np.mean(cv_scores)))
print('Training set score: {:.3f}'.format(ridge.score(X_train, y_train)))
print('Test set score: {:.3f}'.format(ridge.score(X_test, y_test)))

"""
Explanation: Ridge Regression<a name='ridge'></a>
End of explanation
"""


from yellowbrick.regressor import ResidualsPlot

visualizer = ResidualsPlot(ridge)

fig = plt.figure()
visualizer.fit(X_train, y_train)
visualizer.score(X_test, y_test)
g = visualizer.poof() 
fig.savefig('ml_graphs/ridge_residuals_plot.png')

"""
Explanation: Residuals Plot <a name='residuals plot'></a>
End of explanation
"""


from yellowbrick.regressor import PredictionError

visualizer = PredictionError(ridge)

fig = plt.figure()
visualizer.fit(X_train, y_train)
visualizer.score(X_test, y_test)
g = visualizer.poof()
fig.savefig('ml_graphs/ridge_prediction_error.png')

"""
Explanation: Prediction Error Plot <a name='prediction error plot'></a>
End of explanation
"""


from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

# Setup the pipeline with StandardScaler: steps
steps = [('scaler', StandardScaler()),
         ('ridge', Ridge())]

# Create the pipeline: pipeline
pipeline = Pipeline(steps)

# Fit the pipeline to the training set: logreg_scaled
ridge_scaled = pipeline.fit(X_train, y_train)

# Instantiate and fit a Logistic Regression classifier to the unscaled data: logreg_unscaled
ridge_unscaled = Ridge().fit(X_train, y_train)

# Compute and print metrics
print('Accuracy with Scaling: {:.4f}'.format(ridge_scaled.score(X_test, y_test)))
print('Accuracy without Scaling: {:.4f}'.format(ridge_unscaled.score(X_test, y_test)))

from sklearn.pipeline import make_pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import PolynomialFeatures, StandardScaler

pipe = make_pipeline(StandardScaler(),
                     PolynomialFeatures(),
                     Ridge())

param_grid = {'polynomialfeatures__degree': [0, 1, 2, 3],
              'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}

grid = GridSearchCV(estimator=pipe, param_grid=param_grid, cv=tscv, n_jobs=-1)

ridgecv = grid.fit(X_train, y_train)

ridgecv

print('Best score: {:.4f}'.format(ridgecv.best_score_))
print('Best parameters: {}'.format(ridgecv.best_params_))

y_pred = ridgecv.predict(X_test)

print('Ridge Model')
print('Root Mean Squared Error: {:.3f}'.format(np.sqrt(mse(y_test, y_pred))))
print('Mean Squared Error: {:.3f}'.format(mse(y_test, y_pred)))
print('Coefficent of Determination: {:.3f}'.format(r2_score(y_test, y_pred)))

# Compute 12-fold cross-validation scores: cv_scores
cv_scores = cross_val_score(ridgecv, X, y, cv=tscv, scoring='neg_mean_squared_error')

# Print mean absolute error score
print('Mean Absolute Error: {:.4f}'.format(np.mean(cv_scores)))

"""
Explanation: Ridge Hyperparameter Tuning<a name='ridgecv'></a>
End of explanation
"""


import pickle

ridge_model = 'ridge_regression_model.sav'

# Save fitted model to disk
pickle.dump(ridgecv, open(ridge_model, 'wb'))

"""
Explanation: Save Model<a name='pickle'></a>
End of explanation
"""


from sklearn.linear_model import Lasso
from sklearn.metrics import mean_squared_error as mse, r2_score

# Create a ridge regression object: ridge
lasso = Lasso().fit(X_train, y_train)

# Predict on the test data: y_pred
y_pred = lasso.predict(X_test)

print('LASSO Model')
print('Root Mean Squared Error: {:.3f}'.format(np.sqrt(mse(y_test, y_pred))))
print('Mean Squared Error: {:.3f}'.format(mse(y_test, y_pred)))
print('Coefficient of Determination: {:.3f}'.format(r2_score(y_test, y_pred)))

"""
Explanation: LASSO<a name='lasso'></a>
End of explanation
"""


from sklearn.linear_model import ElasticNet
from sklearn.metrics import mean_squared_error as mse, r2_score

# Create a ridge regression object: ridge
elastic = ElasticNet().fit(X_train, y_train)

# Predict on the test data: y_pred
y_pred = elastic.predict(X_test)

print('ElasticNet Model')
print('Root Mean Squared Error: {:.3f}'.format(np.sqrt(mse(y_test, y_pred))))
print('Mean Squared Error: {:.3f}'.format(mse(y_test, y_pred)))
print('Coefficient of Determination: {:.3f}'.format(r2_score(y_test, y_pred)))

"""
Explanation: ElasticNet<a name='elasticnet'></a>
End of explanation
"""


