# install latest scikit-learn if you dont have it
!pip install scikit-learn==0.23.1

#get all the necessary libraries
import pandas as pd
import pylab as pl
import numpy as np
import scipy.optimize as opt
from sklearn import preprocessing
%matplotlib inline 
import matplotlib.pyplot as plt

#get the dataset
!wget -O ChurnData.csv https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-ML0101EN-SkillsNetwork/labs/Module%203/data/ChurnData.csv
  
#convert the csv into a nice panda dataframe
churn_df = pd.read_csv("ChurnData.csv")
churn_df.head()

#select all the features that will be used for modelling
#also convert the category to be predicted into integer because that's how scikti-learn rolls

churn_df = churn_df[['tenure', 'age', 'address', 'income', 'ed', 'employ', 'equip',   'callcard', 'wireless','churn']]
churn_df['churn'] = churn_df['churn'].astype('int')
churn_df.head()

#define what should be independent and dependent (needs to be convereted into arrays, no more beautiful dataframes)
X = np.asarray(churn_df[['tenure', 'age', 'address', 'income', 'ed', 'employ', 'equip']])
X[0:5]
y = np.asarray(churn_df['churn'])
y[0:5]

#normalize the dataset of Xs, because otherwise features which are anyway big (like income) will have a disproportionate effect
#over features which are anyway small, like age

from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)
X[0:5]

#you know the drill: split into a training and a 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=4)
print ('Train set:', X_train.shape,  y_train.shape)
print ('Test set:', X_test.shape,  y_test.shape)

#now predict using scikit-learn LogisticRegression.
#This does all the work of 1 setting up initial values for theta, 2 creating an error function,
#3 calculating the error function at the values of theta, 4 using an optimization like gradient descent to understand
#whether to increase or decrease the theta value by taking the derivative of the error function at the previously picked theta
#5 updating the values of theta and 6 repeating the process a certain amount of times, or until a certain error level has been reached

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
LR = LogisticRegression(C=0.01, solver='liblinear').fit(X_train,y_train)
LR

#now use our LR model to predict using our test set
yhat = LR.predict(X_test)
yhat

#probabilities of being correct accourding to our model
yhat_prob = LR.predict_proba(X_test)
yhat_prob

#evaluate our accuracy with the jaccard score
from sklearn.metrics import jaccard_score
jaccard_score(y_test, yhat,pos_label=0)

#evaluate our model accuracy with confusion matrix instead
from sklearn.metrics import classification_report, confusion_matrix
import itertools
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix, without normalization')

    print(cm)

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
print(confusion_matrix(y_test, yhat, labels=[1,0]))

# Compute confusion matrix
cnf_matrix = confusion_matrix(y_test, yhat, labels=[1,0])
np.set_printoptions(precision=2)

# Plot non-normalized confusion matrix
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=['churn=1','churn=0'],normalize= False,  title='Confusion matrix')

#have a look at the precision, recall and f1score
print (classification_report(y_test, yhat))
#Precision is a measure of the accuracy provided that a class label has been predicted. It is defined by: precision = TP / (TP + FP)
#Recall is the true positive rate. It is defined as: Recall =  TP / (TP + FN)

#one call also measure accuracy via logoloss
from sklearn.metrics import log_loss
log_loss(y_test, yhat_prob)
