

# Importing the libraries
import commonfunctions.core
import commonfunctions.context
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import r2_score
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVR
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
import tensorflow as tf
tf.__version__




def convertToPercentageAndRound(value, roundDigits=3):
    value = value * 100
    value = round(value, roundDigits)
    return value

def decisionTreeRegression(X, y, test_size=0.2, random_state=0, *args, **kwargs):
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Training the Decision Tree Regression model on the Training set
    regressor = DecisionTreeRegressor(random_state=random_state, *args, **kwargs)
    regressor.fit(X_train, y_train)
    
    # Predicting the Test set results
    y_pred = regressor.predict(X_test)
    #np.set_printoptions(precision=2)
    #print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
    
    # Evaluating the Model Performance
    r2ScoreValue = r2_score(y_test, y_pred)
    r2ScoreValuePercentRounded =  convertToPercentageAndRound(value=r2ScoreValue, roundDigits=3)
    
    return r2ScoreValuePercentRounded, regressor


def randomForestRegression(X, y, test_size=0.2, random_state=0, *args, **kwargs):
    
    # Random Forest Regression
    # Importing the dataset
    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Training the Random Forest Regression model on the whole dataset
    regressor = RandomForestRegressor(n_estimators = 10, random_state=random_state, *args, **kwargs)
    regressor.fit(X_train, y_train)
    
    # Predicting the Test set results
    y_pred = regressor.predict(X_test)
    #np.set_printoptions(precision=2)
    #print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
    
    # Evaluating the Model Performance
    r2ScoreValue = r2_score(y_test, y_pred)
    r2ScoreValuePercentRounded =  convertToPercentageAndRound(value=r2ScoreValue, roundDigits=3)
    
    return r2ScoreValuePercentRounded, regressor


