"""
# Data Science - Job Hunters
"""
"""
#### We will be doing the following things in this notebook - 

* **Predicting the probability of a prospective candidate to apply for a Data Science job**
* **Improving our Predictions for a better result**
"""
"""
Let's however first start with understanding what we are working with. For that, we first need to import our datasets.
"""
"""
## Understanding our Data
"""
"""
### Importing Libraries
"""
import pandas as pd
import numpy as np

import matplotlib as mpl
import matplotlib.pyplot as plt
import plotly.express as px
import seaborn as sns
import plotly.graph_objects as go
import plotly.express as px
import plotly.figure_factory as ff
from plotly.subplots import make_subplots

from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn.preprocessing import LabelBinarizer, LabelEncoder
from sklearn.metrics import precision_score

from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import LinearSVC

from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import NearMiss

import tensorflow as tf
import keras
import warnings
from tensorflow.keras import layers
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense, ThresholdedReLU
from keras.optimizers import Adam
from keras import regularizers
from keras.utils.np_utils import to_categorical

from lightgbm import LGBMClassifier

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
"""
Now that our libraries are imported, let's import our dataset.
"""
"""
### Importing Datasets
"""
data_filepath = "../input/hr-analytics-job-change-of-data-scientists/aug_train.csv"
data = pd.read_csv(data_filepath)

test_filepath = "../input/hr-analytics-job-change-of-data-scientists/aug_test.csv"
test = pd.read_csv(test_filepath)

data.sample(10)
"""
We have our dataset. Couple of things we need to understand before we proceed further.
"""
"""
#### Let's explore what information each column has to offer


* **enrollee_id** : Unique ID for candidate

* **city** : City code

* **city_development_index** : Developement index of the city (scaled)

* **gender** : Gender of candidate

* **relevent_experience** : Relevant experience of candidate

* **enrolled_university** : Type of University course enrolled if any

* **education_level** : Education level of candidate

* **major_discipline** : Education major discipline of candidate

* **experience** : Candidate total experience in years

* **company_size** : No of employees in current employer's company

* **company_type** : Type of current employer

* **last_new_job** : Difference in years between previous job and current job

* **training_hours** : training hours completed

* **target** : 0 – *Not looking for job change*, 1 – *Looking for a job change*
"""
"""
### Checking for Null Values
"""
data.isnull().sum(axis = 0).sort_values(ascending=False)
"""
There are a couple of columns that have null values. 

Normally, this would be resolved by either simply **dropping the columns** with a higher number of null values, or complicating our work with an algorithm like **Nearest Neighbours**.

In this case, however, we will treat the null values as their own entity and designate them with a numerical value, or a string value such as **Unspecified**.
"""
"""
### Treating Null Values
"""
data['company_type'] = data['company_type'].fillna("Unspecified")

data['company_size'] = data['company_size'].fillna(0)
dict_company_size = {'<10': 1, '10/49': 2, '50-99': 3, '100-500': 4, '500-999': 5, '1000-4999': 6, '5000-9999': 7, '10000+': 8}
data = data.replace({"company_size": dict_company_size})

data['gender'] = data['gender'].fillna(0)
dict_gender = {'Male': 1, 'Female': 2, 'Other': 3}
data = data.replace({"gender": dict_gender})

data['major_discipline'] = data['major_discipline'].fillna("Unspecified")

data['education_level'] = data['education_level'].fillna("Unspecified")

data['last_new_job'] = data['last_new_job'].fillna(0)
dict_last_new_job = {'>4': 5, 'never': 6}
data = data.replace({"last_new_job": dict_last_new_job})

data['enrolled_university'] = data['enrolled_university'].fillna("Unspecified")

data['experience'] = data['experience'].fillna(22)
dict_experience = {'<1': 0, '>20': 21}
data = data.replace({"experience": dict_experience})

col_nums = ['company_size', 'gender', 'last_new_job', 'experience', 'enrollee_id', 'city_development_index', 'training_hours', 'target']
data[col_nums] = data[col_nums].apply(pd.to_numeric, errors='coerce')
data['target'] = data['target'].astype(int)

columns = data.columns
for i in columns:
    print("\n{} :\n{}".format(i, data[i].unique()))
"""
Our dataset does not seem to have any null values anymore.

We went a step further and converted all **numerical columns** to **numerical datatypes**.

We can now explore our training dataset below.
"""
data.info()
"""
## Predicting the probability of a prospective candidate to apply for a Data Science job
"""
"""
This is the part where we use relevant factors to process our data to create prediction models.

For this, we will use **Classification** techniques, since we have to **categorise people** into job takers and non-takers.

Before that, we will process our data, as shown below.
"""
"""
### Data Selection and Splitting
"""
X_for_labelencoding = data[['city_development_index', 'gender', 'city', 'relevent_experience', 'enrolled_university', 'education_level', 'major_discipline', 'company_type', 'company_size', 'experience', 'last_new_job', 'training_hours']]
X = X_for_labelencoding.copy()
y = data['target']

labelencoder = LabelEncoder()

str_cols = X_for_labelencoding.iloc[:, 2:8]
clfs = {c:labelencoder for c in str_cols}

for col, clf in clfs.items():
    X[col] = clfs[col].fit_transform(X_for_labelencoding[col])

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=30)
"""
#### The ***LabelEncoder***

Machine Learning algorithms understand the **numbers** and **not texts**. Hence, all the **text values** must be **converted** into **numerical values** to make it understandable for the algorithm.

**Categorical variables** represent types of data which may be **divided into groups**. It has a limited and usually **fixed number** of possible values called **categories**. But, if this data is encoded into **numerical values** then only it can be processed in a machine learning algorithm.

**Label encoding** is a simple and straight forward approach. This **converts** each value in a **categorical column** into a **numerical value**. Each value in a categorical column is called ***Label***.
"""
"""
#### The ***train_test_split***

The train-test split is a technique for evaluating the performance of a machine learning algorithm.

It can be used for classification or regression problems and can be used for any supervised learning algorithm.

The procedure involves taking a dataset and dividing it into two subsets. The first subset is used to fit the model and is referred to as the training dataset. The second subset is not used to train the model; instead, the input element of the dataset is provided to the model, then predictions are made and compared to the expected values. This second dataset is referred to as the test dataset.

* **Train Dataset** : Used to fit the machine learning model.
* **Test Dataset** : Used to evaluate the fit machine learning model.

The objective is to estimate the performance of the machine learning model on new data: data not used to train the model.
"""
"""
### Applying Standard Scaler
"""
"""
Variables that are **measured at different scales** do **not** contribute equally to the **model fitting** & **model learned function** and might end up **creating a bias**. 
Thus, to deal with this potential problem **feature-wise** **standardized (μ=0, σ=1)** is usually used **prior to model fitting**.

The main idea is to **normalize/standardize i.e. μ = 0 and σ = 1** your **features/variables/columns** of X, individually, before applying any machine learning model. Thus, **StandardScaler()** will **normalize** the features i.e. each column of X, **INDIVIDUALLY** so that each **column/feature/variable** will have μ = 0 and σ = 1

"""
sc = StandardScaler()
X_train = sc.fit_transform(X_train.astype(np.float32))
X_val = sc.transform(X_val.astype(np.float32))

