# DO NOT EDIT THIS CELL

print("importing...")
import csv
import random
import math
import operator
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
print("done")

#Input data
data_set = 'students.data'

def loadDataset(filename, split, trainingSet=[] , testSet=[]):
    with open(filename, 'r') as csvfile:
        lines = csv.reader(csvfile)
        dataset = list(lines)
        for x in range(len(dataset) - 1):
            for y in range(len(dataset[0]) - 1):
                dataset[x][y] = float(dataset[x][y])
            if random.random() < split:
                trainingSet.append(dataset[x])
            else:
                testSet.append(dataset[x])

"""
Explanation: Autor: Nils Verheyen\
Matrikelnummer: 3043171
End of explanation
"""


import numpy as np

train = []
test = []

loadDataset(data_set, .75, train, test)

print('train sample', end='\n\n')
for i in range(10):
    print(train[i])
    
    
print('test sample', end='\n\n')
for i in range(10):
    print(test[i])

def plot_data(data_sample):
    
    types = set([t[2] for t in data_sample])
    for t in types:
        df = [row for row in data_sample if row[2] == t]
        xs = [row[0] for row in df]
        ys = [row[1] for row in df]
        plt.scatter(xs, ys)
    plt.show()

print("Training data")
plot_data(train)

print("Test data")
plot_data(test)

# TODO Task 4B.1

def euclideanDistance(sample1, sample2):
    p1 = sample1[:2]
    p2 = sample2[:2]
    distance = math.sqrt(sum([(a - b) ** 2 for a, b in zip(p1, p2)]))
    return distance

print(train[0])
print(train[1])

euclideanDistance(train[0], train[1])

# TODO Task 4B.2

def getNeighbors(trainingSet, testSample):
    
    k = 3
    
    #TODO: calcualte the euclidean distance to the neighbors.
    #Then return the k-nearest.
    
    neighbors = []
    for s in trainingSet:
        distance = euclideanDistance(s, testSample)
        neighbors.append((s, distance))
    nearest = sorted(neighbors, key=lambda t: t[1])
    
    # return with distance
    return nearest[:k]

sample = test[0]
neighbors = getNeighbors(train, sample)

print(sample)
for n in neighbors:
    print(n)

# TODO Task 4B.3


def estimateType(neighbors):
    
    #TODO: determine the type of the current sample
    #by checking the neighbors and return it.
    
    types = dict()
    for key in set([t[2] for t in train]):
        types[key] = dict(count=0, distance=0)
    # types: 
    # {
    #     'passed': {
    #         'count': 0
    #         'distance': 0
    #     },
    #     ...
    # }
    
    for n in neighbors:
        key = n[0][2]
        types[key]['count'] += 1
        types[key]['distance'] += n[1]
        
    # max count of all types
    max_val = max(types.items(), key = lambda x: x[1]['count'])[1]['count']
    # may contain multiple types in case of even k (eg. fails=2, passed=2)
    targets = [t for t in types.items() if t[1]['count'] == max_val]
    
    # return the first type if only one type is inside the list
    if len(targets) == 1:
        return targets[0][0]
    
    # otherwise sort by distance and return the type with the lowest distance
    targets = sorted(targets, key=lambda t: t[1]['distance'])
    return targets[0]

estimateType(neighbors)

def getAccuracy(testSet, predictions):
    correct = 0
    for x in range(len(testSet)):
        if testSet[x][-1] == predictions[x]:
            correct += 1
    return (correct/float(len(testSet))) * 100.0

from datetime import datetime
start = datetime.now()

predictions = [estimateType(getNeighbors(train, t)) for t in test]

end = datetime.now()
print('Took %s to complete' % str(end - start))

print('Accuracy: %f' % getAccuracy(test, predictions))

"""
Explanation: Implement the following functions

euclideanDistance
getNeighbors
estimateType
End of explanation
"""


xs = []
ys = []
zs = []
cs = []

fig = plt.figure(figsize=(25, 25))

plot_2d = fig.add_subplot(441)

with open(data_set, 'r') as csvfile:
    lines = csv.reader(csvfile)
    data = list(lines)
    for d in data:
        xs.append(int(d[0]))
        ys.append(int(d[1]))
        cs.append('r' if d[2] == "fails" else "g")

plot_2d.scatter(xs, ys, c=cs, marker='o')
plot_2d.set_xlabel('X: Student learned')
plot_2d.set_ylabel('Y: Student IQ')

plt.show()

trainingSet = []
testSet = []
split = 0.67

loadDataset(data_set, split, trainingSet, testSet)
print ('Train set: ' + repr(len(trainingSet)))
print ('Test set: ' + repr(len(testSet)))

predictions=[]

#This is k 
count = 0
for x in range(len(testSet)):
    neighbors = getNeighbors(trainingSet, testSet[x])
    result = estimateType(neighbors)
    predictions.append(result)
    print(str(count) + ': predicted=' + repr(result) + ', actual=' + repr(testSet[x][-1]))
    count += 1
    
accuracy = getAccuracy(testSet, predictions)
print('Accuracy: ' + repr(accuracy) + '%')

"""
Explanation: Take a look on the data to get an impression of what's going on.
You should see two clusters of students.
End of explanation
"""


