# -*- coding: utf-8 -*-
"""
Created on Fri Aug 31 18:33:58 2018

@author: Marios Michailidis

metrics and method to check metrics used within StackNet

"""

from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score , mean_squared_log_error  #regression metrics
from sklearn.metrics import roc_auc_score, log_loss ,accuracy_score, f1_score ,matthews_corrcoef
import numpy as np

valid_regression_metrics=["rmse","mae","rmsle","r2","mape","smape"]
valid_classification_metrics=["auc","logloss","accuracy","f1","matthews"]

############ classification metrics ############

def auc(y_true, y_pred, sample_weight=None):    
    return roc_auc_score(y_true, y_pred, sample_weight=sample_weight)

def logloss(y_true, y_pred, sample_weight=None, labels = None):  
    return log_loss(y_true, y_pred, sample_weight=sample_weight, labels = labels)

def accuracy(y_true, y_pred, sample_weight=None):    
    return accuracy_score(y_true, y_pred, sample_weight=sample_weight)

def f1(y_true, y_pred, sample_weight=None):    
    return f1_score(y_true, y_pred, sample_weight=sample_weight)

def matthews(y_true, y_pred, sample_weight=None):    
    return matthews_corrcoef(y_true, y_pred, sample_weight=sample_weight)

############ regression metrics ############

def rmse(y_true, y_pred, sample_weight=None):    
    return np.sqrt(mean_squared_error(y_true, y_pred, sample_weight=sample_weight))

def mae(y_true, y_pred, sample_weight=None):    
    return mean_absolute_error(y_true, y_pred, sample_weight=sample_weight)

def rmsle (y_true, y_pred, sample_weight=None):    
    return np.sqrt(mean_squared_log_error(y_true, y_pred, sample_weight=sample_weight))

def r2(y_true, y_pred, sample_weight=None):    
    return r2_score(y_true, y_pred, sample_weight=sample_weight)


def mape(y_true, y_pred, sample_weight=None):
    y_true = y_true.ravel()
    y_pred = y_pred.ravel()
    if sample_weight is not None:
        sample_weight = sample_weight.ravel()
    eps = 1E-15
    ape = np.abs((y_true - y_pred) / (y_true + eps)) * 100
    ape[y_true == 0] = 0
    return np.average(ape, weights=sample_weight)


def smape(y_true, y_pred, sample_weight=None):
    
    y_true = y_true.ravel()
    y_pred = y_pred.ravel()
    if sample_weight is not None:
        sample_weight = sample_weight.ravel()
    eps = 1E-15
    sape = (np.abs(y_true - y_pred) / (0.5 * (np.abs(y_true) + np.abs(y_pred)) + eps)) * 100
    sape[(y_true == 0) & (y_pred == 0)] = 0
    return np.average(sape, weights=sample_weight)         


"""
metric: string or class that returns a metric given (y_true, y_pred, sample_weight=None)
Curently supported metrics are "rmse","mae","rmsle","r2","mape","smape"
"""


def check_regression_metric(metric):
    
    if type(metric) is type(None):
        raise Exception ("metric cannot be None")
    if isinstance(metric, str)  :
        if metric not in valid_regression_metrics:
            raise Exception ("The regression metric has to be one of %s " % (", ".join([str(k) for k in valid_regression_metrics])))
        if metric=="rmse":
            return rmse,metric
        elif metric=="mae":
            return mae,metric
        elif metric=="rmsle":
            return rmsle,metric       
        elif metric=="r2":
            return r2,metric      
        elif metric=="mape":
            return mape,metric      
        elif metric=="smape":
            return smape,metric    
        else :
            raise Exception ("The metric %s is not recognised " % (metric) ) 
    else : #customer metrics is given
        try:
            y_true_temp=[[1],[2],[3]]
            y_pred_temp=[[2],[1],[3]]
            y_true_temp=np.array(y_true_temp)
            y_pred_temp=np.array(y_pred_temp)            
            sample_weight_temp=[1,0.5,1]
            metric(y_true_temp,y_pred_temp,  sample_weight=sample_weight_temp )
            return metric,"custom"
            
        except:
            raise Exception ("The custom metric has to implement metric(y_true, y_pred, sample_weight=None)" ) 
            
            
"""
metric: string or class that returns a metric given (y_true, y_pred, sample_weight=None)
Curently supported metrics are "rmse","mae","rmsle","r2","mape","smape"
"""