X_train_sm_cp, X_val_sm, y_train_sm_cp, y_val_sm = X_train, X_val, y_train, y_val
X_train_nn, X_val_nn, y_train_nn, y_val_nn = X_train, X_val, y_train, y_val

y_train_nn = to_categorical(y_train_nn, 2)
y_val_nn = to_categorical(y_val_nn, 2)
"""
### Algorithm 1 - Logistic Regression

<br>

[Learn more here](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html)
"""
lr = LogisticRegression(max_iter = 10000)
lr.fit(X_train, y_train)

y_pred = lr.predict(X_val)
pipe_lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000, random_state=1))
pipe_lr.fit(X_train, y_train)
lr_pacs = pipe_lr.score(X_val, y_val)*100
print('Logistic Regression -')
print('   Pipeline Accuracy Score = ', lr_pacs)

logreg_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_logreg = confusion_matrix(y_val, y_pred)
"""
### Algorithm 2 - K-Nearest Neighbours Classification

<br>

[Learn more here](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html)
"""
knn = KNeighborsClassifier(n_neighbors = 5, metric = 'manhattan', p = 1)
knn.fit(X_train,y_train)

y_pred = knn.predict(X_val)
pipe_knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors = 5, metric = 'euclidean', p = 2))
pipe_knn.fit(X_train, y_train)
knn_pacs = pipe_knn.score(X_val, y_val)*100
print('K-Nearest Neighbours -')
print('   Pipeline Accuracy Score = ', knn_pacs)

knn_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_knn = confusion_matrix(y_val, y_pred)
"""
### Algorithm 3 - Support Vector Classification

<br>

[Learn more here](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html)
"""
svc = LinearSVC(max_iter=11000)
svc.fit(X_train,y_train)

y_pred = svc.predict(X_val)
pipe_svc = make_pipeline(StandardScaler(), LinearSVC(penalty='l2', loss='squared_hinge', class_weight='balanced', verbose=0, random_state=101))
pipe_svc.fit(X_train, y_train)
svc_pacs = pipe_svc.score(X_val, y_val)*100
print('Support Vector Classifier -')
print('   Pipeline Accuracy Score = ', svc_pacs)

svc_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_svc = confusion_matrix(y_val, y_pred)
"""
### Algorithm 4 - Gaussian Naive Bayes

<br>

[Learn more here](https://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.GaussianNB.html)
"""
nb = GaussianNB()
nb.fit(X_train,y_train)

y_pred = nb.predict(X_val)
pipe_gnb = make_pipeline(StandardScaler(), GaussianNB())
pipe_gnb.fit(X_train, y_train)
nb_pacs = pipe_gnb.score(X_val, y_val)*100
print('Gaussian Naive Bayes -')
print('   Pipeline Accuracy Score = ', nb_pacs)

nb_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_nb = confusion_matrix(y_val, y_pred)
"""
### Algorithm 5 - Decision Tree Classification

<br>

[Learn more here](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html)
"""
dt = DecisionTreeClassifier(criterion = 'entropy')
dt.fit(X_train,y_train)

y_pred = dt.predict(X_val)
pipe_dtc = make_pipeline(StandardScaler(), DecisionTreeClassifier(criterion='entropy', max_depth=3))
pipe_dtc.fit(X_train, y_train)
dt_pacs = pipe_dtc.score(X_val, y_val)*100
print('Decision Tree Classifier -')
print('   Pipeline Accuracy Score = ', dt_pacs)

dt_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_dt = confusion_matrix(y_val, y_pred)
"""
### Algorithm 6 - Random Forest Classification

<br>

[Learn more here](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
"""
rf = RandomForestClassifier()
rf.fit(X_train,y_train)

y_pred = rf.predict(X_val)
pipe_rft = make_pipeline(StandardScaler(), RandomForestClassifier(criterion='entropy', random_state=0))
pipe_rft.fit(X_train, y_train)
rf_pacs = pipe_rft.score(X_val, y_val)*100
print('Random Forest Classifier -')
print('   Pipeline Accuracy Score = ', rf_pacs)

rf_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_rf = confusion_matrix(y_val, y_pred)
"""
### **Classification Report**
"""
"""
The classification report visualizer displays the **precision**, **recall**, **F1**, and **support scores** for the model.

"""
"""

There are four ways to check if the predictions are right or wrong:

* **TN / True Negative** : the case was **negative** and **predicted negative**
* **TP / True Positive** : the case was **positive** and **predicted positive**
* **FN / False Negative** : the case was **positive** but **predicted negative**
* **FP / False Positive** : the case was **negative** but **predicted positive**

<br>

#### **Precision** — ***What percent of predictions were correct?***

**Precision** is the ability of a classifier **not to label an instance positive that is actually negative**. For each class, it is defined as **the ratio of true positives** to **the sum of a true positive and false positive**.


**Precision = TP/(TP + FP)**

<br>

#### **Recall** — ***What percent of the positive cases were caught?***

**Recall** is the ability of a classifier to **find all positive instances**. For each class it is defined as **the ratio of true positives** to the **sum of true positives** and **false negatives**.


**Recall = TP/(TP+FN)**

<br>

#### **F1 score** — ***What percent of positive predictions were correct?***

The **F1 score** is a **weighted harmonic mean** of precision and recall such that the **best score is 1.0** and the **worst score is 0.0**. F1 scores are **lower than accuracy measures** as they **embed precision** and recall into their computation.


**F1 Score = 2*(Recall * Precision) / (Recall + Precision)**

<br>

#### **Support**


**Support** is the number of **actual occurrences** of the class in the specified dataset. **Imbalanced support** in the training data may indicate **structural weaknesses** in the **reported scores** of the classifier and could indicate the need for **stratified sampling** or **rebalancing**. Support **doesn’t change** between models but instead **diagnoses the evaluation process**.
"""
clf_lgrg_df = pd.DataFrame(logreg_clf_report).transpose().rename_axis('parameters').reset_index()
clf_knn_df = pd.DataFrame(knn_clf_report).transpose().rename_axis('parameters').reset_index()
clf_svc_df = pd.DataFrame(svc_clf_report).transpose().rename_axis('parameters').reset_index()
clf_nb_df = pd.DataFrame(nb_clf_report).transpose().rename_axis('parameters').reset_index()
clf_dt_df = pd.DataFrame(dt_clf_report).transpose().rename_axis('parameters').reset_index()
clf_rf_df = pd.DataFrame(rf_clf_report).transpose().rename_axis('parameters').reset_index()

