import numpy as np

import pandas as pd

import math

from sys import stdout

def get_numpy_data(data, features, output):

#Adding a constant column with value 1 in the dataframe.

data['constant'] = 1

#Adding the name of the constant column in the feature list.

features = ['constant'] + features

#Creating Feature matrix(Selecting columns and converting to matrix).

features_matrix=data[features].as_matrix()

#Target column is converted to the numpy array

output_array=np.array(data[output])

return(features_matrix, output_array)

def predict_outcome(feature_matrix, weights):

weights=np.array(weights)

predictions = np.dot(feature_matrix, weights)

return predictions

def errors(output,predictions):

errors=predictions-output

return errors

def feature_derivative(errors, feature):

derivative=np.dot(2,np.dot(feature,errors))

return derivative

def regression_gradient_descent(feature_matrix, output, initial_weights, step_size, tolerance):

converged = False

#Initital weights are converted to numpy array

weights = np.array(initial_weights)

while not converged:

# compute the predictions based on feature_matrix and weights:

predictions=predict_outcome(feature_matrix,weights)

# compute the errors as predictions - output:

error=errors(output,predictions)

gradient_sum_squares = 0 # initialize the gradient

# while not converged, update each weight individually:

for i in range(len(weights)):

# Recall that feature_matrix[:, i] is the feature column associated with weights[i]

feature=feature_matrix[:, i]

# compute the derivative for weight[i]:

#predict=predict_outcome(feature,weights[i])

#err=errors(output,predict)

deriv=feature_derivative(error,feature)

# add the squared derivative to the gradient magnitude

gradient_sum_squares=gradient_sum_squares+(deriv**2)

# update the weight based on step size and derivative:

weights[i]=weights[i] - np.dot(step_size,deriv)

gradient_magnitude = math.sqrt(gradient_sum_squares)

stdout.write("\r%d" % int(gradient_magnitude))

stdout.flush()

if gradient_magnitude < tolerance:

converged = True

return(weights)