'''
	Erich Chu 
	Perceptron Algorithm
'''
import sys
import csv
import math
import operator

def loadDataSets(trainingFile, testFile):
	train_dataset = []
	test_dataset = []
	# Training data
	with open(trainingFile, 'rb') as csvfile:
		csvreader = csv.reader(csvfile)
		next(csvreader, None)
		for row in csvreader:
			train_dataset.append(row)

	with open(testFile, 'rb') as csvfile:
		csvreader = csv.reader(csvfile)
		next(csvreader, None)
		for row in csvreader:
			test_dataset.append(row)

	return train_dataset, test_dataset

def getBiggerVal(value1, value2):
	return float(value1) if float(value1) > float(value2) else float(value2)

def getSmallerVal(value1, value2):
	return float(value1) if float(value1) < float(value2) else float(value2)

def findMinAndMaxValues(dataset):
	max_population = max_population_change = max_age65plus = max_black = max_hispanic = max_edu_bachelors = max_income = max_poverty = max_density = sys.float_info.min
	min_population = min_population_change = min_age65plus = min_black = min_hispanic = min_edu_bachelors = min_income = min_poverty = min_density = sys.float_info.max

	for data in dataset:
		max_population = getBiggerVal(data[1], max_population)
		max_population_change = getBiggerVal(data[2], max_population_change)
		max_age65plus = getBiggerVal(data[3], max_age65plus)
		max_black = getBiggerVal(data[4], max_black)
		max_hispanic = getBiggerVal(data[5], max_hispanic)
		max_edu_bachelors = getBiggerVal(data[6], max_edu_bachelors)
		max_income = getBiggerVal(data[7], max_income)
		max_poverty = getBiggerVal(data[8], max_poverty)
		max_density = getBiggerVal(data[9], max_density)
		min_population = getSmallerVal(data[1], min_population)
		min_population_change = getSmallerVal(data[2], min_population_change)
		min_age65plus = getSmallerVal(data[3], min_age65plus)
		min_black = getSmallerVal(data[4], min_black)
		min_hispanic = getSmallerVal(data[5], min_hispanic)
		min_edu_bachelors = getSmallerVal(data[6], min_edu_bachelors)
		min_income = getSmallerVal(data[7], min_income)
		min_poverty = getSmallerVal(data[8], min_poverty)
		min_density = getSmallerVal(data[9], min_density)
		
	minValues = []
	maxValues = []
	maxValues += [max_population, max_population_change, max_age65plus, max_black, max_hispanic, max_edu_bachelors, max_income, max_poverty, max_density]
	minValues += [min_population, min_population_change, min_age65plus, min_black, min_hispanic, min_edu_bachelors, min_income, min_poverty, min_density]
	return minValues, maxValues

def normalizeData(dataset, minValues, maxValues):
	for datarow in dataset:
		for data in range(len(datarow)):
			if data == 0:
				continue
			else:
				datarow[data] = (float(datarow[data]) - minValues[data-1])/ (maxValues[data-1] - minValues[data-1])
	return dataset

def getAccuracy(predictionResults, test_dataset):
	correct = 0
	for test_data in range(len(test_dataset)):
		if int(test_dataset[test_data][0]) == predictionResults[test_data]:
			correct += 1
	return (float(correct) / len(test_dataset)) * 100

def makePredictionWithWeights(data, weights):
	prediction = weights[0]
	for i in range(1, len(data)):
		prediction += weights[i-1] * data[i]
	if prediction >= 0.0:
		return 1.0
	else:
		return 0.0

def getWeights(train_dataset, learning_rate, epochs):
	# Get number of cols except for party col
	weights = [0.0 for i in range(len(train_dataset[0]) - 1)]
	for epoch in range(epochs):
		for train_datarow in train_dataset:
			prediction = makePredictionWithWeights(train_datarow, weights)
			error = float(train_datarow[0]) - prediction
			weights[0] += (learning_rate * error)
			for i in range(1, len(train_datarow)):
				weights[i-1] += train_datarow[i] * learning_rate * error
	return weights

def predictPartyUsingPerceptron(test_dataset, final_weights):
	allPredictions = []
	for test_data in test_dataset:
		allPredictions.append(makePredictionWithWeights(test_data, final_weights))
	return allPredictions
	
def main():
	raw_train_dataset, raw_test_dataset = loadDataSets('votes-train.csv', 'votes-test.csv')
	trainMinValues, trainMaxValues = findMinAndMaxValues(raw_train_dataset)
	testMinValues, testMaxValues = findMinAndMaxValues(raw_test_dataset)
	train_dataset = normalizeData(raw_train_dataset, trainMinValues, trainMaxValues)
	test_dataset = normalizeData(raw_test_dataset, testMinValues, testMaxValues)

	final_weights = getWeights(train_dataset, 0.1, 145)
	predictionResults = predictPartyUsingPerceptron(test_dataset, final_weights)
	print getAccuracy(predictionResults, test_dataset)	

main()

