# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # 第16章 主成分分析

# 1.假设$x$为$m$ 维随机变量，其均值为$\mu$，协方差矩阵为$\Sigma$。
#
# 考虑由$m$维随机变量$x$到$m$维随机变量$y$的线性变换
# $$y _ { i } = \alpha _ { i } ^ { T } x = \sum _ { k = 1 } ^ { m } \alpha _ { k i } x _ { k } , \quad i = 1,2 , \cdots , m$$
#
# 其中$\alpha _ { i } ^ { T } = ( \alpha _ { 1 i } , \alpha _ { 2 i } , \cdots , \alpha _ { m i } )$。
#
# 如果该线性变换满足以下条件，则称之为总体主成分：
#
# （1）$\alpha _ { i } ^ { T } \alpha _ { i } = 1 , i = 1,2 , \cdots , m$；
#
# （2）$\operatorname { cov } ( y _ { i } , y _ { j } ) = 0 ( i \neq j )$;
#
# （3）变量$y_1$是$x$的所有线性变换中方差最大的；$y_2$是与$y_1$不相关的$x$的所有线性变换中方差最大的；一般地，$y_i$是与$y _ { 1 } , y _ { 2 } , \cdots , y _ { i - 1 } , ( i = 1,2 , \cdots , m )$都不相关的$x$的所有线性变换中方差最大的；这时分别称$y _ { 1 } , y _ { 2 } , \cdots , y _ { m }$为$x$的第一主成分、第二主成分、…、第$m$主成分。
#
# 2.假设$x$是$m$维随机变量，其协方差矩阵是$\Sigma$，$\Sigma$的特征值分别是$\lambda _ { 1 } \geq\lambda _ { 2 } \geq \cdots \geq \lambda _ { m } \geq 0$，特征值对应的单位特征向量分别是$\alpha _ { 1 } , \alpha _ { 2 } , \cdots , \alpha _ { m }$，则$x$的第2主成分可以写作
#
# $$y _ { i } = \alpha _ { i } ^ { T } x = \sum _ { k = 1 } ^ { m } \alpha _ { k i } x _ { k } , \quad i = 1,2 , \cdots , m$$
# 并且，$x$的第$i$主成分的方差是协方差矩阵$\Sigma$的第$i$个特征值，即$$\operatorname { var } ( y _ { i } ) = \alpha _ { i } ^ { T } \Sigma \alpha _ { i } = \lambda _ { i }$$
#
# 3.主成分有以下性质：
#
# 主成分$y$的协方差矩阵是对角矩阵$$\operatorname { cov } ( y ) = \Lambda = \operatorname { diag } ( \lambda _ { 1 } , \lambda _ { 2 } , \cdots , \lambda _ { m } )$$
#
# 主成分$y$的方差之和等于随机变量$x$的方差之和
# $$\sum _ { i = 1 } ^ { m } \lambda _ { i } = \sum _ { i = 1 } ^ { m } \sigma _ { i i }$$
# 其中$\sigma _ { i i }$是$x_2$的方差，即协方差矩阵$\Sigma$的对角线元素。
#
# 主成分$y_k$与变量$x_2$的相关系数$\rho ( y _ { k } , x _ { i } )$称为因子负荷量（factor loading），它表示第$k$个主成分$y_k$与变量$x$的相关关系，即$y_k$对$x$的贡献程度。
# $$\rho ( y _ { k } , x _ { i } ) = \frac { \sqrt { \lambda _ { k } } \alpha _ { i k } } { \sqrt { \sigma _ { i i } } } , \quad k , i = 1,2 , \cdots , m$$
#
# 4.样本主成分分析就是基于样本协方差矩阵的主成分分析。
#
# 给定样本矩阵
# $$X = \left[ \begin{array} { l l l l } { x _ { 1 } } & { x _ { 2 } } & { \cdots } & { x _ { n } } \end{array} \right] = \left[ \begin{array} { c c c c } { x _ { 11 } } & { x _ { 12 } } & { \cdots } & { x _ { 1 n } } \\ { x _ { 21 } } & { x _ { 22 } } & { \cdots } & { x _ { 2 n } } \\ { \vdots } & { \vdots } & { } & { \vdots } \\ { x _ { m 1 } } & { x _ { m 2 } } & { \cdots } & { x _ { m n } } \end{array} \right]$$
#
# 其中$x _ { j } = ( x _ { 1 j } , x _ { 2 j } , \cdots , x _ { m j } ) ^ { T }$是$x$的第$j$个独立观测样本，$j=1,2，…,n$。 
#
# $X$的样本协方差矩阵
# $$\left. \begin{array} { c } { S = [ s _ { i j } ] _ { m \times m } , \quad s _ { i j } = \frac { 1 } { n - 1 } \sum _ { k = 1 } ^ { n } ( x _ { i k } - \overline { x } _ { i } ) ( x _ { j k } - \overline { x } _ { j } ) } \\ { i = 1,2 , \cdots , m , \quad j = 1,2 , \cdots , m } \end{array} \right.$$
#
# 给定样本数据矩阵$X$，考虑向量$x$到$y$的线性变换$$y = A ^ { T } x$$
# 这里
# $$A = \left[ \begin{array} { l l l l } { a _ { 1 } } & { a _ { 2 } } & { \cdots } & { a _ { m } } \end{array} \right] = \left[ \begin{array} { c c c c } { a _ { 11 } } & { a _ { 12 } } & { \cdots } & { a _ { 1 m } } \\ { a _ { 21 } } & { a _ { 22 } } & { \cdots } & { a _ { 2 m } } \\ { \vdots } & { \vdots } & { } & { \vdots } \\ { a _ { m 1 } } & { a _ { m 2 } } & { \cdots } & { a _ { m m } } \end{array} \right]$$
#
# 如果该线性变换满足以下条件，则称之为样本主成分。样本第一主成分$y _ { 1 } = a _ { 1 } ^ { T } x$是在$a _ { 1 } ^ { T } a _ { 1 } = 1$条件下，使得$a _ { 1 } ^ { T } x _ { j } ( j = 1,2 , \cdots , n )$的样本方差$a _ { 1 } ^ { T } S a _ { 1 }$最大的$x$的线性变换；
#
# 样本第二主成分$y _ { 2 } = a _ { 2 } ^ { T } x$是在$a _ { 2 } ^ { T } a _ { 2 } = 1$和$a _ { 2 } ^ { T } x _ { j }$与$a _ { 1 } ^ { T } x _ { j } ( j = 1,2 , \cdots , n )$的样本协方差$a _ { 1 } ^ { T } S a _ { 2 } = 0$条件下，使得$a _ { 2 } ^ { T } x _ { j } ( j = 1,2 , \cdots , n )$的样本方差$a _ { 2 } ^ { T } S a _ { 2 }$最大的$x$的线性变换；
#
# 一般地，样本第$i$主成分$y _ { i } = a _ { i } ^ { T } x$是在$a _ { i } ^ { T } a _ { i } = 1$和$a _ { i } ^ { T } x _ { j }$与$a _ { k } ^ { T } x _ { j } ( k < i , j = 1,2 , \cdots , n )$的样本协方差$a _ { k } ^ { T } S a _ { i } = 0$条件下，使得$a _ { i } ^ { T } x _ { j } ( j = 1,2 , \cdots , n )$的样本方差$a _ { k } ^ { T } S a _ { i }$最大的$x$的线性变换。
#
# 5.主成分分析方法主要有两种，可以通过相关矩阵的特征值分解或样本矩阵的奇异值分解进行。
#
# （1）相关矩阵的特征值分解算法。针对$m \times n$样本矩阵$X$，求样本相关矩阵
# $$R = \frac { 1 } { n - 1 } X X ^ { T }$$
# 再求样本相关矩阵的$k$个特征值和对应的单位特征向量，构造正交矩阵
# $$V = ( v _ { 1 } , v _ { 2 } , \cdots , v _ { k } )$$
#
# $V$的每一列对应一个主成分，得到$k \times n$样本主成分矩阵
# $$Y = V ^ { T } X$$
#
# （2）矩阵$X$的奇异值分解算法。针对$m \times n$样本矩阵$X$ 
# $$X ^ { \prime } = \frac { 1 } { \sqrt { n - 1 } } X ^ { T }$$
# 对矩阵$X ^ { \prime }$进行截断奇异值分解，保留$k$个奇异值、奇异向量，得到
# $$X ^ { \prime } = U S V ^ { T }$$
# $V$的每一列对应一个主成分，得到$k \times n$样本主成分矩阵$Y$
# $$Y = V ^ { T } X$$

