"""
## Introduciton:
### This is the second part of the project. In this part, I am going to construct the prediction model with Neural Network and try to optimize it.
"""
import pandas as pd 
import numpy as np
dt = pd.read_csv("/kaggle/input/simulated-bank-customer-data/CUST_ASSET_DATA.csv")
ind1 = dt[dt["AGE"].isna()].index
ind2 = dt[dt["BRANCH_DIST"].isna()].index
ind = pd.Series((list(ind1)+list(ind2))).unique()
dt = dt.drop(index = ind)
# preprocessing of data
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from keras.models import Sequential
from keras.layers import Dense, Activation
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import r2_score
from keras.callbacks import EarlyStopping
from keras.optimizers import Adam
from keras.models import Sequential
from keras.layers import Dropout
"""
### Since this is a time series data, we hope to use the current data to predict the total assets of the next period. I use current TOTAL_ASSET as one of the features and the next TOTAL_ASSET as y. Using first 11 month as traing data and the 12th month as testing data. Like following picture.
![123.jpg](attachment:123.jpg)
"""
columns = ["AGE","GENDER","CITY","BRANCH_DIST","ACTIVE_WEB_CUST","SECURITY_ACC",
           "INTERACT_AMT_A","INTERACT_AMT_B","TOTAL_ASSET_X","TOTAL_ASSET_y"]

dt2 = pd.DataFrame(np.array(dt[["AGE","GENDER","CITY","BRANCH_DIST",
          "ACTIVE_WEB_CUST_1","SECURITY_ACC_1","INTERACT_AMT_A_1",
          "INTERACT_AMT_B_1","TOTAL_ASSET_1","TOTAL_ASSET_2"]]),columns = columns)

for i in range(2,12):
    dt1 = pd.DataFrame(np.array(dt[["AGE","GENDER","CITY","BRANCH_DIST",
                           "ACTIVE_WEB_CUST_"+str(i),"SECURITY_ACC_"+str(i),
                           "INTERACT_AMT_A_"+str(i),"INTERACT_AMT_B_"+str(i),
                           "TOTAL_ASSET_"+str(i),"TOTAL_ASSET_"+str(i+1)]])
                       ,columns = columns)
    Train_dt = pd.concat([dt2,dt1])

Test_dt = pd.DataFrame(np.array(dt[["AGE","GENDER","CITY","BRANCH_DIST",
          "ACTIVE_WEB_CUST_12","SECURITY_ACC_12","INTERACT_AMT_A_12",
          "INTERACT_AMT_B_12","TOTAL_ASSET_12","TOTAL_ASSET_13"]]),columns = columns)
Train_dt
Test_dt
"""
### Now we got 198026 traing data and 99013 testing data. Before we construct our model, we have to transform our nominal features into nominal codes.
"""
nominal = ["AGE","GENDER","CITY","BRANCH_DIST","ACTIVE_WEB_CUST","SECURITY_ACC"]

Train_dt = Train_dt.copy()
for label in nominal:
    Train_dt[label] = LabelEncoder().fit_transform(Train_dt[label])
    
Test_dt = Test_dt.copy()
for label in nominal:
    Test_dt[label] = LabelEncoder().fit_transform(Test_dt[label])
X_train, y_train,X_test,y_test = Train_dt.iloc[:,0:9],Train_dt.iloc[:,9:10],Test_dt.iloc[:,0:9],Test_dt.iloc[:,9:10]

X_train=np.array(X_train).reshape(-1,9)
X_test=np.array(X_test).reshape(-1,9)
y_train=np.array(y_train).reshape(-1,1)
y_test=np.array(y_test).reshape(-1,1)

