
from numpy import *

#points is array of row of data sets


def compute_error_for_given_points(b, m, points):
	totalError = 0
	# i is the row of the data sets
	for i in range(0, len(points)):
		x = points[i, 0]
		y = points[i, 1]
		# **2 means square
		totalError += (y - (m*x + b))**2

	return totalError / float(len(points))


def step_gradient(b_current, m_current, points, learning_rate):
	#gradient descent
	b_gradient = 0
	m_gradient = 0
	#N is number of points
	N = float(len(points))

	#we are gonna up date b and m over time
	for i in range(0,len(points)):
		x = points[i, 0]
		y = points[i, 1]
		b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
		m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
	new_b = b_current - (learning_rate * b_gradient)
	new_m = m_current - (learning_rate * m_gradient)
	return [new_b, new_m]

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
	b = starting_b
	m = starting_m

	for i in range(num_iterations):
		b, m = step_gradient(b, m, array(points), learning_rate)

	return [b,m]

def run():
	#data sets will be separated by a comma like an x and y pair
	points = genfromtxt('data.csv', delimiter=',')

	#hyperparameters
	learning_rate = 0.0001

	#y = mx + b (slope formula)
	initial_b = 0
	initial_m = 0
	num_iterations = 1000

	print "Before gradient descent with b = {0}, m = {1}, error = {2}".format(initial_b, initial_m, compute_error_for_given_points(initial_b, initial_m, points))

	[b, m] = gradient_descent_runner(points,initial_b, initial_m, learning_rate, num_iterations)
	
	print "After gradient descent with b = {0}, m = {1}, error = {2}".format(b, m, compute_error_for_given_points(b, m, points))

	

if __name__ == '__main__':
	run()
