#  Exercise 7 | Principle Component Analysis and K-Means Clustering

from matplotlib import use
use('TkAgg')
import numpy as np
import scipy.io
import scipy.misc
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from featureNormalize import featureNormalize
from pca import pca
from projectData import projectData
from recoverData import recoverData
from kMeansInitCentroids import kMeansInitCentroids
from runkMeans import runkMeans
from plotDataPoints import plotDataPoints
from displayData import displayData
from matplotlib.pyplot import show

#  ================== Part 1: Load Example Dataset  ===================

print('Visualizing example dataset for PCA.')
# The following command loads the dataset. You should now have the
# variable X in your environment
data = scipy.io.loadmat('ex7data1.mat')
X = data['X']

# Visualize the example dataset
"""plt.figure()
plt.scatter(X[:, 0], X[:, 1], marker='o', color='b', facecolors='none', lw=1.0)
plt.axis([0.5, 6.5, 2, 8])
plt.axis('equal')
show()"""

input('Program paused. Press Enter to continue...')

#  =============== Part 2: Principal Component Analysis ===============

print('Running PCA on example dataset.')

#  Before running PCA, it is important to first normalize X
X_norm, mu, sigma = featureNormalize(X)

# Run PCA
U, S, V = pca(X_norm)

# Compute mu, the mean of the each feature

# Draw the eigenvectors centered at mean of data. These lines show the
# directions of maximum variations in the dataset.
mu2 = mu + 1.5 * S.dot(U.T)
"""plt.figure()
plt.scatter(X[:, 0], X[:, 1], marker='o', color='b', facecolors='none', lw=1.0)
plt.axis([0.5, 6.5, 2, 8])
plt.axis('equal')
plt.plot([mu[0], mu2[0, 0]], [mu[1], mu2[0, 1]], '-k', lw=2)
plt.plot([mu[0], mu2[1, 0]], [mu[1], mu2[1, 1]], '-k', lw=2)
show()"""

print('Top eigenvector: ')
print(' U(:,1) = %f %f ' % (U[0, 0], U[1, 0]))
print('(you should expect to see -0.707107 -0.707107)')

input('Program paused. Press Enter to continue...')

#  =================== Part 3: Dimension Reduction ===================

print('Dimension reduction on example dataset.')

# Plot the normalized dataset (returned from pca)
plt.figure()
plt.scatter(X_norm[:, 0], X_norm[:, 1], marker='o',
            color='b', facecolors='none', lw=1.0)
plt.axis([-4, 3, -4, 3])  # axis square
plt.axis('equal')

# Project the data onto K = 1 dimension
K = 1
Z = projectData(X_norm, U, K)
print('Projection of the first example: %f' % Z[0])
print('(this value should be about 1.481274)')

X_rec = recoverData(Z, U, K)
print('Approximation of the first example: %f %f' % (X_rec[0, 0], X_rec[0, 1]))
print('(this value should be about  -1.047419 -1.047419)')

#  Draw lines connecting the projected points to the original points
plt.scatter(X_rec[:, 0], X_rec[:, 1], marker='o',
            color='r', facecolor='none', lw=1.0)
for i in range(len(X_norm)):
    plt.plot([X_norm[i, 0], X_rec[i, 0]], [X_norm[i, 1], X_rec[i, 1]], '--k')
show()

input('Program paused. Press Enter to continue...')

#  =============== Part 4: Loading and Visualizing Face Data =============

print('Loading face dataset.')

# Load Face dataset
data = scipy.io.loadmat('ex7faces.mat')
X = data['X']

# Display the first 100 faces in the dataset
plt.figure()
displayData(X[0:100, :])

input('Program paused. Press Enter to continue...')

#  =========== Part 5: PCA on Face Data: Eigenfaces  ===================

print('Running PCA on face dataset.\n(this might take a minute or two ...)\n\n')

# Before running PCA, it is important to first normalize X by subtracting
# the mean value from each feature
X_norm, mu, sigma = featureNormalize(X)

#  Run PCA
U, S, V = pca(X_norm)

#  Visualize the top 36 eigenvectors found
plt.figure()
displayData(U[:, 1:36].T)

input('Program paused. Press Enter to continue...')

#  ============= Part 6: Dimension Reduction for Faces =================
print('Dimension reduction for face dataset.')

K = 100
Z = projectData(X_norm, U, K)

print('The projected data Z has a size of: ')
print('%d %d' % Z.shape)

input('Program paused. Press Enter to continue...')

#  ==== Part 7: Visualization of Faces after PCA Dimension Reduction ====

print('Visualizing the projected (reduced dimension) faces.')

K = 100
X_rec = recoverData(Z, U, K)

# Display normalized data
plt.figure()
plt.subplot(1, 2, 1)
displayData(X_norm[:100,:])
plt.title('Original faces')
plt.axis('equal')

# Display reconstructed data from only k eigenfaces
plt.subplot(1, 2, 2)
displayData(X_rec[:100, :])
plt.title('Recovered faces')
plt.axis('equal')
show()
input('Program paused. Press Enter to continue...')


#  === Part 8 -  PCA for Visualization ===
A = scipy.misc.imread('bird_small.png')

# If imread does not work for you, you can try instead
#   load ('bird_small.mat')

A = A / 255.0
img_size = A.shape
X = A.reshape(img_size[0] * img_size[1], 3)
K = 16
max_iters = 10
initial_centroids = kMeansInitCentroids(X, K)
centroids, idx = runkMeans(X, initial_centroids, max_iters)

# Sample 1000 random indexes (since working with all the data is
# too expensive. If you have a fast computer, you may increase this.
sel = np.floor(np.random.random(1000) * len(X))
sel = sel.astype(int)

# Setup Color Palette

# Visualize the data and centroid memberships in 3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xs = np.array([X[s] for s in sel])
xs = Xs[:, 0]
ys = Xs[:, 1]
zs = Xs[:, 2]
cmap = plt.get_cmap("jet")
idxn = idx[sel]
idxn = idxn.astype('float') / max(idxn.astype('float'))
colors = cmap(idxn)

ax = Axes3D(fig)
ax.scatter3D(xs, ys, zs=zs, edgecolors=colors,
             marker='o', facecolors='none', lw=0.4, s=10)

plt.title('Pixel dataset plotted in 3D. Color shows centroid memberships')
show()
input('Program paused. Press Enter to continue...')

#  === Part 8 -  PCA for Visualization ===
#  Use PCA to project this cloud to 2D for visualization

# Subtract the mean to use PCA
X_norm, mu, sigma = featureNormalize(X)

# PCA and project the data to 2D
U, S, V = pca(X_norm)
Z = projectData(X_norm, U, 2)

# Plot in 2D
plt.figure()
zs = np.array([Z[s] for s in sel])

plotDataPoints(zs, idxn)
plt.title('Pixel dataset plotted in 2D, using PCA for dimensionality reduction')
show()

input('Program paused. Press Enter to continue...')