import tensorflow as tf
X_train = tf.constant(X_train, tf.float32)
X_test = tf.constant(X_test, tf.float32)
y_train = tf.constant(y_train, tf.float32)
y_test = tf.constant(y_test, tf.float32)
"""
### Now we are going to construct our model with NN.
"""
#Plotting acc and loss plot.
import keras
class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = {'batch':[], 'epoch':[]}
        self.accuracy = {'batch':[], 'epoch':[]}
        self.val_loss = {'batch':[], 'epoch':[]}
        self.val_acc = {'batch':[], 'epoch':[]}

    def on_batch_end(self, batch, logs={}):
        self.losses['batch'].append(logs.get('loss'))
        self.accuracy['batch'].append(logs.get('accuracy'))
        self.val_loss['batch'].append(logs.get('val_loss'))
        self.val_acc['batch'].append(logs.get('val_accuracy'))

    def on_epoch_end(self, batch, logs={}):
        self.losses['epoch'].append(logs.get('loss'))
        self.accuracy['epoch'].append(logs.get('accuracy'))
        self.val_loss['epoch'].append(logs.get('val_loss'))
        self.val_acc['epoch'].append(logs.get('val_accuracy'))

    def loss_plot(self, loss_type):
        iters = range(len(self.losses[loss_type]))
        # loss
        plt.plot(iters, self.losses[loss_type], 'g', label='train loss')
        if loss_type == 'epoch':
            # val_loss
            plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss')
        plt.grid(True)
        plt.xlabel(loss_type)
        plt.ylabel("loss")
        plt.legend(loc="upper right")
        plt.show()
ann=Sequential()
ann.add(Dense(64, input_dim=9, activation='relu'))
ann.add(Dense(32, input_dim=64, activation='relu'))
ann.add(Dense(units=1, kernel_initializer='normal', activation='linear'))
ann.compile(loss='mean_squared_error', optimizer='adam',metrics=['accuracy'])
history = LossHistory() # call the class we built 
ann.fit(X_train, y_train, epochs=300, batch_size=8000, 
        validation_data=(X_test, y_test),callbacks=[history],
        verbose=1, shuffle=False)
history.loss_plot('epoch')
"""
### It seems that loss function converge very quickly 
"""
y_pred_test = ann.predict(X_test)
y_train_pred =ann.predict(X_train)
print("The R2 score on the ANNTrain set is:\t{:0.3f}".format(r2_score(y_train, y_train_pred)))
print("The R2 score on the ANNTest set is:\t{:0.3f}".format(r2_score(y_test, y_pred_test)))
y_pred = y_pred_test[0:50] 
y_origin = y_test[0:50]

X = range(0,50)
plt.plot(X,y_pred,label = "prediction",color = "red")
plt.plot(X,y_origin,label = "origin",color = "blue")
plt.legend()
plt.xlabel("customer")
plt.ylabel("amount")
Answer = np.array(dt["TOTAL_ASSET_13"]).reshape(-1,1)
print("MSE of Prediction: "+str((((np.array(y_pred_test).reshape(-1,1)-Answer)**2)/len(dt)).sum()))
"""
### Now we reset the epoch to 50 time and construct the model again.
"""
ann=Sequential()
ann.add(Dense(64, input_dim=9, activation='relu'))
ann.add(Dense(32, input_dim=64, activation='relu'))
ann.add(Dense(units=1, kernel_initializer='normal', activation='linear'))
ann.compile(loss='mean_squared_error', optimizer='adam',metrics=['accuracy'])
history = LossHistory() # call the class we built 
ann.fit(X_train, y_train, epochs=50, batch_size=8000, 
        validation_data=(X_test, y_test),callbacks=[history],
        verbose=1, shuffle=False)
y_pred_test = ann.predict(X_test)
y_train_pred =ann.predict(X_train)
print("The R2 score on the ANNTrain set is:\t{:0.3f}".format(r2_score(y_train, y_train_pred)))
print("The R2 score on the ANNTest set is:\t{:0.3f}".format(r2_score(y_test, y_pred_test)))
Answer = np.array(dt["TOTAL_ASSET_13"]).reshape(-1,1)
print("MSE of Prediction: "+str((((np.array(y_pred_test).reshape(-1,1)-Answer)**2)/len(dt)).sum()))
"""
### After we set the epoch to 50 times, we get the better R-square and MSE. I infer that the model is overfitted if we set the hyper parameter to 200.
"""
ann.save('my_model.h5') # save the model for further research