"""
## Credit Card fraud detection using Self Organising Map 
"""
"""
I will be using MiniSom implimentation
"""
"""
# Importing basic libraries
"""
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
"""
# Importing the dataset
"""
dataset = pd.read_csv('../input/credit-card-applications/Credit_Card_Applications.csv')
dataset.head()
X = dataset.iloc[: , :-1].values
y = dataset.iloc[: , -1].values  #We won't be using this value in our training as SOM is an Unsupervised Learning algorithm
X.shape
y.shape
"""
# Feature Scaling
"""
"""
Normalizing
"""
from sklearn.preprocessing import MinMaxScaler

sc = MinMaxScaler(feature_range=(0,1)) 
X = sc.fit_transform(X)
"""
# Training the SOM
"""
! pip install minisom
from minisom import MiniSom
"""
Parameters :
1. x and y = dimensions of the grid
2. input_len = No of features in input dataset
3. sigma = radius of the neighbourhoods
4. learning_rate = By how much the weiights are updated in each iteration. Higher the value faster the algorithm runs.
"""
som = MiniSom(x=20 ,y=20 ,sigma=1.0 ,learning_rate=0.5 ,input_len=15)
"""
Initializing the weights
"""
som.random_weights_init(X)
"""
**Training the som.** 
"""
som.train_random(data=X ,num_iteration=100)
"""
# Visualizing the results
"""
"""
We will be plotting such that lighter shades have a higher chance of fraud and darker shades have lower chance.
"""
from pylab import bone, pcolor, colorbar, plot, show

plt.figure(figsize=(17, 10), dpi= 80, facecolor='w', edgecolor='k') # To make the fig bigger 

pcolor(som.distance_map().T) # This line finds out the mean inter neuron distance and makes a map based on these distances.
                             # It makes clusters based on the colours based on the distances. The darker the colour the closer the neurons is to it's neighbourhood.
                             # The lighter neurons are the outliers and if customers are present in it that means they are fradulent.
colorbar() # This is the legend of the map
"""
We can stop here and find out the customers in the white squares by inverse transforming. And return the data to the bank.

However, we have the data of fraud customers decided by bank(y) and we want to compare it with our result. 

For that we will add a feature of 'markers' in our map. These markers will tell us if customer got their credit card approved or not :

1. Red circles - Card approved
2. Green squares - Card Denied
"""
plt.figure(figsize=(17, 10), dpi= 80, facecolor='w', edgecolor='k') # To make the fig bigger 
pcolor(som.distance_map().T) 

colorbar()
markers = ['o', 's']
colors = ['r', 'g']
for i, x in enumerate(X):  # Looping through X such as x loops through the rows and i loops through columns of that customer (each attribute of a customer).
    w = som.winner(x)      # Finding out the winner node of each customer
    plot(w[0] + 0.5,       # w[0]- x coordinate , w[1] - y coordinate. We are placing the markers at the center of each node/neuron. That's the whole code inside the plot().
         w[1] + 0.5,
         markers[y[i]],
         markeredgecolor = colors[y[i]],
         markerfacecolor = 'None',
         markersize = 10,
         markeredgewidth = 2)
show()
"""
The outlier categories are the frauds. 

As you can see there are 2 outliers categories (white)  at co-ordinates (16,3) and (18,3). Which means under these categories customers are most likely to be frauds.

As there can be many customers in a category. In both of these categories there are both green square and red circle, which means that some of them got their credit card approved too.
"""
"""
# Finding the frauds
"""
"""
To get all the mappings for all the winning nodes in this SOM. This will be in a form of a dictionary where the key is the co-ordinate of the mapping and the values are all the attributes of customers (corresponding rows of X)
"""
mappings = som.win_map(X)
"""
These are the co-ordinates of all the mappings done. 

Of these we have identified customers of (16,3) and (18,3) as frauds.
"""
mappings.keys()
"""
Concatenating all the fraud customers in a nparray.
"""
frauds = np.concatenate( (mappings[(16,3)],mappings[(18,3)]) , axis = 0 ) 
"""
Scaling back the attributes to find out the customer ID's that are frauds
"""
frauds = sc.inverse_transform(frauds)
"""
Extracting out all the Customer ID's that are frauds.
"""
CustID = frauds[:,0]
"""
# Here are the fraudulent customers 
"""
CustID
"""
# Conclusion
"""
"""
So here we determined the fradulent customers based on the distance between the neurons.
The outlier neurons were the ones which had the maximum mean distance between it's neighbours. And can be seen in lighter shades in the map.
"""