import sys import numpy as np from matplotlib import pyplot from matplotlib.animation import FuncAnimation import matplotlib as mpl sys.path.append('..') from submission import SubmissionBase def displayData(X, example_width=None, figsize=(10, 10)): """ Displays 2D data in a nice grid. Parameters ---------- X : array_like The input data of size (m x n) where m is the number of examples and n is the number of features. example_width : int, optional THe width of each 2-D image in pixels. If not provided, the image is assumed to be square, and the width is the floor of the square root of total number of pixels. figsize : tuple, optional A 2-element tuple indicating the width and height of figure in inches. """ # Compute rows, cols if X.ndim == 2: m, n = X.shape elif X.ndim == 1: n = X.size m = 1 X = X[None] # Promote to a 2 dimensional array else: raise IndexError('Input X should be 1 or 2 dimensional.') example_width = example_width or int(np.round(np.sqrt(n))) example_height = int(n / example_width) # Compute number of items to display display_rows = int(np.floor(np.sqrt(m))) display_cols = int(np.ceil(m / display_rows)) fig, ax_array = pyplot.subplots(display_rows, display_cols, figsize=figsize) fig.subplots_adjust(wspace=0.025, hspace=0.025) ax_array = [ax_array] if m == 1 else ax_array.ravel() for i, ax in enumerate(ax_array): ax.imshow(X[i].reshape(example_height, example_width, order='F'), cmap='gray') ax.axis('off') def featureNormalize(X): """ Normalizes the features in X returns a normalized version of X where the mean value of each feature is 0 and the standard deviation is 1. This is often a good preprocessing step to do when working with learning algorithms. Parameters ---------- X : array_like An dataset which is a (m x n) matrix, where m is the number of examples, and n is the number of dimensions for each example. Returns ------- X_norm : array_like The normalized input dataset. mu : array_like A vector of size n corresponding to the mean for each dimension across all examples. sigma : array_like A vector of size n corresponding to the standard deviations for each dimension across all examples. """ mu = np.mean(X, axis=0) X_norm = X - mu sigma = np.std(X_norm, axis=0, ddof=1) X_norm /= sigma return X_norm, mu, sigma def plotProgresskMeans(i, X, centroid_history, idx_history): """ A helper function that displays the progress of k-Means as it is running. It is intended for use only with 2D data. It plots data points with colors assigned to each centroid. With the previous centroids, it also plots a line between the previous locations and current locations of the centroids. Parameters ---------- i : int Current iteration number of k-means. Used for matplotlib animation function. X : array_like The dataset, which is a matrix (m x n). Note since the plot only supports 2D data, n should be equal to 2. centroid_history : list A list of computed centroids for all iteration. idx_history : list A list of computed assigned indices for all iterations. """ K = centroid_history[0].shape[0] pyplot.gcf().clf() cmap = pyplot.cm.rainbow norm = mpl.colors.Normalize(vmin=0, vmax=2) for k in range(K): current = np.stack([c[k, :] for c in centroid_history[:i+1]], axis=0) pyplot.plot(current[:, 0], current[:, 1], '-Xk', mec='k', lw=2, ms=10, mfc=cmap(norm(k)), mew=2) pyplot.scatter(X[:, 0], X[:, 1], c=idx_history[i], cmap=cmap, marker='o', s=8**2, linewidths=1,) pyplot.grid(False) pyplot.title('Iteration number %d' % (i+1)) def runkMeans(X, centroids, findClosestCentroids, computeCentroids, max_iters=10, plot_progress=False): """ Runs the K-means algorithm. Parameters ---------- X : array_like The data set of size (m, n). Each row of X is a single example of n dimensions. The data set is a total of m examples. centroids : array_like Initial centroid location for each clusters. This is a matrix of size (K, n). K is the total number of clusters and n is the dimensions of each data point. findClosestCentroids : func A function (implemented by student) reference which computes the cluster assignment for each example. computeCentroids : func A function(implemented by student) reference which computes the centroid of each cluster. max_iters : int, optional Specifies the total number of interactions of K-Means to execute. plot_progress : bool, optional A flag that indicates if the function should also plot its progress as the learning happens. This is set to false by default. Returns ------- centroids : array_like A (K x n) matrix of the computed (updated) centroids. idx : array_like A vector of size (m,) for cluster assignment for each example in the dataset. Each entry in idx is within the range [0 ... K-1]. anim : FuncAnimation, optional A matplotlib animation object which can be used to embed a video within the jupyter notebook. This is only returned if `plot_progress` is `True`. """ K = centroids.shape[0] idx = None idx_history = [] centroid_history = [] for i in range(max_iters): idx = findClosestCentroids(X, centroids) if plot_progress: idx_history.append(idx) centroid_history.append(centroids) centroids = computeCentroids(X, idx, K) if plot_progress: fig = pyplot.figure() anim = FuncAnimation(fig, plotProgresskMeans, frames=max_iters, interval=500, repeat_delay=2, fargs=(X, centroid_history, idx_history)) return centroids, idx, anim return centroids, idx class Grader(SubmissionBase): # Random Test Cases X = np.sin(np.arange(1, 166)).reshape(15, 11, order='F') Z = np.cos(np.arange(1, 122)).reshape(11, 11, order='F') C = Z[:5, :] idx = np.arange(1, 16) % 3 def __init__(self): part_names = ['Find Closest Centroids (k-Means)', 'Compute Centroid Means (k-Means)', 'PCA', 'Project Data (PCA)', 'Recover Data (PCA)'] part_names_key = ['7yN0U', 'G1WGM', 'ixOMV', 'AFoJK', 'vf9EL'] assignment_key = 'rGGTuM9gQoaikOnlhLII1A' super().__init__('k-means-clustering-and-pca', assignment_key, part_names, part_names_key) def __iter__(self): for part_id in range(1, 6): try: func = self.functions[part_id] # Each part has different expected arguments/different function if part_id == 1: res = 1 + func(self.X, self.C) elif part_id == 2: res = func(self.X, self.idx, 3) elif part_id == 3: U, S = func(self.X) res = np.hstack([U.ravel('F'), np.diag(S).ravel('F')]).tolist() elif part_id == 4: res = func(self.X, self.Z, 5) elif part_id == 5: res = func(self.X[:, :5], self.Z, 5) else: raise KeyError yield part_id, res except KeyError: yield part_id, 0