import pandas as pd
import numpy as np

from sklearn.linear_model import LinearRegression, LassoLars
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import mean_squared_error

from math import sqrt

def min_max_scale(X_train, X_validate, X_test):
    # import scaler
    from sklearn.preprocessing import MinMaxScaler
    # Create scaler object
    scaler = MinMaxScaler(copy=True).fit(X_train)
    
    # tranform into scaled data (arrays)
    X_train_scaled = scaler.transform(X_train)
    X_validate_scaled = scaler.transform(X_validate)
    X_test_scaled = scaler.transform(X_test)
    
    # Create dataframes out of the scaled arrays that were generated by the scaler tranform.
    X_train_scaled = pd.DataFrame(X_train_scaled, 
                              columns=X_train.columns.values).\
                            set_index([X_train.index.values])

    X_validate_scaled = pd.DataFrame(X_validate_scaled, 
                                columns=X_validate.columns.values).\
                            set_index([X_validate.index.values])

    X_test_scaled = pd.DataFrame(X_test_scaled, 
                                columns=X_test.columns.values).\
                            set_index([X_test.index.values])
    return X_train_scaled, X_validate_scaled, X_test_scaled


def select_kbest(X, y, n):
    '''Uses correlation to select the best k number of features 
    to use in a model'''
    # import the selector
    from sklearn.feature_selection import SelectKBest, f_regression
    # create the selector and fit it to the scaled data
    f_selector = SelectKBest(f_regression, k=n).fit(X, y)
    # get the 'selected/best' features with boolean mask
    f_support = f_selector.get_support()
    # return the actual column names of selected features into a list
    f_feature = X.iloc[:,f_support].columns.tolist()
    return f_feature

def rfe(X, y, n):
    '''Uses a linear regression model to predict the best features'''
    # import the selector
    from sklearn.feature_selection import RFE
    # import the linear regression model
    from sklearn.linear_model import LinearRegression
    # create the model object
    lm = LinearRegression()
    # create the rfe selector
    rfe = RFE(lm, n)
    # fit and transform the selector to the scaled data
    X_rfe = rfe.fit_transform(X, y)
    # get the 'best' features in a boolean mask
    mask = rfe.support_
    # map the features to the mask
    X_reduced_scaled_rfe = X.iloc[:,mask]
    # get the columns of the best features and add to a list
    f_feature = X_reduced_scaled_rfe.columns.tolist()
    return f_feature


def regression_errors(actual, predicted):
    from sklearn.metrics import mean_squared_error
    from math import sqrt

    MSE = mean_squared_error(actual, predicted)
    SSE = MSE*len(actual)
    ESS = sum((predicted - actual.mean())**2)
    TSS = ESS + SSE
    RMSE = sqrt(MSE)
    return SSE, ESS, TSS, MSE, RMSE


def baseline_mean_errors(actual):
    from sklearn.metrics import mean_squared_error
    from math import sqrt
    
    df = pd.DataFrame()
    df['y'] = actual
    df['baseline'] = actual.mean()
    
    MSE = mean_squared_error(actual, df.baseline)
    SSE = MSE*len(actual)
    RMSE = sqrt(MSE)
    return SSE, MSE, RMSE

def linear_regression(X, y):
    # fit the model
    lm = LinearRegression(normalize=True)\
    .fit(X, y)

    # predict train observations
    lm_pred = lm.predict(X)

    # evaluate train: compute root mean squared error
    lm_rmse = sqrt(mean_squared_error(y, lm_pred))
    return lm_rmse

def lasso_lars(X,y):
    #train model
    lars = LassoLars(alpha=0.1)\
    .fit(X, y)

    lars_pred = lars.predict(X)

    lars_rmse = sqrt(mean_squared_error(y, lars_pred))
    return lars_rmse

def polynomial_regression(X, y, n):
    # make the polynomial thing
    pf = PolynomialFeatures(degree=n)

    # fit and transform the thing
    # to get a new set of features..which are the original features sqauared
    X_train_squared = pf.fit_transform(X)

    # feed that data into our linear model. 
    # make the thing
    lm_squared = LinearRegression()
    lm_squared.fit(X_train_squared, y)

    # predict training observervations
    lm_squared_pred = lm_squared.predict(X_train_squared)

    # Evaluate our training predictions
    lm_squared_rmse = sqrt(mean_squared_error(y, lm_squared_pred))
    return lm_squared_rmse

######################### validate rmse ######################################

def lr_validate_test(X, y, X_vt, y_vt):
    # fit the model
    lm = LinearRegression(normalize=True)\
    .fit(X, y)

    #predict validate observations
    lm_pred_v = lm.predict(X_vt)

    # evaluate validate: compute root mean squared error
    lm_rmse_v = sqrt(mean_squared_error(y_vt, lm_pred_v))
    return lm_rmse_v

def ll_validate_test(X, y, X_vt, y_vt):
    #train model
    lars = LassoLars(alpha=0.1)\
    .fit(X, y)

    #validate model
    lars_pred_v = lars.predict(X_vt)

    lars_rmse_v = sqrt(mean_squared_error(y_vt, lars_pred_v))
    return lars_rmse_v

def pr_validate_test(X, y, n, X_vt, y_vt):
    # make the polynomial thing
    pf = PolynomialFeatures(degree=n)

    # fit and transform the thing
    # to get a new set of features..which are the original features sqauared
    X_train_squared = pf.fit_transform(X)
    X_validate_squared = pf.transform(X_vt)

    # feed that data into our linear model. 
    # make the thing
    lm_squared = LinearRegression()
    lm_squared.fit(X_train_squared, y)

    # validate

    lm_squared_pred_v = lm_squared.predict(X_validate_squared)

    lm_squared_rmse_v = sqrt(mean_squared_error(y_vt, lm_squared_pred_v))
    return lm_squared_rmse_v
    