import numpy as np
import pandas as pd
import re
import csv
from random import random
from time import time
from sklearn.neighbors import KNeighborsClassifier

titlelist = {"Mr":0, \
             "Ms":1, "Miss":1, "Mlle":1,\
             "Mrs":2, "Master":2, "Mme":2,\
             "Col":3, "Major":3, "Capt":3, "Rev":3, "Dr":3,\
             "Sir":4, "Don":4, "Jonkheer":4, "Lady":4,"the Countess":4}
             
#Print you can execute arbitrary python code
train = pd.read_csv("../input/train.csv", dtype={"Age": np.float64}, )
test = pd.read_csv("../input/test.csv", dtype={"Age": np.float64}, )

#Print to standard output, and see the results in the "log" section below after running your script
print("\n\nTop of the training data:")
print(train.head())

print("\n\nSummary statistics of training data")
print(train.describe())

#Any files you save will be available in the output tab below
#train.to_csv('copy_of_the_training_data.csv', index=False)

def processRow(row,titlelist=titlelist):
    sexList = {"male":0, "female":1}
    embarkList = {"C":1, "Q":2, "S":3}
    features = list()
    label = np.array(int(row[1]))               #label
    PassengerId = np.array(int(row[0]))         #id
    title = re.findall(', (.*?)\. ',row[3])[0]
    features.append(int(row[2]))                #Pclass
    try:
        features.append(titlelist[title])       #Title
    except:
        features.append(0)                      #Title
    features.append(sexList[row[4]])            #Sex
    try:
        features.append(int(row[5]))            #Age
    except:
        features.append(28)                      #Median of Age
    features.append(int(row[6]))                #Siblings
    features.append(int(row[7]))                #Parents
    features.append(float(row[9]))              #Fare
    features.append(embarkList.get(row[11],0))  #Embark Place
    features.append(\
        float(row[9])/(1.0+float(row[6])+float(row[7])))    #Fare per person
    try:
        features.append(int(row[2])*int(row[5]))    #Class*Age
    except:
        features.append(int(row[2])*28)
    features.append(sexList[row[4]]*int(row[2])) #Sex*class
    return features+[label,PassengerId]

def processData(datafile="train.csv"):
    features = list()
    with open(datafile,"r") as csvfile:
        reader = csv.reader(csvfile, delimiter=",")
        next(reader, None)
        for row in reader:
            features.append(processRow(row))

    features = np.array(features)
    labels = features[:,-2]
    PassengerIds = features[:,-1]
    features = features[:,:-2]
    avg = np.average(features,axis=0)
    maxmin = np.max(features,axis=0)-np.min(features,axis=0)
    features = (features-avg)/maxmin
    #print maxmin
    return features, labels,PassengerIds,avg, maxmin

def predictData(avg,maxmin,datafile="test.csv"):
    features = list()
    with open(datafile,"r") as csvfile:
        reader = csv.reader(csvfile, delimiter=",")
        next(reader, None)
        for row in reader:
            features.append(predictRow(row))

    features = np.array(features)
    PassengerIds = features[:,-1]
    features = features[:,:-1]
    features = (features-avg)/maxmin
    #print maxmin
    return features, PassengerIds

def predictRow(row,titlelist=titlelist):
    sexList = {"male":0, "female":1}
    embarkList = {"C":1, "Q":2, "S":3}
    features = list()
    passendgerID = int(row[0])                  #id
    title = re.findall(', (.*?)\. ',row[2])[0]
    features.append(int(row[1]))                #Pclass
    try: features.append(titlelist[title])      #Title
    except: features.append(0)                  #Title
    features.append(sexList[row[3]])            #Sex
    try: features.append(int(row[4]))           #Age
    except: features.append(28)                 #Median of Age
    features.append(int(row[5]))                #Siblings
    features.append(int(row[6]))                #Parents
    try: features.append(float(row[8]))              #Fare
    except: features.append(0)
    features.append(embarkList.get(row[10],0))  #Embark Place
    try: features.append(\
        float(row[8])/(1.0+float(row[5])+float(row[6])))    #Fare per person
    except: features.append(0)
    try:
        features.append(int(row[1])*int(row[4]))    #Class*Age
    except:
        features.append(int(row[1])*28)
    features.append(sexList[row[3]]*int(row[1])) #Sex*class
    return features+[passendgerID]