def multipleLinearRegression(X, y, test_size=0.2, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Training the Multiple Linear Regression model on the Training set
    regressor = LinearRegression(*args, **kwargs)
    regressor.fit(X_train, y_train)
    
    # Predicting the Test set results
    y_pred = regressor.predict(X_test)
    #np.set_printoptions(precision=2)
    #print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
    
    # Evaluating the Model Performance
    r2ScoreValue = r2_score(y_test, y_pred)
    r2ScoreValuePercentRounded =  convertToPercentageAndRound(value=r2ScoreValue, roundDigits=3)
    
    return r2ScoreValuePercentRounded, regressor



def polynomialRegression(X, y, test_size=0.2, random_state=0, *args, **kwargs):

    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    
    # Training the Polynomial Regression model on the Training set
    poly_reg = PolynomialFeatures(degree = 4)
    X_poly = poly_reg.fit_transform(X_train)
    regressor = LinearRegression()
    regressor.fit(X_poly, y_train)
    
    # Predicting the Test set results
    y_pred = regressor.predict(poly_reg.transform(X_test))
    #np.set_printoptions(precision=2)
    #print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
    
    # Evaluating the Model Performance
    r2ScoreValue = r2_score(y_test, y_pred)
    r2ScoreValuePercentRounded =  convertToPercentageAndRound(value=r2ScoreValue, roundDigits=3)
    
    return r2ScoreValuePercentRounded, regressor




def supportVectorRegression(X, y, test_size=0.2, random_state=0, *args, **kwargs):

    y = y.reshape(len(y),1)
    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    

    # Feature Scaling
    sc_X = StandardScaler()
    sc_y = StandardScaler()
    X_train = sc_X.fit_transform(X_train)
    y_train = sc_y.fit_transform(y_train)
    
    # Training the SVR model on the Training set
    regressor = SVR(kernel = 'rbf')
    regressor.fit(X_train, y_train)
    
    # Predicting the Test set results
    y_pred = sc_y.inverse_transform(regressor.predict(sc_X.transform(X_test)))
    #np.set_printoptions(precision=2)
    #print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
    
    # Evaluating the Model Performance
    r2ScoreValue = r2_score(y_test, y_pred)
    r2ScoreValuePercentRounded =  convertToPercentageAndRound(value=r2ScoreValue, roundDigits=3)
    
    return r2ScoreValuePercentRounded, regressor, sc_X, sc_y




def decisionTreeClassification(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    
    # Training the Decision Tree Classification model on the Training set
    classifier = DecisionTreeClassifier(criterion = 'entropy', random_state=random_state)
    classifier.fit(X_train, y_train)
    
    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc




def kNearestNeighbors(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    
    # Training the K-NN model on the Training set
    classifier = KNeighborsClassifier(n_neighbors = 5, metric = 'minkowski', p = 2)
    classifier.fit(X_train, y_train)
    
    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc



def kernelSVM(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    

    # Training the Kernel SVM model on the Training set
    classifier = SVC(kernel = 'rbf', random_state=random_state)
    classifier.fit(X_train, y_train)
    


    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc



# Logistic Regression
def logisticRegression(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    

    # Training the Logistic Regression model on the Training set
    classifier = LogisticRegression(random_state=random_state)
    classifier.fit(X_train, y_train)
    
    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc





def naiveBayes(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    

    # Training the Naive Bayes model on the Training set
    classifier = GaussianNB()
    classifier.fit(X_train, y_train)
    
    
    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc





def randomForestClassification(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    
    
    # Training the Random Forest Classification model on the Training set
    classifier = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0)
    classifier.fit(X_train, y_train)
    
    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc




def supportVectorMachine(X, y, test_size=0.25, random_state=0, *args, **kwargs):

    
    # Splitting the dataset into the Training set and Test set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
    
    # Feature Scaling
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)

    # Training the SVM model on the Training set
    classifier = SVC(kernel = 'linear', random_state = 0)
    classifier.fit(X_train, y_train)
    
    # Making the Confusion Matrix
    y_pred = classifier.predict(X_test)
    confusionMatrix = confusion_matrix(y_test, y_pred)
    #print(cm)
    accuracyScore = accuracy_score(y_test, y_pred)
    
    accuracyScorePercentRounded =  convertToPercentageAndRound(value=accuracyScore, roundDigits=3)
    
    return confusionMatrix, accuracyScorePercentRounded, classifier, sc





def artificialNeuralNetwork(X, y, test_size=0.25, random_state=0, *args, **kwargs):
    pass
    
# Artificial Neural Network

# Importing the libraries
import numpy as np
import pandas as pd


# Part 1 - Data Preprocessing

# Importing the dataset
dataset = pd.read_csv('Churn_Modelling.csv')
X = dataset.iloc[:, 3:-1].values
y = dataset.iloc[:, -1].values
print(X)
print(y)

# Encoding categorical data
# Label Encoding the "Gender" column
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
X[:, 2] = le.fit_transform(X[:, 2])
print(X)
# One Hot Encoding the "Geography" column
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
ct = ColumnTransformer(transformers=[('encoder', OneHotEncoder(), [1])], remainder='passthrough')
X = np.array(ct.fit_transform(X))
print(X)

# Feature Scaling
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X)
print(X)

# Splitting the dataset into the Training set and Test set
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)

# Part 2 - Building the ANN

# Initializing the ANN
ann = tf.keras.models.Sequential()

# Adding the input layer and the first hidden layer
ann.add(tf.keras.layers.Dense(units=6, activation='relu'))

# Adding the second hidden layer
ann.add(tf.keras.layers.Dense(units=6, activation='relu'))

# Adding the output layer
ann.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))

# Part 3 - Training the ANN

# Compiling the ANN
ann.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])

# Training the ANN on the Training set
ann.fit(X_train, y_train, batch_size = 32, epochs = 100)

# Part 4 - Making the predictions and evaluating the model

# Predicting the Test set results
y_pred = ann.predict(X_test)
y_pred = (y_pred > 0.5)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))

# Making the Confusion Matrix
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred)
print(cm)