def check_classification_metric(metric):
    
    if type(metric) is type(None):
        raise Exception ("metric cannot be None")
    if isinstance(metric, str)  :
        if metric not in valid_classification_metrics:
            raise Exception ("The classification metric has to be one of %s " % (", ".join([str(k) for k in valid_classification_metrics])))
        if metric=="auc":
            return auc,metric
        elif metric=="logloss":
            return logloss,metric
        elif metric=="accuracy":
            return accuracy,metric       
        elif metric=="r2":
            return r2,metric      
        elif metric=="f1":
            return f1,metric      
        elif metric=="matthews":
            return matthews,metric    
        else :
            raise Exception ("The metric %s is not recognised " % (metric) ) 
    else : #customer metrics is given
        try:
            y_true_temp=[[1],[0],[1]]
            y_pred_temp=[[0.4],[1],[0.2]]
            y_true_temp=np.array(y_true_temp)
            y_pred_temp=np.array(y_pred_temp)
            sample_weight_temp=[1,0.5,1]
            metric(y_true_temp,y_pred_temp,  sample_weight=sample_weight_temp )
            return metric,"custom"
            
        except:
            raise Exception ("The custom metric has to implement metric(y_true, y_pred, sample_weight=None)" ) 
            
                        
            
            
            
[((21, 11, 21, 69), 'sklearn.metrics.roc_auc_score', 'roc_auc_score', (), '', False, 'from sklearn.metrics import roc_auc_score, log_loss, accuracy_score, f1_score, matthews_corrcoef\n'), ((24, 11, 24, 81), 'sklearn.metrics.log_loss', 'log_loss', (), '', False, 'from sklearn.metrics import roc_auc_score, log_loss, accuracy_score, f1_score, matthews_corrcoef\n'), ((27, 11, 27, 70), 'sklearn.metrics.accuracy_score', 'accuracy_score', (), '', False, 'from sklearn.metrics import roc_auc_score, log_loss, accuracy_score, f1_score, matthews_corrcoef\n'), ((30, 11, 30, 64), 'sklearn.metrics.f1_score', 'f1_score', (), '', False, 'from sklearn.metrics import roc_auc_score, log_loss, accuracy_score, f1_score, matthews_corrcoef\n'), ((33, 11, 33, 73), 'sklearn.metrics.matthews_corrcoef', 'matthews_corrcoef', (), '', False, 'from sklearn.metrics import roc_auc_score, log_loss, accuracy_score, f1_score, matthews_corrcoef\n'), ((41, 11, 41, 75), 'sklearn.metrics.mean_absolute_error', 'mean_absolute_error', (), '', False, 'from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_squared_log_error\n'), ((47, 11, 47, 64), 'sklearn.metrics.r2_score', 'r2_score', (), '', False, 'from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_squared_log_error\n'), ((58, 11, 58, 49), 'numpy.average', 'np.average', (), '', True, 'import numpy as np\n'), ((70, 11, 70, 50), 'numpy.average', 'np.average', (), '', True, 'import numpy as np\n'), ((38, 19, 38, 82), 'sklearn.metrics.mean_squared_error', 'mean_squared_error', (), '', False, 'from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_squared_log_error\n'), ((44, 19, 44, 86), 'sklearn.metrics.mean_squared_log_error', 'mean_squared_log_error', (), '', False, 'from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_squared_log_error\n'), ((56, 10, 56, 52), 'numpy.abs', 'np.abs', ({(56, 17, 56, 51): '((y_true - y_pred) / (y_true + eps))'}, {}), '((y_true - y_pred) / (y_true + eps))', True, 'import numpy as np\n'), ((68, 12, 68, 35), 'numpy.abs', 'np.abs', ({(68, 19, 68, 34): '(y_true - y_pred)'}, {}), '(y_true - y_pred)', True, 'import numpy as np\n'), ((104, 24, 104, 45), 'numpy.array', 'np.array', ({(104, 33, 104, 44): 'y_true_temp'}, {}), '(y_true_temp)', True, 'import numpy as np\n'), ((105, 24, 105, 45), 'numpy.array', 'np.array', ({(105, 33, 105, 44): 'y_pred_temp'}, {}), '(y_pred_temp)', True, 'import numpy as np\n'), ((145, 24, 145, 45), 'numpy.array', 'np.array', ({(145, 33, 145, 44): 'y_true_temp'}, {}), '(y_true_temp)', True, 'import numpy as np\n'), ((146, 24, 146, 45), 'numpy.array', 'np.array', ({(146, 33, 146, 44): 'y_pred_temp'}, {}), '(y_pred_temp)', True, 'import numpy as np\n'), ((68, 46, 68, 60), 'numpy.abs', 'np.abs', ({(68, 53, 68, 59): 'y_true'}, {}), '(y_true)', True, 'import numpy as np\n'), ((68, 63, 68, 77), 'numpy.abs', 'np.abs', ({(68, 70, 68, 76): 'y_pred'}, {}), '(y_pred)', True, 'import numpy as np\n')]