75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
from __future__ import print_function, division
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
from mlfromscratch.utils import calculate_covariance_matrix, normalize, standardize
|
|
|
|
|
|
class MultiClassLDA():
|
|
"""Enables dimensionality reduction for multiple
|
|
class distributions. It transforms the features space into a space where
|
|
the between class scatter is maximized and the within class scatter is
|
|
minimized.
|
|
|
|
Parameters:
|
|
-----------
|
|
solver: str
|
|
If 'svd' we use the pseudo-inverse to calculate the inverse of matrices
|
|
when doing the transformation.
|
|
"""
|
|
def __init__(self, solver="svd"):
|
|
self.solver = solver
|
|
|
|
def _calculate_scatter_matrices(self, X, y):
|
|
n_features = np.shape(X)[1]
|
|
labels = np.unique(y)
|
|
|
|
# Within class scatter matrix:
|
|
# SW = sum{ (X_for_class - mean_of_X_for_class)^2 }
|
|
# <=> (n_samples_X_for_class - 1) * covar(X_for_class)
|
|
SW = np.empty((n_features, n_features))
|
|
for label in labels:
|
|
_X = X[y == label]
|
|
SW += (len(_X) - 1) * calculate_covariance_matrix(_X)
|
|
|
|
# Between class scatter:
|
|
# SB = sum{ n_samples_for_class * (mean_for_class - total_mean)^2 }
|
|
total_mean = np.mean(X, axis=0)
|
|
SB = np.empty((n_features, n_features))
|
|
for label in labels:
|
|
_X = X[y == label]
|
|
_mean = np.mean(_X, axis=0)
|
|
SB += len(_X) * (_mean - total_mean).dot((_mean - total_mean).T)
|
|
|
|
return SW, SB
|
|
|
|
def transform(self, X, y, n_components):
|
|
SW, SB = self._calculate_scatter_matrices(X, y)
|
|
|
|
# Determine SW^-1 * SB by calculating inverse of SW
|
|
A = np.linalg.inv(SW).dot(SB)
|
|
|
|
# Get eigenvalues and eigenvectors of SW^-1 * SB
|
|
eigenvalues, eigenvectors = np.linalg.eigh(A)
|
|
|
|
# Sort the eigenvalues and corresponding eigenvectors from largest
|
|
# to smallest eigenvalue and select the first n_components
|
|
idx = eigenvalues.argsort()[::-1]
|
|
eigenvalues = eigenvalues[idx][:n_components]
|
|
eigenvectors = eigenvectors[:, idx][:, :n_components]
|
|
|
|
# Project the data onto eigenvectors
|
|
X_transformed = X.dot(eigenvectors)
|
|
|
|
return X_transformed
|
|
|
|
|
|
def plot_in_2d(self, X, y, title=None):
|
|
""" Plot the dataset X and the corresponding labels y in 2D using the LDA
|
|
transformation."""
|
|
X_transformed = self.transform(X, y, n_components=2)
|
|
x1 = X_transformed[:, 0]
|
|
x2 = X_transformed[:, 1]
|
|
plt.scatter(x1, x2, c=y)
|
|
if title: plt.title(title)
|
|
plt.show()
|