def savePredictions(Ids,predictions,title):
    try:
        fh = open("Prediction_"+title+".csv","w")
        fh.writelines("PassengerId,Survived\n")
        for idx,row in enumerate(predictions):
            fh.writelines(str(int(Ids[idx]))+","+str(int(row))+'\n')
    except:
        print("Predictions not saved")
    finally:
        fh.close()

def generateSets(features,labels,cv,test,rnd=True):
    f_train = list()
    l_train = list()
    f_test = list()
    l_test = list()
    f_cv = list()
    l_cv = list()
    
    for label,item in enumerate(features):
        if rnd: value = random()
        else: value = float(label)/float(len(features))
        if value < cv:
            f_cv.append(item)
            l_cv.append(labels[label])
        elif value < cv+test:
            f_test.append(item)
            l_test.append(labels[label])
        else:
            f_train.append(item)
            l_train.append(labels[label])
          

    return np.array(f_train), np.array(l_train), \
           np.array(f_cv), np.array(l_cv), \
           np.array(f_test), np.array(l_test)
           
           
def createLog(clf,clf_type,time,cv_score,test_score):
    result = clf.get_params().copy()
    log = "Top "+clf_type+" classifier is:\n"
    for item in result.keys():
        log += item +": "+str(result[item])+"\n"

    log += "\nTraining time: "+str(time)+"s\n"+\
           "CV score: "+str(cv_score)+"\n"+\
           "Test score: "+str(test_score)
    return log
        
#features, labels = processData("train.csv")

'''features_train, labels_train,\
                features_cv, labels_cv,\
                features_test, labels_test \
                = generateSets(features,labels,0.15,0.15)
'''
def evaluate(clf, top_score, top_clf,f_t,l_t):
    score = clf.score(f_t, l_t)
    if  score > top_score:
        return clf, score
    else:
        return top_clf, top_score
        
        
features, labels, PassengerId, avg, maxmin = processData("../input/train.csv")

features_train, labels_train,\
                features_cv, labels_cv,\
                features_test, labels_test \
                = generateSets(features,labels,0.1,0.01,rnd=False)

#w = [0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1.0, 3.0, 10.0, 30.0, 100.0, 200.0, 300.0]
w = [0.002, 0.004, 0.006, 0.008, 0.01, 0.012, 0.014, 0.016, 0.018, 0.02]
print("kNN")

top_general_score = 0.0
top_general_clf = None


top_score = 0.0
top_clf = None
t0 = time()
for n_neighbors in range(2,50):############
    for weights in ['uniform', 'distance']:
        for algorithm in ['auto', 'ball_tree', 'kd_tree', 'brute']:
            for leaf_size in range(2,50): #### 
                clf_KNN = KNeighborsClassifier(n_neighbors=n_neighbors, weights=weights, \
                                                  algorithm=algorithm, \
                                                  leaf_size=leaf_size)
                clf_KNN.fit(features_train, labels_train)
                top_clf, top_score = \
                             evaluate(clf_KNN, top_score, top_clf,features_cv,labels_cv) 

dt = time()-t0
score = top_clf.score(features_test, labels_test)
log = createLog(top_clf,"kNN",dt,top_score,score)
#sendLog(log)
print(log)

features_predict, PassengerId_predict = predictData(avg,maxmin,"../input/test.csv")
predictions = top_clf.predict(features_predict)

#savePredictions(PassengerId_predict,predictions,"kNN")