from __future__ import division, absolute_import, print_function
import time
import numpy as np
import matplotlib
from  matplotlib import pyplot as plt
from  matplotlib import *
from numpy import *
import scipy as sc
from scipy.optimize import fmin_ncg
 
from plotData import plotData
from sigmoid import sigmoid
from costFunction import costFunction
from costFunction import gradFunction
from plotDecisionBoundary import plotDecisionBoundary
from predict import predict

## Logistic Regression

plt.clf() ; plt.close('all')

# Load data

# The first two columns contains the exam scores
# and the third column contains the label

data = np.loadtxt("C:\\Python37\\Data\\ex2data1.txt", delimiter =",")

X = data[:,0:2] ; y = data[:,2];

#### Part 1 : Plotting

# We start the exercise by first plotting the data to understand the problem
# we are working with .

print("Plotting data with + indicating (y=1) examples and o indicating (y=0) examples.\n")

plotData(X,y)
# Put some labels

plt.xlabel("Exam 1 score")
plt.ylabel("Exam 2 score")
plt.axis([30, 105, 30, 105])
# Specified in plot order

plt.legend(["Admitted", "Not admitted"], loc = 'best')
plt.show()

print("\nProgram paused. Press enter to continue.\n")
time.sleep(2)

#### Part 2 : Compute Cost and Gradient
# In this part of the exercise, we implement the cost and gradient
# for logistic regression. 

# Setup the data matrix appropriatel, and add ones for the intercept term

m,n = X.shape

# Add intercept term to x and X_test

X = np.c_[np.ones((m,)), X]

# Initialize fitting parameters

initial_theta = np.zeros((n+1,))

# Compute and display initial cost and gradient

cost = costFunction (initial_theta, X, y)
grad = gradFunction (initial_theta, X, y)
print("Cost at initial thata (zeros) : %f\n" %cost)
print("Expected cost (approx) : 0.693\n")
print("Gradient and initial theta (zeros): \n", grad)
print("\nExpected gradients (approx):\n -0.1000\n -12.0092\n -11.2628\n")

# Compute and display cost and gradient with non-zero theta

test_theta = [-24, 0.2, 0.2]
cost = costFunction(test_theta, X, y)
grad = gradFunction(test_theta, X, y)

print("\nCost at test_theta: %f\n"%cost)
print("Expected cost (approx): 0.218\n")
print("Gradient at test theta: \n", grad)
print("Expected gradients (approx): \n 0.043\n 2.566\n 2.647\n")

print("\nProgram paused. Press enter to continue.\n")
time.sleep(2)

#### Part 3 : Optimizing using fmin_ncg

opts = {'disp': disp}
theta, cost, u, v, w ,x = fmin_ncg(f = costFunction,
                                   x0= initial_theta,
                                   fprime= gradFunction,
                                   args = (X, y),
                                   maxiter = 400,
                                   full_output = 1
                                   )
# from the guide

#    u = fcalls : int
#        Number of function calls made.
#    v = gcalls : int
#        Number of gradient calls made.
#    w = hcalls : int
#        Number of hessian calls made.
#    x = warnflag : int
#        Warnings generated by the algorithm.
#        1 : Maximum number of iterations exceeded.

# Print theta to screen

print("\nCost at theta  found by fmin_ncg: %f\n" %cost)
print("Expected cost (approx): 0.203\n")
print("theta: \n", theta)
print("\nExpected theta (approx):")
print("-25.161\n 0.206\n 0.201\n")

# Plot Boundary
plotDecisionBoundary(theta, X, y)

# Labels and legend
plt.xlabel("Exam 1 score")
plt.ylabel("Exam 2 score")

# Specified in plot order
plt.legend(['Admitted', 'Not admitted'], loc='best')
plt.show()

print("\nProgram paused. Press enter to continue.\n")
time.sleep(2)

#### Part 4: Predict and Accuracies

# After learning the parameters, you'll like to use it to predict the outcomes
# on unseen datta. In this part, you will use the logistic regression model
# to predict the probability that a student with score 45 on exam 1 and
# score 85 on exam 2 will be admitted.

# Furthermore, you will compute the training and test set accuracies of our
# model.

# Predict probability for a student with score 45 on exam 1
# and score 85 on exam 2

prob = sigmoid((array([1, 45, 85]).dot(theta)))
print("For a student with scores 45 and 85, we predict an admission probability of \n%f" %prob)
print("\nExpected value : 0.775 +/- 0.002 \n\n    ")

# Compute accuracy on our training set
p = predict(theta, X)

print("Train accuracy: %f\n" %(np.mean(p == y) * 100))
print("Expected accuracy (approx): 89.0\n")
print("\n")

