# -*- coding: utf-8 -*-
"""
Created on Thu Oct 31 09:14:10 2019
https://github.com/jaimeps/adaboost-implementation/blob/master/adaboost.py
@author: sprawesh
"""

import pandas as pd
from matplotlib import pyplot as plt
import numpy as np
import sklearn.metrics as sm

plt.rcParams['font.family'] = 'Arial'
plt.rcParams['axes.linewidth'] = 0.5

def error_rate(y_true, y_pred):
    return 1 - sm.accuracy_score(y_true, y_pred)

#load data
df = pd.read_csv('E:\\fall-19\\ime692\\python-codes\\spam\\Spam.txt')
df.head()
var = list(df.columns)
var.remove('test')
var.remove('spam')

#feature names
features = []
def feature_names(var):
    for item in var:
        i = item.rindex('_', 0, len(item))
        si = item[i:len(item)]
        features.append(si)

feature_names(var)
#remove test and spam from columns
#preparing response and predictor for train and test
X, y = df[var].values, df['spam'].values

#split X, and y for training and test
X_train = X[df.test.values == 0]
X_test = X[df.test.values == 1]

y_train = y[df.test.values == 0]
y_test = y[df.test.values == 1]

from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier

#random forest
def rf_predict_test(X_train, y_train, X_test, y_test):
    err_test = []
    for i in range(500, 501):
        rf_clf = RandomForestClassifier(n_estimators=i+1, random_state=10).fit(X_train, y_train) 
        y_test_hat = rf_clf.predict(X_test)
        rf_error_rate = error_rate(y_test, y_test_hat)
        err_test.append((i, rf_error_rate)) 
        if (i%20 == 0):
            print(f'Random Forest Test Error Rate: {rf_error_rate*100:.1f}%', "index = ", i)
    return err_test

dum = rf_predict_test(X_train, y_train, X_test, y_test)
#convert tuple list into numpy array
dt = np.dtype('int,float')
xerr = np.array(dum, dtype=dt)
            
           
#xerr['f0'][1:10]
#xerr['f1'][1:10]

#bagging
def bag_predict_test(X_train, y_train, X_test, y_test):
    err_test = []
    for i in range(500):
        rf_clf = RandomForestClassifier(n_estimators=i+1, random_state=10, max_features=None).fit(X_train, y_train) 
        y_test_hat = rf_clf.predict(X_test)
        rf_error_rate = error_rate(y_test, y_test_hat)
        err_test.append((i, rf_error_rate))        
        if (i%20 == 0):
            print(f'Random Forest Test Error Rate: {rf_error_rate*100:.1f}%', "index = ", i)
    return err_test 


#plot OOB results in the sample plot created earlier
berr = np.array(bag_predict_test(X_train, y_train, X_test, y_test), dtype=dt)


#adaboost implementation
#SAMME is Adaboost for binary classification
def ada_predict_test(X_train, y_train, X_test, y_test, depth):
    err_test = []
    for i in range(300):
        ad_clf = AdaBoostClassifier(base_estimator=DecisionTreeClassifier(max_depth=depth), n_estimators=i+1, algorithm="SAMME").fit(X_train, y_train)
        y_test_hat = ad_clf.predict(X_test)
        ad_error_rate = error_rate(y_test, y_test_hat)
        err_test.append((i, ad_error_rate))
        if (i%20 == 0):
            print(f'Boosting Test Error Rate: {ad_error_rate*100:.1f}%', "index = ", i)
    return err_test


#aerr = np.array(ada_te, dtype=dt)


for i in range(8):
    f_name = "boost_test"
    ada_te = ada_predict_test(X_train, y_train, X_test, y_test, i+1)
    ada_tr = ada_predict_test(X_train, y_train, X_train, y_train, i+1)
    aet = np.array(ada_te, dtype=dt)
    atr = np.array(ada_tr, dtype=dt)
    y = pd.DataFrame(aet)
    z = pd.DataFrame(atr)
    f_name = f_name + str(i+1) + ".csv"
    y.to_csv('E:\\fall-19\\ime692\\python-codes\\spam\\'+f_name)
    z.to_csv('E:\\fall-19\\ime692\\python-codes\\spam\\'+"boost_train"+str(i+1) + ".csv")



#plot the results for random forest
fig, ax = plt.subplots(figsize=(2.8, 2.8), dpi=110)
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
#ax.scatter(xerr['f0'], xerr['f1'], c='#0000FF', s=1)
ax.plot(xerr['f0'], xerr['f1'], c='#0000FF', linewidth = 0.6, label = 'Random Forest')
ax.plot(berr['f0'], berr['f1'], c='#00000F', linewidth = 0.6, label = 'Bagging') 
#ax.plot(aerr['f0'], aerr['f1'], c='#a000FE', linewidth = 0.6, label = 'Boosting')         
ax.axhline(y=0.047, c='#0000FF', linewidth=0.6, linestyle='--') # green '#00FF00'
ax.axhline(y=0.055, c='#00000F', linewidth=0.6, linestyle='--')
ax.axhline(y=0.081, c='#a0000F', linewidth=0.6, linestyle='--')           
           
for i in ax.get_yticklabels() + ax.get_xticklabels():
    i.set_fontsize(8)
ax.set_xlabel('Number of Trees', color='#ab00FF', fontsize=8)
ax.set_ylabel('Test error', color='#ab00FF', fontsize=8)
#legend should come after giving label?              
plt.legend(loc='upper right', bbox_to_anchor=(0.5, 0.5, 0.5, 0.5), fontsize = 8)             
plt.title('Spam Data', fontsize=10)   
plt.show()

#variable importance plot
rf_cx = RandomForestClassifier(n_estimators=100, random_state=10).fit(X_train, y_train)

def plot_relative_importance(importance):
    max_importance = np.max(importance)
    relative_importance = sorted(zip(100*importance/max_importance, features))
    yticks = np.arange(len(relative_importance))
    yticklabels = [ri[1] for ri in relative_importance][::-1]
    bars_sizes = [ri[0] for ri in relative_importance][::-1]
    
    fig, ax = plt.subplots(figsize=(4.3, 6.5), dpi=150)
    bars = ax.barh(yticks, bars_sizes, height=0.8, color='red')
    plt.setp(ax, yticks=yticks, yticklabels=yticklabels)
    ax.set_xlim([0, 100])
    ax.set_ylim([-0.5, 57])
    for e in ax.get_yticklabels()+ax.get_xticklabels():
        e.set_fontsize(6)
    ax.tick_params(left=False)
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    _ = ax.set_xlabel('Relative Importance', fontsize=7)


plot_relative_importance(rf_cx.feature_importances_)


        