prec0_x = [clf_lgrg_df.iloc[0]['precision'], clf_knn_df.iloc[0]['precision'], clf_svc_df.iloc[0]['precision'], clf_nb_df.iloc[0]['precision'], clf_dt_df.iloc[0]['precision'], clf_rf_df.iloc[0]['precision']]
prec1_x = [clf_lgrg_df.iloc[1]['precision'], clf_knn_df.iloc[1]['precision'], clf_svc_df.iloc[1]['precision'], clf_nb_df.iloc[1]['precision'], clf_dt_df.iloc[1]['precision'], clf_rf_df.iloc[1]['precision']]
precac_x = [clf_lgrg_df.iloc[2]['precision'], clf_knn_df.iloc[2]['precision'], clf_svc_df.iloc[2]['precision'], clf_nb_df.iloc[2]['precision'], clf_dt_df.iloc[2]['precision'], clf_rf_df.iloc[2]['precision']]
precma_x = [clf_lgrg_df.iloc[3]['precision'], clf_knn_df.iloc[3]['precision'], clf_svc_df.iloc[3]['precision'], clf_nb_df.iloc[3]['precision'], clf_dt_df.iloc[3]['precision'], clf_rf_df.iloc[3]['precision']]
precwe_x = [clf_lgrg_df.iloc[4]['precision'], clf_knn_df.iloc[4]['precision'], clf_svc_df.iloc[4]['precision'], clf_nb_df.iloc[4]['precision'], clf_dt_df.iloc[4]['precision'], clf_rf_df.iloc[4]['precision']]

rec0_x = [clf_lgrg_df.iloc[0]['recall'], clf_knn_df.iloc[0]['recall'], clf_svc_df.iloc[0]['recall'], clf_nb_df.iloc[0]['recall'], clf_dt_df.iloc[0]['recall'], clf_rf_df.iloc[0]['recall']]
rec1_x = [clf_lgrg_df.iloc[1]['recall'], clf_knn_df.iloc[1]['recall'], clf_svc_df.iloc[1]['recall'], clf_nb_df.iloc[1]['recall'], clf_dt_df.iloc[1]['recall'], clf_rf_df.iloc[1]['recall']]
recac_x = [clf_lgrg_df.iloc[2]['recall'], clf_knn_df.iloc[2]['recall'], clf_svc_df.iloc[2]['recall'], clf_nb_df.iloc[2]['recall'], clf_dt_df.iloc[2]['recall'], clf_rf_df.iloc[2]['recall']]
recma_x = [clf_lgrg_df.iloc[3]['recall'], clf_knn_df.iloc[3]['recall'], clf_svc_df.iloc[3]['recall'], clf_nb_df.iloc[3]['recall'], clf_dt_df.iloc[3]['recall'], clf_rf_df.iloc[3]['recall']]
recwe_x = [clf_lgrg_df.iloc[4]['recall'], clf_knn_df.iloc[4]['recall'], clf_svc_df.iloc[4]['recall'], clf_nb_df.iloc[4]['recall'], clf_dt_df.iloc[4]['recall'], clf_rf_df.iloc[4]['recall']]

f10_x = [clf_lgrg_df.iloc[0]['f1-score'], clf_knn_df.iloc[0]['f1-score'], clf_svc_df.iloc[0]['f1-score'], clf_nb_df.iloc[0]['f1-score'], clf_dt_df.iloc[0]['f1-score'], clf_rf_df.iloc[0]['f1-score']]
f11_x = [clf_lgrg_df.iloc[1]['f1-score'], clf_knn_df.iloc[1]['f1-score'], clf_svc_df.iloc[1]['f1-score'], clf_nb_df.iloc[1]['f1-score'], clf_dt_df.iloc[1]['f1-score'], clf_rf_df.iloc[1]['f1-score']]
f1ac_x = [clf_lgrg_df.iloc[2]['f1-score'], clf_knn_df.iloc[2]['f1-score'], clf_svc_df.iloc[2]['f1-score'], clf_nb_df.iloc[2]['f1-score'], clf_dt_df.iloc[2]['f1-score'], clf_rf_df.iloc[2]['f1-score']]
f1ma_x = [clf_lgrg_df.iloc[3]['f1-score'], clf_knn_df.iloc[3]['f1-score'], clf_svc_df.iloc[3]['f1-score'], clf_nb_df.iloc[3]['f1-score'], clf_dt_df.iloc[3]['f1-score'], clf_rf_df.iloc[3]['f1-score']]
f1we_x = [clf_lgrg_df.iloc[4]['f1-score'], clf_knn_df.iloc[4]['f1-score'], clf_svc_df.iloc[4]['f1-score'], clf_nb_df.iloc[4]['f1-score'], clf_dt_df.iloc[4]['f1-score'], clf_rf_df.iloc[4]['f1-score']]

sup0_x = [clf_lgrg_df.iloc[0]['support'], clf_knn_df.iloc[0]['support'], clf_svc_df.iloc[0]['support'], clf_nb_df.iloc[0]['support'], clf_dt_df.iloc[0]['support'], clf_rf_df.iloc[0]['support']]
sup1_x = [clf_lgrg_df.iloc[1]['support'], clf_knn_df.iloc[1]['support'], clf_svc_df.iloc[1]['support'], clf_nb_df.iloc[1]['support'], clf_dt_df.iloc[1]['support'], clf_rf_df.iloc[1]['support']]
supac_x = [clf_lgrg_df.iloc[2]['support'], clf_knn_df.iloc[2]['support'], clf_svc_df.iloc[2]['support'], clf_nb_df.iloc[2]['support'], clf_dt_df.iloc[2]['support'], clf_rf_df.iloc[2]['support']]
supma_x = [clf_lgrg_df.iloc[3]['support'], clf_knn_df.iloc[3]['support'], clf_svc_df.iloc[3]['support'], clf_nb_df.iloc[3]['support'], clf_dt_df.iloc[3]['support'], clf_rf_df.iloc[3]['support']]
supwe_x = [clf_lgrg_df.iloc[4]['support'], clf_knn_df.iloc[4]['support'], clf_svc_df.iloc[4]['support'], clf_nb_df.iloc[4]['support'], clf_dt_df.iloc[4]['support'], clf_rf_df.iloc[4]['support']]

classifiers = ["Logistic Regression", "K-Nearest Neighbours", "Support Vector Classifier", "Gaussian Naive-Bayes", "Decision Tree Classifier", "Random Forest Classifier"]

fig = plt.figure(figsize=(10,30), dpi=150)
gs = fig.add_gridspec(19, 3)
gs.update(wspace=0.2, hspace=0.5)
sns.set_theme(style="whitegrid")
sns.despine(bottom=True, right=True)
mpl.rcParams['xtick.labelsize'] = 6
mpl.rcParams['ytick.labelsize'] = 7

axs1 = fig.add_subplot(gs[0, 0])
axs2 = fig.add_subplot(gs[0, 1])
axs3 = fig.add_subplot(gs[0, 2])
axs5 = fig.add_subplot(gs[1, 0])
axs6 = fig.add_subplot(gs[1, 1])
axs7 = fig.add_subplot(gs[1, 2])
axs9 = fig.add_subplot(gs[2, 0])
axs10 = fig.add_subplot(gs[2, 1])
axs11 = fig.add_subplot(gs[2, 2])
axs13 = fig.add_subplot(gs[3, 0])
axs14 = fig.add_subplot(gs[3, 1])
axs15 = fig.add_subplot(gs[3, 2])
axs17 = fig.add_subplot(gs[4, 0])
axs18 = fig.add_subplot(gs[4, 1])
axs19 = fig.add_subplot(gs[4, 2])


colors_prec = [0.1, 0.2, 0.3, 0.4]
colors_rec = [0.2, 0.4, 0.6, 0.8]
colors_f1 = [0.5, 0.625, 0.75, 0.875]

