# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# +
# import libraries
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split, cross_validate
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import confusion_matrix, classification_report
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif

from plot_confusion_matrix import plot_confusion_matrix 

import warnings
warnings.filterwarnings('ignore')

# +
# Dowloaded dataset for credit card fraud from Kaggle
# -

data = pd.read_csv('data.csv')

# Show first 5 rows of the csv file
data.head()

# In the class column, 1.0 indicates that the transaction if fraudulent, and 0 means that the transaction is genuine
data.describe().round(decimals = 2)

# Column Names
print('Columns : ', list(data))
print('Number of Columns : ', len(list(data)))

# In the dataset, each row is labelled as fraud (0), or genuine (1)
n_genuine = len(data[data['Class'] == 0])
# Output is a dataframe and the length of the dataframe tells us the number of transactions which are genuine
n_fraud =len(data[data['Class'] == 1])

# +
# To plot the number of genuine and fraud transactions as a 
# pie chart to get a visual representation of the proportions 
# of fraud and genuine transactions
print("Number of Genuine Transactions : ", n_genuine)
print("Number of Fraudulent Transactions : ", n_fraud)

plt.pie([n_genuine, n_fraud], labels = ['Genuine', 'Fraud'], radius = 1)
plt.show()
# The pie chart shows that the data is highly imbalanced. 
# -

# ## Selecting the best features

# X includes all rows of all columns excluding the last one
# Y includes all rows of the last column
X, Y = data.iloc[:, :-1], data.iloc[:, -1]
X.head()

Y.head()

# Using f_classif score to select the k best features
#K is specified as parameter to SelectKBest constructor
# fit() function computes the scores and selects the k best features
k = 10
k_best = SelectKBest(f_classif, k = k)
k_best.fit(X,Y)

# ### Discard all the "bad" features

# +
# Using get_support(), get the list of booleans that represent whether the 
# Ith feature is among the k-best or not
# Then using the function drop to discard the "bad" features from the dataframe

mask = k_best.get_support()
not_mask = np.logical_not(mask)

all_features = np.array(list(X))

best_features = all_features[mask]
bad_features = all_features[not_mask]

print("Best Features : ", best_features)
print("Bad Features : ", bad_features)
# -

X = X.drop(bad_features, axis = 1)
X.head()


# ### Plot distribution plots of best features and bad features to observe the distribution of the data

# +
# If the distribution plots of genuine and fraudulent data line on top of each other,
# it means that using this column, we will not be able to disinguish between genuine 
# and fraudulent transactions.

# However if the plots have different shapes, then using this plot, we can distinguish between the two.

def plot_fraud_genuine(features, data):
    plt_index = 0
    plt.figure(figsize = (10,10))
    plt.subplots_adjust(top = 0.99, bottom = 0.01, hspace = 1.5, wspace = 0.4)
    for feature in features: 
        plt_index += 1
        feature_data = pd.concat([data[feature], data['Class']], axis = 1)
        fraud = feature_data[data['Class'] == 1]
        genuine = feature_data[data['Class'] == 0]
        if len(genuine > 10000): 
            genuine = genuine[::100]
        plt.subplot(5, 5, plt_index)
        sns.distplot(fraud[feature], label = 'fraud')
        sns.distplot(genuine[feature], label = 'genuine')
        plt.title(feature)
        #plt.legend()


# -

plot_fraud_genuine(best_features, data)
# The plots do not have majoirty operlapping portions

plot_fraud_genuine(bad_features, data)
# The plots have majority portions overlapping

# Split the datset into training and testing data
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2)

# ## Cross Validation 
# The data is divided into k equal parts. Then each chunk is considered as testing data and the rest of the chunk is considered as the testing data.
#
#
# Since the data is highly imbalanced, cross validation is used. 
# In this, the model is trained on the training dataset using cross validation.
#
# ## Why we use cross validation?
# We have a very small number of fraud transactions. 
#
# In order to make the model more general.

nb = GaussianNB()
cv_results = cross_validate(nb, X_train, Y_train, cv = 10, 
                           scoring = 'recall', 
                           return_train_score = True, 
                           return_estimator = True)

print('Training scores from each fold : ', cv_results['train_score'])
max_score_index = np.argmax(cv_results['train_score'])
best_estimator = cv_results['estimator'][max_score_index]


# ## Evaluate and visualize using Confusion Matrix.

def display_results(estimator, X, Y):
    predicted = estimator.predict(X)
    cm = confusion_matrix(Y, predicted)
    report = classification_report(Y, predicted)
    print(report)
    plot_confusion_matrix(cm, classes = ['Genuine', 'Fraud'], title = "Fraud Detection")


display_results(best_estimator, X_test, Y_test)

display_results(best_estimator, X_train, Y_train)

StarcoderdataJupyterScriptsDedupFiltered