# 本章代码直接使用Coursera机器学习课程的第六个编程练习。

# ----
# PCA（principal components analysis）即主成分分析技术旨在利用降维的思想，把多指标转化为少数几个综合指标。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
from scipy.io import loadmat

data = loadmat('data/ex7data1.mat')
# data

# +
X = data['X']

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(X[:, 0], X[:, 1])
plt.show()


# -

# PCA的算法相当简单。 在确保数据被归一化之后，输出仅仅是原始数据的协方差矩阵的奇异值分解。

def pca(X):
    # normalize the features
    X = (X - X.mean()) / X.std()
    
    # compute the covariance matrix
    X = np.matrix(X)
    cov = (X.T * X) / X.shape[0]
    
    # perform SVD
    U, S, V = np.linalg.svd(cov)
    
    return U, S, V


U, S, V = pca(X)
U, S, V


# 现在我们有主成分（矩阵U），我们可以用这些来将原始数据投影到一个较低维的空间中。 对于这个任务，我们将实现一个计算投影并且仅选择顶部K个分量的函数，有效地减少了维数。

def project_data(X, U, k):
    U_reduced = U[:,:k]
    return np.dot(X, U_reduced)


Z = project_data(X, U, 1)
Z


# 我们也可以通过反向转换步骤来恢复原始数据。

def recover_data(Z, U, k):
    U_reduced = U[:,:k]
    return np.dot(Z, U_reduced.T)


X_recovered = recover_data(Z, U, 1)
X_recovered

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(list(X_recovered[:, 0]), list(X_recovered[:, 1]))
plt.show()

# 请注意，第一主成分的投影轴基本上是数据集中的对角线。 当我们将数据减少到一个维度时，我们失去了该对角线周围的变化，所以在我们的再现中，一切都沿着该对角线。

# ----
#
# 本文代码更新地址：https://github.com/fengdu78/lihang-code
#
# 中文注释制作：机器学习初学者公众号：ID:ai-start-com
#
# 配置环境：python 3.5+
#
# 代码全部测试通过。
# ![gongzhong](../gongzhong.jpg)

StarcoderdataJupyterScriptsDedupFiltered