axs1.set(title = "Precision\n\nMajority Class")
clfplt1 = sns.barplot(x=[i for i in prec0_x], y=classifiers, ax=axs1, color=colors_prec)
clfplt1.set(xticklabels=[])

axs2.set(title = "Recall\n\nMajority Class")
clfplt2 = sns.barplot(x=[i for i in rec0_x], y=classifiers, ax=axs2, color=colors_rec)
clfplt2.set(xticklabels=[], yticklabels=[])

axs3.set(title = "F-1 Score\n\nMajority Class")
clfplt3 = sns.barplot(x=[i for i in f10_x], y=classifiers, ax=axs3, color=colors_f1)
clfplt3.set(xticklabels=[], yticklabels=[])


axs5.set(title = "Minority Class")
clfplt5 = sns.barplot(x=[i for i in prec1_x], y=classifiers, ax=axs5, color=colors_prec)
clfplt5.set(xticklabels=[])

axs6.set(title = "Minority Class")
clfplt6 = sns.barplot(x=[i for i in rec1_x], y=classifiers, ax=axs6, color=colors_rec)
clfplt6.set(xticklabels=[], yticklabels=[])

axs7.set(title = "Minority Class")
clfplt7 = sns.barplot(x=[i for i in f11_x], y=classifiers, ax=axs7, color=colors_f1)
clfplt7.set(xticklabels=[], yticklabels=[])


axs9.set(title = "Accuracy")
clfplt9 = sns.barplot(x=[i for i in precac_x], y=classifiers, ax=axs9, color=colors_prec)
clfplt9.set(xticklabels=[])

axs10.set(title = "Accuracy")
clfplt10 = sns.barplot(x=[i for i in recac_x], y=classifiers, ax=axs10, color=colors_rec)
clfplt10.set(xticklabels=[], yticklabels=[])

axs11.set(title = "Accuracy")
clfplt11 = sns.barplot(x=[i for i in f1ac_x], y=classifiers, ax=axs11, color=colors_f1)
clfplt11.set(xticklabels=[], yticklabels=[])


axs13.set(title = "Macro Average")
clfplt13 = sns.barplot(x=[i for i in precma_x], y=classifiers, ax=axs13, color=colors_prec)
clfplt13.set(xticklabels=[])

axs14.set(title = "Macro Average")
clfplt14 = sns.barplot(x=[i for i in recma_x], y=classifiers, ax=axs14, color=colors_rec)
clfplt14.set(xticklabels=[], yticklabels=[])

axs15.set(title = "Macro Average")
clfplt15 = sns.barplot(x=[i for i in f1ma_x], y=classifiers, ax=axs15, color=colors_f1)
clfplt15.set(xticklabels=[], yticklabels=[])


axs17.set(title = "Weighted Average")
clfplt17 = sns.barplot(x=[i for i in precwe_x], y=classifiers, ax=axs17, color=colors_prec)
clfplt17.set()

axs18.set(title = "Weighted Average")
clfplt18 = sns.barplot(x=[i for i in recwe_x], y=classifiers, ax=axs18, color=colors_rec)
clfplt18.set(yticklabels=[])

axs19.set(title = "Weighted Average")
clfplt19 = sns.barplot(x=[i for i in f1we_x], y=classifiers, ax=axs19, color=colors_f1)
clfplt19.set(yticklabels=[])

sns.set_theme(style="whitegrid")
sns.despine(bottom=True, right=True)
mpl.rcParams['xtick.labelsize'] = 6
mpl.rcParams['ytick.labelsize'] = 7
plt.show()
"""
### **Confusion Matrix**
"""
"""
A **Confusion Matrix** provides a summary of the **predictive results** in a **classification problem**. Correct and incorrect predictions are summarized in a table with their values and broken down by each class.

It is predominantly an N x N matrix used for evaluating the performance of a classification model, where N is the number of target classes. The matrix compares the actual target values with those predicted by the machine learning model. This gives us a holistic view of how well our classification model is performing and what kinds of errors it is making

We can not rely on a single value of accuracy in classification when the classes are imbalanced. 
"""
fig = plt.figure(figsize=(10,15), dpi=150)
gs = fig.add_gridspec(5, 2)
gs.update(wspace=0.1, hspace=0.5)

ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, 0])
ax4 = fig.add_subplot(gs[1, 1])
ax5 = fig.add_subplot(gs[2, 0])
ax6 = fig.add_subplot(gs[2, 1])

colors = ["black", "lightgrey"]
colormap = mpl.colors.LinearSegmentedColormap.from_list("", colors)

ax1.set(title = "Logistic Regression")
sns.heatmap(cm_logreg, linewidths=2.5, yticklabels=['Actual Non-Job Seeker','Actual Job Seeker'], xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d',ax=ax1, annot_kws={"fontsize":10})
ax2.set(title = "K-Nearest Neighbours")
sns.heatmap(cm_knn, linewidths=2.5, yticklabels=False, xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True,fmt='d',ax=ax2,annot_kws={"fontsize":10})
ax3.set(title = "Support Vector Classifier")
sns.heatmap(cm_svc, linewidths=2.5, yticklabels=['Actual Non-Job Seeker','Actual Job Seeker'],xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d', ax=ax3, annot_kws={"fontsize":10})
ax4.set(title = "Gaussian Naive Bayes")
sns.heatmap(cm_nb, linewidths=2.5, yticklabels=False, xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d',ax=ax4,annot_kws={"fontsize":10})
ax5.set(title = "Decision Tree Classifier")
sns.heatmap(cm_dt, linewidths=2.5, yticklabels=['Actual Non-Job Seeker','Actual Job Seeker'],xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d', ax=ax5, annot_kws={"fontsize":10})
ax6.set(title = "Random Forest Classifier")
sns.heatmap(cm_rf, linewidths=2.5, yticklabels=False, xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d',ax=ax6,annot_kws={"fontsize":10})

plt.show()
"""
#### What do we understand from this?

**True Positive (TP)**

* The predicted value matches the actual value
* The actual value was positive and the model predicted a positive value


**True Negative (TN)** 

* The predicted value matches the actual value
* The actual value was negative and the model predicted a negative value


**False Positive (FP) – Type 1 error**

* The predicted value was falsely predicted
* The actual value was negative but the model predicted a positive value
* Also known as the Type 1 error


**False Negative (FN) – Type 2 error**

* The predicted value was falsely predicted
* The actual value was positive but the model predicted a negative value
* Also known as the Type 2 error
"""
"""
### Classification Model Scores
"""
ml_class = ["Logistic Regression", "K-Nearest Neighbours", "Support Vector Classifier", "Gaussian Naive-Bayes", "Decision Tree Classifier", "Random Forest Classifier"]
ml_score = [lr_pacs, knn_pacs, svc_pacs, nb_pacs, dt_pacs, rf_pacs]

fig = go.Figure(data=[
    go.Bar(name='Pipeline Classification Scores', x = ml_class, y = ml_score, hovertemplate = "Classification Levels: <br>Classification Technique: %{x} </br>Percentage Score: %{y} </br><extra></extra>")
])

fig.update(layout_title_text='Classification Models and their Scores')
fig.update_layout(hoverlabel=dict(bgcolor="white", font_size=16, font_family="Rockwell"), plot_bgcolor='white')
fig.update_layout(xaxis=dict(showline=True, showgrid=False, showticklabels=True, linecolor='rgb(2, 2, 2)',
                             linewidth=2, ticks='outside', tickfont=dict(family='Arial', size=12, color='rgb(82, 82, 82)')),
                  yaxis=dict(showgrid=True, zeroline=True, showline=True, showticklabels=True, linecolor='rgb(2, 2, 2)',
                             linewidth=2, ticks='outside', tickfont=dict(family='Arial', size=12, color='rgb(82, 82, 82)')))
fig.update_layout(barmode='group')
fig.show()
"""
### What do we learn?
"""
"""
### Is there another way?

In any case, the simplest way to **avoid the disparity** in target data value is to **fill more** of the **minority values**. In order to do that, we implement a technique called **oversampling**.

**Oversampling** is a way of duplicating the minority class values without disrupting the remainder of the dataset. **Simply put**, we will be adding more values of the target **"1"** value (**those who are job seekers**).

We could also **undersample** our dataset, which is the process of **removing members of the majority class values** to **reduce the disparity ratio** between the two classes, but in doing so we will be **losing key information and factors** that would otherwise be essential to the prediction process. 
"""
"""
### What oversampling technique should we use?

**Oversampling** in general just **replicates data to reduce the ratio**. However, if we want a **higher accuracy** from our prediction model, we have to **enter relevant data**. 
"""
"""
### **SMOTE (Synthetic Minority Oversampling Technique)**

**SMOTE** or **Synthetic Minority Oversampling Technique** is an oversampling technique but SMOTE works differently from your typical oversampling.

**SMOTE** works by **utilizing a k-nearest neighbor algorithm** to create **synthetic data**. 

**SMOTE** first start by **choosing random data** from the **minority class**, then **k-nearest neighbors from the data are set**. Synthetic data would then be made **between the random data** and **the randomly selected k-nearest neighbor**.
"""
sm = SMOTE(random_state=101)
X_train_sm, y_train_sm = sm.fit_resample(X_train_sm_cp, y_train_sm_cp.ravel())
(unique_sm, counts_sm) = np.unique(y_train_sm, return_counts=True)
(unique, counts) = np.unique(y_train, return_counts=True)

fig = make_subplots(rows=1, cols=2, specs=[[{"type": "domain"}, {"type": "domain"}]])

fig.add_trace(go.Pie(labels=["Job non-seeker", "Job seeker"], values=counts, title='Unique Values', scalegroup='one', 
                     hovertemplate = "Unique Value Distribution: <br>Value: %{label} </br>Percentage Score: %{percent} </br><extra></extra>",
                     marker = dict(line = dict(color='#000000', width=0.5)), pull=[0.05, 0.05]), 1, 1)
fig.add_trace(go.Pie(labels=["Job non-seeker", "Job seeker"], values=counts_sm, title='Unique Values - After SMOTE', scalegroup='one', 
                     hovertemplate = "Unique Value Distribution: <br>Value: %{label} </br>Percentage Score: %{percent} </br><extra></extra>",
                     marker = dict(line = dict(color='#000000', width=0.5)), pull=[0.05, 0.05]), 1, 2)

fig.update_traces(hoverinfo='label+percent')
fig.update(layout_title_text='Unique Value Distribution - Before & After SMOTE', layout_showlegend=False)
fig.update_layout(hoverlabel=dict(bgcolor="white", font_size=16, font_family="Rockwell"), hovermode='x')
fig = go.Figure(fig)
fig.show()
lr = LogisticRegression(max_iter=10000)
lr.fit(X_train_sm, y_train_sm)
y_pred_sm_lr = lr.predict(X_val_sm)
pipe_lr_sm = make_pipeline(StandardScaler(), LogisticRegression())
pipe_lr_sm.fit(X_train_sm, y_train_sm)
lr_pacs_sm = pipe_lr_sm.score(X_val_sm, y_val_sm)*100
print('Logistic Regression -')
print('   Pipeline Accuracy Score = ', lr_pacs_sm)
logreg_clf_report_sm = classification_report(y_val_sm, y_pred_sm_lr, output_dict=True)
cm_logreg_sm = confusion_matrix(y_val_sm, y_pred_sm_lr)

knn = KNeighborsClassifier(algorithm='ball_tree')
knn.fit(X_train_sm, y_train_sm)
y_pred_sm_knn = knn.predict(X_val_sm)
pipe_knn_sm = make_pipeline(StandardScaler(), KNeighborsClassifier(algorithm='ball_tree'))
pipe_knn_sm.fit(X_train_sm, y_train_sm)
knn_pacs_sm = pipe_knn_sm.score(X_val_sm, y_val_sm)*100
print('\nK-Nearest Neighbours -')
print('   Pipeline Accuracy Score = ', knn_pacs_sm)
knn_clf_report_sm = classification_report(y_val_sm, y_pred_sm_knn, output_dict=True)
cm_knn_sm = confusion_matrix(y_val_sm, y_pred_sm_knn)

svc = LinearSVC(max_iter=11000)
svc.fit(X_train_sm, y_train_sm)
y_pred_sm_svc = svc.predict(X_val_sm)
pipe_svc_sm = make_pipeline(StandardScaler(), LinearSVC(penalty='l2', loss='squared_hinge', class_weight='balanced', verbose=0, random_state=101))
pipe_svc_sm.fit(X_train_sm, y_train_sm)
svc_pacs_sm = pipe_svc_sm.score(X_val_sm, y_val_sm)*100
print('\nSupport Vector Classifier -')
print('   Pipeline Accuracy Score = ', svc_pacs_sm)
svc_clf_report_sm = classification_report(y_val_sm, y_pred_sm_svc, output_dict=True)
cm_svc_sm = confusion_matrix(y_val_sm, y_pred_sm_svc)

nb = GaussianNB()
nb.fit(X_train_sm, y_train_sm)
y_pred_sm_nb = nb.predict(X_val_sm)
pipe_gnb_sm = make_pipeline(StandardScaler(), GaussianNB())
pipe_gnb_sm.fit(X_train_sm, y_train_sm)
nb_pacs_sm = pipe_gnb_sm.score(X_val_sm, y_val_sm)*100
print('\nGaussian Naive Bayes -')
print('   Pipeline Accuracy Score = ', nb_pacs_sm)
nb_clf_report_sm = classification_report(y_val_sm, y_pred_sm_nb, output_dict=True)
cm_nb_sm = confusion_matrix(y_val_sm, y_pred_sm_nb)

dt = DecisionTreeClassifier(criterion = 'entropy')
dt.fit(X_train_sm, y_train_sm)
y_pred_sm_dt = dt.predict(X_val_sm)
pipe_dtc_sm = make_pipeline(StandardScaler(), DecisionTreeClassifier())
pipe_dtc_sm.fit(X_train_sm, y_train_sm)
dt_pacs_sm = pipe_dtc_sm.score(X_val_sm, y_val_sm)*100
print('\nDecision Tree Classifier -')
print('   Pipeline Accuracy Score = ', dt_pacs_sm)
dt_clf_report_sm = classification_report(y_val_sm, y_pred_sm_dt, output_dict=True)
cm_dt_sm = confusion_matrix(y_val_sm, y_pred_sm_dt)

rf = RandomForestClassifier()
rf.fit(X_train_sm, y_train_sm)
y_pred_sm_rf = rf.predict(X_val_sm)
pipe_rft_sm = make_pipeline(StandardScaler(), RandomForestClassifier())
pipe_rft_sm.fit(X_train_sm, y_train_sm)
rf_pacs_sm = pipe_rft_sm.score(X_val_sm, y_val_sm)*100
print('\nRandom Forest Classifier -')
print('   Pipeline Accuracy Score = ', rf_pacs_sm)
rf_clf_report_sm = classification_report(y_val_sm, y_pred_sm_rf, output_dict=True)
cm_rf_sm = confusion_matrix(y_val_sm, y_pred_sm_rf)
"""
## **Hyperparameter Tuning**
"""
lr = LogisticRegression(max_iter=10000)
lr.fit(X_train_sm, y_train_sm)
y_pred_sm_lr = lr.predict(X_val_sm)
pipe_lr_sm = make_pipeline(StandardScaler(), LogisticRegression(class_weight = 'balanced', random_state=101, solver='lbfgs', max_iter=10000))
pipe_lr_sm.fit(X_train_sm, y_train_sm)
lr_pacs_sm = pipe_lr_sm.score(X_val_sm, y_val_sm)*100
print('Logistic Regression -')
print('   Pipeline Accuracy Score = ', lr_pacs_sm)
logreg_clf_report_sm = classification_report(y_val_sm, y_pred_sm_lr, output_dict=True)
cm_logreg_sm = confusion_matrix(y_val_sm, y_pred_sm_lr)

knn = KNeighborsClassifier(algorithm='ball_tree')
knn.fit(X_train_sm, y_train_sm)
y_pred_sm_knn = knn.predict(X_val_sm)
pipe_knn_sm = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=12, weights='uniform', algorithm='ball_tree', leaf_size=85, p=1, metric='manhattan'))
pipe_knn_sm.fit(X_train_sm, y_train_sm)
knn_pacs_sm = pipe_knn_sm.score(X_val_sm, y_val_sm)*100
print('\nK-Nearest Neighbours -')
print('   Pipeline Accuracy Score = ', knn_pacs_sm)
knn_clf_report_sm = classification_report(y_val_sm, y_pred_sm_knn, output_dict=True)
cm_knn_sm = confusion_matrix(y_val_sm, y_pred_sm_knn)

svc = LinearSVC(max_iter=11000)
svc.fit(X_train_sm, y_train_sm)
y_pred_sm_svc = svc.predict(X_val_sm)
pipe_svc_sm = make_pipeline(StandardScaler(), LinearSVC(penalty='l2', loss='squared_hinge', class_weight='balanced', verbose=0, random_state=101))
pipe_svc_sm.fit(X_train_sm, y_train_sm)
svc_pacs_sm = pipe_svc_sm.score(X_val_sm, y_val_sm)*100
print('\nSupport Vector Classifier -')
print('   Pipeline Accuracy Score = ', svc_pacs_sm)
svc_clf_report_sm = classification_report(y_val_sm, y_pred_sm_svc, output_dict=True)
cm_svc_sm = confusion_matrix(y_val_sm, y_pred_sm_svc)

nb = GaussianNB()
nb.fit(X_train_sm, y_train_sm)
y_pred_sm_nb = nb.predict(X_val_sm)
pipe_gnb_sm = make_pipeline(StandardScaler(), GaussianNB())
pipe_gnb_sm.fit(X_train_sm, y_train_sm)
nb_pacs_sm = pipe_gnb_sm.score(X_val_sm, y_val_sm)*100
print('\nGaussian Naive Bayes -')
print('   Pipeline Accuracy Score = ', nb_pacs_sm)
nb_clf_report_sm = classification_report(y_val_sm, y_pred_sm_nb, output_dict=True)
cm_nb_sm = confusion_matrix(y_val_sm, y_pred_sm_nb)

dt = DecisionTreeClassifier(criterion = 'gini')
dt.fit(X_train_sm, y_train_sm)
y_pred_sm_dt = dt.predict(X_val_sm)
pipe_dtc_sm = make_pipeline(StandardScaler(), DecisionTreeClassifier(criterion='entropy', splitter='best', max_depth=6, min_samples_split=2, min_samples_leaf=2, random_state=101, class_weight='balanced'))
pipe_dtc_sm.fit(X_train_sm, y_train_sm)
dt_pacs_sm = pipe_dtc_sm.score(X_val_sm, y_val_sm)*100
print('\nDecision Tree Classifier -')
print('   Pipeline Accuracy Score = ', dt_pacs_sm)
dt_clf_report_sm = classification_report(y_val_sm, y_pred_sm_dt, output_dict=True)
cm_dt_sm = confusion_matrix(y_val_sm, y_pred_sm_dt)

rf = RandomForestClassifier()
rf.fit(X_train_sm, y_train_sm)
y_pred_sm_rf = rf.predict(X_val_sm)
pipe_rft_sm = make_pipeline(StandardScaler(), RandomForestClassifier(criterion='entropy', max_depth=10, min_samples_split=2, min_samples_leaf=2, random_state=101, class_weight='balanced'))
pipe_rft_sm.fit(X_train_sm, y_train_sm)
rf_pacs_sm = pipe_rft_sm.score(X_val_sm, y_val_sm)*100
print('\nRandom Forest Classifier -')
print('   Pipeline Accuracy Score = ', rf_pacs_sm)
rf_clf_report_sm = classification_report(y_val_sm, y_pred_sm_rf, output_dict=True)
cm_rf_sm = confusion_matrix(y_val_sm, y_pred_sm_rf)
clf_lgrg_df = pd.DataFrame(logreg_clf_report_sm).transpose().rename_axis('parameters').reset_index()
clf_knn_df = pd.DataFrame(knn_clf_report_sm).transpose().rename_axis('parameters').reset_index()
clf_svc_df = pd.DataFrame(svc_clf_report_sm).transpose().rename_axis('parameters').reset_index()
clf_nb_df = pd.DataFrame(nb_clf_report_sm).transpose().rename_axis('parameters').reset_index()
clf_dt_df = pd.DataFrame(dt_clf_report_sm).transpose().rename_axis('parameters').reset_index()
clf_rf_df = pd.DataFrame(rf_clf_report_sm).transpose().rename_axis('parameters').reset_index()

prec0_x = [clf_lgrg_df.iloc[0]['precision'], clf_knn_df.iloc[0]['precision'], clf_svc_df.iloc[0]['precision'], clf_nb_df.iloc[0]['precision'], clf_dt_df.iloc[0]['precision'], clf_rf_df.iloc[0]['precision']]
prec1_x = [clf_lgrg_df.iloc[1]['precision'], clf_knn_df.iloc[1]['precision'], clf_svc_df.iloc[1]['precision'], clf_nb_df.iloc[1]['precision'], clf_dt_df.iloc[1]['precision'], clf_rf_df.iloc[1]['precision']]
precac_x = [clf_lgrg_df.iloc[2]['precision'], clf_knn_df.iloc[2]['precision'], clf_svc_df.iloc[2]['precision'], clf_nb_df.iloc[2]['precision'], clf_dt_df.iloc[2]['precision'], clf_rf_df.iloc[2]['precision']]
precma_x = [clf_lgrg_df.iloc[3]['precision'], clf_knn_df.iloc[3]['precision'], clf_svc_df.iloc[3]['precision'], clf_nb_df.iloc[3]['precision'], clf_dt_df.iloc[3]['precision'], clf_rf_df.iloc[3]['precision']]
precwe_x = [clf_lgrg_df.iloc[4]['precision'], clf_knn_df.iloc[4]['precision'], clf_svc_df.iloc[4]['precision'], clf_nb_df.iloc[4]['precision'], clf_dt_df.iloc[4]['precision'], clf_rf_df.iloc[4]['precision']]

rec0_x = [clf_lgrg_df.iloc[0]['recall'], clf_knn_df.iloc[0]['recall'], clf_svc_df.iloc[0]['recall'], clf_nb_df.iloc[0]['recall'], clf_dt_df.iloc[0]['recall'], clf_rf_df.iloc[0]['recall']]
rec1_x = [clf_lgrg_df.iloc[1]['recall'], clf_knn_df.iloc[1]['recall'], clf_svc_df.iloc[1]['recall'], clf_nb_df.iloc[1]['recall'], clf_dt_df.iloc[1]['recall'], clf_rf_df.iloc[1]['recall']]
recac_x = [clf_lgrg_df.iloc[2]['recall'], clf_knn_df.iloc[2]['recall'], clf_svc_df.iloc[2]['recall'], clf_nb_df.iloc[2]['recall'], clf_dt_df.iloc[2]['recall'], clf_rf_df.iloc[2]['recall']]
recma_x = [clf_lgrg_df.iloc[3]['recall'], clf_knn_df.iloc[3]['recall'], clf_svc_df.iloc[3]['recall'], clf_nb_df.iloc[3]['recall'], clf_dt_df.iloc[3]['recall'], clf_rf_df.iloc[3]['recall']]
recwe_x = [clf_lgrg_df.iloc[4]['recall'], clf_knn_df.iloc[4]['recall'], clf_svc_df.iloc[4]['recall'], clf_nb_df.iloc[4]['recall'], clf_dt_df.iloc[4]['recall'], clf_rf_df.iloc[4]['recall']]

f10_x = [clf_lgrg_df.iloc[0]['f1-score'], clf_knn_df.iloc[0]['f1-score'], clf_svc_df.iloc[0]['f1-score'], clf_nb_df.iloc[0]['f1-score'], clf_dt_df.iloc[0]['f1-score'], clf_rf_df.iloc[0]['f1-score']]
f11_x = [clf_lgrg_df.iloc[1]['f1-score'], clf_knn_df.iloc[1]['f1-score'], clf_svc_df.iloc[1]['f1-score'], clf_nb_df.iloc[1]['f1-score'], clf_dt_df.iloc[1]['f1-score'], clf_rf_df.iloc[1]['f1-score']]
f1ac_x = [clf_lgrg_df.iloc[2]['f1-score'], clf_knn_df.iloc[2]['f1-score'], clf_svc_df.iloc[2]['f1-score'], clf_nb_df.iloc[2]['f1-score'], clf_dt_df.iloc[2]['f1-score'], clf_rf_df.iloc[2]['f1-score']]
f1ma_x = [clf_lgrg_df.iloc[3]['f1-score'], clf_knn_df.iloc[3]['f1-score'], clf_svc_df.iloc[3]['f1-score'], clf_nb_df.iloc[3]['f1-score'], clf_dt_df.iloc[3]['f1-score'], clf_rf_df.iloc[3]['f1-score']]
f1we_x = [clf_lgrg_df.iloc[4]['f1-score'], clf_knn_df.iloc[4]['f1-score'], clf_svc_df.iloc[4]['f1-score'], clf_nb_df.iloc[4]['f1-score'], clf_dt_df.iloc[4]['f1-score'], clf_rf_df.iloc[4]['f1-score']]

sup0_x = [clf_lgrg_df.iloc[0]['support'], clf_knn_df.iloc[0]['support'], clf_svc_df.iloc[0]['support'], clf_nb_df.iloc[0]['support'], clf_dt_df.iloc[0]['support'], clf_rf_df.iloc[0]['support']]
sup1_x = [clf_lgrg_df.iloc[1]['support'], clf_knn_df.iloc[1]['support'], clf_svc_df.iloc[1]['support'], clf_nb_df.iloc[1]['support'], clf_dt_df.iloc[1]['support'], clf_rf_df.iloc[1]['support']]
supac_x = [clf_lgrg_df.iloc[2]['support'], clf_knn_df.iloc[2]['support'], clf_svc_df.iloc[2]['support'], clf_nb_df.iloc[2]['support'], clf_dt_df.iloc[2]['support'], clf_rf_df.iloc[2]['support']]
supma_x = [clf_lgrg_df.iloc[3]['support'], clf_knn_df.iloc[3]['support'], clf_svc_df.iloc[3]['support'], clf_nb_df.iloc[3]['support'], clf_dt_df.iloc[3]['support'], clf_rf_df.iloc[3]['support']]
supwe_x = [clf_lgrg_df.iloc[4]['support'], clf_knn_df.iloc[4]['support'], clf_svc_df.iloc[4]['support'], clf_nb_df.iloc[4]['support'], clf_dt_df.iloc[4]['support'], clf_rf_df.iloc[4]['support']]

classifiers = ["Logistic Regression", "K-Nearest Neighbours", "Support Vector Classifier", "Gaussian Naive-Bayes", "Decision Tree Classifier", "Random Forest Classifier"]

fig = plt.figure(figsize=(10,30), dpi=150)
gs = fig.add_gridspec(19, 3)
gs.update(wspace=0.2, hspace=0.5)

axs1 = fig.add_subplot(gs[0, 0])
axs2 = fig.add_subplot(gs[0, 1])
axs3 = fig.add_subplot(gs[0, 2])
axs5 = fig.add_subplot(gs[1, 0])
axs6 = fig.add_subplot(gs[1, 1])
axs7 = fig.add_subplot(gs[1, 2])
axs9 = fig.add_subplot(gs[2, 0])
axs10 = fig.add_subplot(gs[2, 1])
axs11 = fig.add_subplot(gs[2, 2])
axs13 = fig.add_subplot(gs[3, 0])
axs14 = fig.add_subplot(gs[3, 1])
axs15 = fig.add_subplot(gs[3, 2])
axs17 = fig.add_subplot(gs[4, 0])
axs18 = fig.add_subplot(gs[4, 1])
axs19 = fig.add_subplot(gs[4, 2])


colors_f1 = [0.1, 0.2, 0.3, 0.4]
colors_rec = [0.2, 0.4, 0.6, 0.8]
colors_prec = [0.5, 0.625, 0.75, 0.875]

axs1.set(title = "Precision\n\nMajority Class")
clfplt1 = sns.barplot(x=[i for i in prec0_x], y=classifiers, ax=axs1, color=colors_prec)
clfplt1.set(xticklabels=[])

axs2.set(title = "Recall\n\nMajority Class")
clfplt2 = sns.barplot(x=[i for i in rec0_x], y=classifiers, ax=axs2, color=colors_rec)
clfplt2.set(xticklabels=[], yticklabels=[])

axs3.set(title = "F-1 Score\n\nMajority Class")
clfplt3 = sns.barplot(x=[i for i in f10_x], y=classifiers, ax=axs3, color=colors_f1)
clfplt3.set(xticklabels=[], yticklabels=[])


axs5.set(title = "Minority Class")
clfplt5 = sns.barplot(x=[i for i in prec1_x], y=classifiers, ax=axs5, color=colors_prec)
clfplt5.set(xticklabels=[])

axs6.set(title = "Minority Class")
clfplt6 = sns.barplot(x=[i for i in rec1_x], y=classifiers, ax=axs6, color=colors_rec)
clfplt6.set(xticklabels=[], yticklabels=[])

axs7.set(title = "Minority Class")
clfplt7 = sns.barplot(x=[i for i in f11_x], y=classifiers, ax=axs7, color=colors_f1)
clfplt7.set(xticklabels=[], yticklabels=[])


axs9.set(title = "Accuracy")
clfplt9 = sns.barplot(x=[i for i in precac_x], y=classifiers, ax=axs9, color=colors_prec)
clfplt9.set(xticklabels=[])

axs10.set(title = "Accuracy")
clfplt10 = sns.barplot(x=[i for i in recac_x], y=classifiers, ax=axs10, color=colors_rec)
clfplt10.set(xticklabels=[], yticklabels=[])

axs11.set(title = "Accuracy")
clfplt11 = sns.barplot(x=[i for i in f1ac_x], y=classifiers, ax=axs11, color=colors_f1)
clfplt11.set(xticklabels=[], yticklabels=[])


axs13.set(title = "Macro Average")
clfplt13 = sns.barplot(x=[i for i in precma_x], y=classifiers, ax=axs13, color=colors_prec)
clfplt13.set(xticklabels=[])

axs14.set(title = "Macro Average")
clfplt14 = sns.barplot(x=[i for i in recma_x], y=classifiers, ax=axs14, color=colors_rec)
clfplt14.set(xticklabels=[], yticklabels=[])

axs15.set(title = "Macro Average")
clfplt15 = sns.barplot(x=[i for i in f1ma_x], y=classifiers, ax=axs15, color=colors_f1)
clfplt15.set(xticklabels=[], yticklabels=[])


axs17.set(title = "Weighted Average")
clfplt17 = sns.barplot(x=[i for i in precwe_x], y=classifiers, ax=axs17, color=colors_prec)
clfplt17.set()

axs18.set(title = "Weighted Average")
clfplt18 = sns.barplot(x=[i for i in recwe_x], y=classifiers, ax=axs18, color=colors_rec)
clfplt18.set(yticklabels=[])

axs19.set(title = "Weighted Average")
clfplt19 = sns.barplot(x=[i for i in f1we_x], y=classifiers, ax=axs19, color=colors_f1)
clfplt19.set(yticklabels=[])

sns.set_theme(style="whitegrid")
sns.despine(bottom=True, right=True)
mpl.rcParams['xtick.labelsize'] = 6
mpl.rcParams['ytick.labelsize'] = 7
plt.show()
fig = plt.figure(figsize=(10,15), dpi=150)
gs = fig.add_gridspec(5, 2)
gs.update(wspace=0.1, hspace=0.5)

ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, 0])
ax4 = fig.add_subplot(gs[1, 1])
ax5 = fig.add_subplot(gs[2, 0])
ax6 = fig.add_subplot(gs[2, 1])

colors = ["black", "lightgrey"]
colormap = mpl.colors.LinearSegmentedColormap.from_list("", colors)

ax1.set(title = "Logistic Regression")
sns.heatmap(cm_logreg_sm, linewidths=2.5, yticklabels=['Actual Non-Job Seeker','Actual Job Seeker'], xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d',ax=ax1, annot_kws={"fontsize":10})
ax2.set(title = "K-Nearest Neighbours")
sns.heatmap(cm_knn_sm, linewidths=2.5, yticklabels=False, xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True,fmt='d',ax=ax2,annot_kws={"fontsize":10})
ax3.set(title = "Support Vector Classifier")
sns.heatmap(cm_svc_sm, linewidths=2.5, yticklabels=['Actual Non-Job Seeker','Actual Job Seeker'],xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d', ax=ax3, annot_kws={"fontsize":10})
ax4.set(title = "Gaussian Naive Bayes")
sns.heatmap(cm_nb_sm, linewidths=2.5, yticklabels=False, xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d',ax=ax4,annot_kws={"fontsize":10})
ax5.set(title = "Decision Tree Classifier")
sns.heatmap(cm_dt_sm, linewidths=2.5, yticklabels=['Actual Non-Job Seeker','Actual Job Seeker'],xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d', ax=ax5, annot_kws={"fontsize":10})
ax6.set(title = "Random Forest Classifier")
sns.heatmap(cm_rf_sm, linewidths=2.5, yticklabels=False, xticklabels=['Predicted Non-Job Seeker','Predicted Job Seeker'], cmap=colormap, cbar=None, annot=True, fmt='d',ax=ax6,annot_kws={"fontsize":10})

plt.show()
"""
## **Neural Networks** with Keras
"""
norm = tf.keras.layers.LayerNormalization(epsilon=0.001, center=True, scale=True)

model_bin = Sequential()
model_bin.add(Dense(200, activation='relu'))
model_bin.add(Dense(100, activation='relu'))
model_bin.add(Dense(2, activation='softmax'))
model_bin.add(norm)
    
model_bin.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model_bin.fit(X_train_nn, y_train_nn, validation_data=(X_val_nn, y_val_nn), epochs=50)
"""
## **Light GBM Classifier**
"""
lgbm = LGBMClassifier()
lgbm.fit(X_train, y_train)

y_pred = lgbm.predict(X_val)
pipe_lgbm = make_pipeline(StandardScaler(), LGBMClassifier(objective='binary', num_leaves=10, learning_rate=0.05, max_depth=1, n_estimators=50, boosting_type='goss'))
pipe_lgbm.fit(X_train, y_train)
lgbm_pacs = pipe_dtc.score(X_val, y_val)*100
print('Light Gradient Booster Machine Classifier -')
print('   Pipeline Accuracy Score = ', lgbm_pacs)

lgbm_clf_report = classification_report(y_val, y_pred, output_dict=True)
cm_lgbm = confusion_matrix(y_val, y_pred)
"""
### Information Sources

* [Standard Scaler](https://towardsdatascience.com/how-and-why-to-standardize-your-data-996926c2c832)
* [Classification Report](https://medium.com/@kohlishivam5522/understanding-a-classification-report-for-your-machine-learning-model-88815e2ce397)
* [Confusion Matrix](https://www.analyticsvidhya.com/blog/2020/04/confusion-matrix-machine-learning/)
"""