主成分分析(PCA)
作用
主成分分析将多个有一定相关性的指标进行线性组合,以最少的维度解释原数据中尽可能多的信息为目标进行降维,降维后的各变量间彼此线性无关,最终确定的新变量是原始变量的线性组合,且越往后主成分在方差中的比重也小,综合原信息的能力越弱,与因子分析不同的是,因子分析是利用少数几个公共因子去解释较多个要观测变量中存在的关系,它不是对原始变量的重新组合。
使用范围
- 处理高维数据:数据集通常包含大量特征,甚至超过样本数量,直接在高维空间中建模可能导致过拟合和计算复杂性增加。PCA 通过提取主要特征(主成分)降低数据维度,简化问题复杂度,使建模和分析更容易。
- 消除冗余特征:特征之间往往存在较高相关性,冗余特征可能影响模型性能。PCA 构造新的、不相关的主成分,帮助提高模型的泛化能力和表现。
- 去除噪声:PCA 能够识别并去除数据中的噪声成分,尤其是在数据包含噪声或异常值时。通过提取主要信号,PCA 有助于提高模型的准确性和稳定性。
基本过程
假设有
n
n
n个样本,
p
p
p个指标,则可构成大小为
n
×
p
n\times p
n×p的样本矩阵
x
x
x:
x
=
[
x
11
x
12
⋯
x
1
p
x
21
x
22
⋯
x
2
p
⋮
⋮
⋱
⋮
x
n
1
x
n
2
⋯
x
n
p
]
=
(
x
1
,
x
2
,
⋯
,
x
p
)
x=\begin{bmatrix}x_{11}&x_{12}&\cdots&x_{1p}\\\\x_{21}&x_{22}&\cdots&x_{2p}\\\vdots&\vdots&\ddots&\vdots\\\\x_{n1}&x_{n2}&\cdots&x_{np}\end{bmatrix}=(x_{1},x_{2},\cdots,x_{p})
x=
x11x21⋮xn1x12x22⋮xn2⋯⋯⋱⋯x1px2p⋮xnp
=(x1,x2,⋯,xp)
数据的标准化处理
目的:标准化处理使得每个特征具有相同的尺度和单位,避免特征值较大或较小的变量对主成分的影响。PCA 对特征的尺度敏感,因此标准化是必要的步骤。
计算均值:计算每个特征的均值。对于特征
x
i
x_i
xi,其均值
μ
i
\mu_i
μi计算公式为:
μ
i
=
1
n
∑
j
=
1
n
x
i
j
\mu_i=\frac1n\sum_{j=1}^nx_{ij}
μi=n1j=1∑nxij
其中,
n
n
n是样本数量,
x
i
j
x_{ij}
xij是第
j
j
j个样本在第
i
i
i个特征上的值。
计算标准差:计算每个特征的标准差
σ
i
\sigma_i
σi:
σ
i
=
1
n
−
1
∑
j
=
1
n
(
X
i
j
−
μ
i
)
2
\sigma_i=\sqrt{\frac1{n-1}\sum_{j=1}^n(X_{ij}-\mu_i)^2}
σi=n−11j=1∑n(Xij−μi)2
标准化数据:对每个特征进行标准化,得到标准化后的数据
X
i
j
X_{ij}
Xij:
X
i
j
=
x
i
j
−
μ
i
σ
i
X_{ij}=\frac{x_{ij}-\mu_i}{\sigma_i}
Xij=σixij−μi
原始样本矩阵经过标准化变为:
X
=
[
X
11
X
12
⋯
X
1
p
X
21
X
22
⋯
X
2
p
⋮
⋮
⋱
⋮
X
n
1
X
n
2
⋯
X
n
p
]
=
(
X
1
,
X
2
,
⋯
,
X
p
)
X=\begin{bmatrix}X_{11}&X_{12}&\cdots&X_{1p}\\X_{21}&X_{22}&\cdots&X_{2p}\\\vdots&\vdots&\ddots&\vdots\\X_{n1}&X_{n2}&\cdots&X_{np}\end{bmatrix}=(X_1,X_2,\cdots,X_p)
X=
X11X21⋮Xn1X12X22⋮Xn2⋯⋯⋱⋯X1pX2p⋮Xnp
=(X1,X2,⋯,Xp)
计算标准化样本的协方差矩阵
目的:协方差矩阵描述了数据集中各特征之间的线性关系,PCA 通过分析协方差矩阵来提取主要特征(主成分)。
R
=
[
r
11
r
12
⋯
r
1
p
r
21
r
22
⋯
r
2
p
⋮
⋮
⋱
⋮
r
p
1
r
p
2
⋯
r
p
p
]
R=\begin{bmatrix}r_{11}&r_{12}&\cdots&r_{1p}\\r_{21}&r_{22}&\cdots&r_{2p}\\\vdots&\vdots&\ddots&\vdots\\r_{p1}&r_{p2}&\cdots&r_{pp}\end{bmatrix}
R=
r11r21⋮rp1r12r22⋮rp2⋯⋯⋱⋯r1pr2p⋮rpp
其中
r
i
j
=
1
n
−
1
∑
k
=
1
n
(
X
k
i
−
X
‾
i
)
(
X
k
j
−
X
‾
j
)
=
1
n
−
1
∑
k
=
1
n
X
k
i
X
k
j
r_{ij}={\frac{1}{n-1}}\sum_{k=1}^{n}(X_{ki}-{\overline{X}}_{i})(X_{kj}-{\overline{X}}_{j})={\frac{1}{n-1}}\sum_{k=1}^{n}X_{ki}X_{kj}
rij=n−11k=1∑n(Xki−Xi)(Xkj−Xj)=n−11k=1∑nXkiXkj
直接计算
X
X
X矩阵的样本相关系数矩阵:
R
=
∑
k
=
1
n
(
x
k
i
−
x
i
‾
)
(
x
k
j
−
x
j
‾
)
∑
k
=
1
n
(
x
k
i
−
x
i
‾
)
2
∑
k
=
1
n
(
x
k
j
−
x
j
‾
)
2
R=\frac{\sum_{k=1}^{n}(x_{ki}-\overline{x_{i}})(x_{kj}-\overline{x_{j}})}{\sqrt{\sum_{k=1}^{n}(x_{ki}-\overline{x_{i}})^{2}\sum_{k=1}^{n}(x_{kj}-\overline{x_{j}})^{2}}}
R=∑k=1n(xki−xi)2∑k=1n(xkj−xj)2∑k=1n(xki−xi)(xkj−xj)
计算 R R R的特征值和特征向量
特征值的计算
对于一个半正定矩阵 RRR,其特征值 $\lambda_1, \lambda_2, \dots, \lambda_p $满足以下条件:
λ
1
≥
λ
2
≥
⋯
≥
λ
p
≥
0
\lambda_1\geq\lambda_2\geq\cdots\geq\lambda_p\geq0
λ1≥λ2≥⋯≥λp≥0
半正定矩阵意味着所有特征值都是非负的。此外,矩阵 RRR 的迹(矩阵对角线上元素的和)等于所有特征值的总和:
tr
(
R
)
=
∑
k
=
1
p
λ
k
=
p
\operatorname{tr}(R)=\sum_{k=1}^p\lambda_k=p
tr(R)=k=1∑pλk=p
特征向量的计算
特征向量是与特征值相对应的非零向量,它满足以下条件:
R
a
k
=
λ
k
a
k
(
k
=
1
,
2
,
…
,
p
)
Ra_k=\lambda_ka_k\quad(k=1,2,\ldots,p)
Rak=λkak(k=1,2,…,p)
计算出特征值后,我们可以使用这些特征值计算与之对应的特征向量
a
k
a_k
ak:
a
1
=
[
a
11
a
21
⋮
a
p
1
]
,
a
2
=
[
a
12
a
22
⋮
a
p
2
]
,
⋯
,
a
p
=
[
a
1
p
a
2
p
⋮
a
p
p
]
a_1=\begin{bmatrix}a_{11}\\a_{21}\\\vdots\\a_{p1}\end{bmatrix}, a_2=\begin{bmatrix}a_{12}\\a_{22}\\\vdots\\a_{p2}\end{bmatrix}, \cdots, a_p=\begin{bmatrix}a_{1p}\\a_{2p}\\\vdots\\a_{pp}\end{bmatrix}
a1=
a11a21⋮ap1
,a2=
a12a22⋮ap2
,⋯,ap=
a1pa2p⋮app
通常不需要手动计算这些特征值和特征向量,可以使用Python 中的 numpy
或 scipy
等来计算。
计算主成分贡献率以及累计贡献率
主成分贡献率的计算
主成分贡献率表示每个主成分解释的总方差的比例。第
i
i
i个主成分的贡献率可以通过以下公式计算:
贡献率
i
=
λ
i
∑
k
=
1
p
λ
k
(
i
=
1
,
2
,
…
,
p
)
\text{贡献率}_i=\frac{\lambda_i}{\sum_{k=1}^p\lambda_k}\quad(i=1,2,\ldots,p)
贡献率i=∑k=1pλkλi(i=1,2,…,p)
这里,
λ
i
\lambda_i
λi是第
i
i
i个特征值,
∑
k
=
1
p
λ
k
\sum_{k=1}^p\lambda_k
∑k=1pλk是所有特征值的和。
累计贡献率的计算
累计贡献率表示前
i
i
i个主成分解释的总方差的比例,是前 iii 个主成分贡献率的累加和:
累计贡献率
i
=
∑
k
=
1
i
λ
k
∑
k
=
1
p
λ
k
(
i
=
1
,
2
,
…
,
p
)
\text{累计贡献率}_i=\frac{\sum_{k=1}^i\lambda_k}{\sum_{k=1}^p\lambda_k}\quad(i=1,2,\ldots,p)
累计贡献率i=∑k=1pλk∑k=1iλk(i=1,2,…,p)
累计贡献率通常用于判断需要保留多少个主成分,以便解释大部分的数据变异性。在实际操作中,当累计贡献率达到 85%-95% 时,可以考虑只保留前几个主成分。
写出主成分
一般累计贡献率超过80%的特征值对应的第一、第二、…、第
(
m
⩽
p
)
(m\leqslant p)
(m⩽p)个主成分。第
i
i
i个主成分:
F
i
=
a
1
i
X
1
+
a
2
i
X
2
+
⋯
+
a
p
i
X
p
(
i
=
1
,
2
,
⋯
,
m
)
F_{i}=a_{1i}X_{1}+a_{2i}X_{2}+\cdots+a_{pi}X_{p}\quad(i=1,2,\cdots,m)
Fi=a1iX1+a2iX2+⋯+apiXp(i=1,2,⋯,m)
根据系数分析主成分代表的意义
对于某个主成分而言,指标前面的系数越大,代表该指标对于该主成分的影响越大。
利用主成分的结果进行后续的分析
基于降维后的数据进行建模和分析,例如回归分析、聚类分析等。降维后的数据在一定程度上保留了原始数据的主要信息,同时减少了计算复杂性和过拟合风险。
主成分得分不可以用于评价类模型
主成分分析法的具体应用
这是一组涵盖各省份多个指标的数据集,包括食品、衣着、家庭设备、医疗、交通、娱乐、居住和杂项等。为了简化后续的分析过程,我们将使用主成分分析法对这些指标进行降维,从而提取出最能代表原始数据的信息,减少数据维度,同时保留关键特征。
数据准备
导入所需的包
import pandas as pd
import numpy as np
import scipy
from sklearn import preprocessing
import matplotlib.pyplot as plt
import seaborn as sns
np.set_printoptions(suppress=True) # 不使用用科学计数法
读取数据
data = pd.read_excel('cost_data.xlsx', index_col=0)
数据标准化
使用Z-score 标准化,对数据集 data
中的每一个特征
x
j
x_j
xj进行标准化,将其转化为均值为 0,标准差为 1 的新特征
z
j
z_j
zj,具体公式为:
z
j
=
x
j
−
μ
j
σ
j
z_j=\frac{x_j-\mu_j}{\sigma_j}
zj=σjxj−μj
其中:
-
x j x_j xj是原始数据集中第 j j j个特征的值。
-
μ j \mu_j μj是第 j j j个特征的平均值:
μ j = 1 n ∑ i = 1 n x i j \mu_j=\frac1n\sum_{i=1}^nx_{ij} μj=n1i=1∑nxij
其中 x i j x_{ij} xij表示第 i i i个样本的第 j j j个特征值, n n n是样本的数量。
- σ j \sigma_j σj是第 j j j个特征的标准差:
σ j = 1 n ∑ i = 1 n ( x i j − μ j ) 2 \sigma_j=\sqrt{\frac1n\sum_{i=1}^n\left(x_{ij}-\mu_j\right)^2} σj=n1i=1∑n(xij−μj)2
# 标准化
scale_data = preprocessing.scale(data)
计算特征向量和特征值
计算标准化数据的协方差矩阵
C
C
C:
C
=
1
n
−
1
Z
T
Z
\mathbf{C}=\frac1{n-1}\mathbf{Z}^T\mathbf{Z}
C=n−11ZTZ
其中:
- Z Z Z是标准化后的数据矩阵(每列为一个特征,每行为一个样本)
- n n n是样本数量
对协方差矩阵
C
C
C进行特征值分解,得到特征值
λ
i
\lambda_{i}
λi和对应的特征向量
V
i
V_i
Vi:
C
v
i
=
λ
i
V
i
\mathbf{C}\mathbf{v}_i=\lambda_i\mathbf{V}_i
Cvi=λiVi
其中:
- λ i 是第 \lambda_{i}是第 λi是第 i i i个主成分的特征值
- V i V_i Vi是对应的特征向量(即主成分)
# 应用PCA
pca = PCA() # 不指定 n_components 以获取所有主成分
principal_components = pca.fit_transform(scaled_data)
# 创建 DataFrame 包含主成分
pc_df = pd.DataFrame(data=principal_components, index=df.index)
# 获取特征值(各主成分的方差)
explained_variance = pca.explained_variance_
explained_variance
计算方差贡献率和累计贡献率
每个主成分的方差贡献率计算公式为:
方差贡献率
i
=
λ
i
∑
j
=
1
k
λ
j
\text{方差贡献率}_i=\frac{\lambda_i}{\sum_{j=1}^k\lambda_j}
方差贡献率i=∑j=1kλjλi
其中:
- λ i \lambda_{i} λi是第 i i i个主成分的特征值
- k k k是主成分的总数
累计贡献率是前
i
i
i 个主成分的方差贡献率之和:
累计贡献率
i
=
∑
j
=
1
i
方差贡献率
j
\text{累计贡献率}_i=\sum_{j=1}^i\text{方差贡献率}_j
累计贡献率i=j=1∑i方差贡献率j
# 计算方差贡献率
explained_variance_ratio = pca.explained_variance_ratio_
# 计算累计贡献率
cumulative_explained_variance_ratio = explained_variance_ratio.cumsum()
# 创建 DataFrame 包含方差、方差贡献率和累计贡献率
results_df = pd.DataFrame({
'特征值': explained_variance,
'方差贡献率': explained_variance_ratio,
'累计贡献率': cumulative_explained_variance_ratio
})
# 输出结果
print("主成分特征值、方差贡献率及累计贡献率:")
print(results_df)
得出的结果如下表所示
主成分 | 特征值 | 方差贡献率 | 累计贡献率 |
---|---|---|---|
1 | 5.2676 | 0.6372 | 0.6372 |
2 | 1.3973 | 0.1690 | 0.8062 |
3 | 0.5939 | 0.0718 | 0.8781 |
4 | 0.4198 | 0.0508 | 0.9289 |
5 | 0.2906 | 0.0352 | 0.9640 |
6 | 0.1264 | 0.0153 | 0.9793 |
7 | 0.0958 | 0.0116 | 0.9909 |
8 | 0.0752 | 0.0091 | 1.0000 |
选择能够解释大部分方差的主成分。通常,选择累计贡献率达到某个阈值的主成分(如 80% 或 90%)作为主要主成分。
根据上表可知主成分1、2、3的累计贡献率到达率87%,所以选择这三个主成分。
计算主成分的载荷矩阵
载荷矩阵的每个元素表示原始特征在每个主成分中的权重。载荷矩阵
L
\mathbf{L}
L可以通过将主成分特征向量
v
i
\mathbf{v}_i
vi 乘以特征值
λ
i
\lambda_i
λi 的平方根来计算。
L
i
j
=
v
i
j
×
λ
i
\mathbf{L}_{ij}=\mathbf{v}_{ij}\times\sqrt{\lambda_i}
Lij=vij×λi
其中:
- L i j \mathbf{L}_{ij} Lij是载荷矩阵中第 i i i行、第 j j j列的元素。
- v i j \mathbf{v}_{ij} vij是主成分 i i i对应的特征向量在第 j j j个特征上的值。
- λ i \sqrt{\lambda_i} λi是特征值 λ i \lambda_i λi的平方根。
# 创建 DataFrame
df = pd.DataFrame(data)
# 标准化数据
scaler = StandardScaler()
scaled_data = scaler.fit_transform(df)
# 应用 PCA
pca = PCA() # 不指定 n_components 以获取所有主成分
principal_components = pca.fit_transform(scaled_data)
# 选择前三个主成分
n_components = 3
components = pca.components_[:n_components]
explained_variance = pca.explained_variance_[:n_components]
eigenvalues_sqrt = np.sqrt(explained_variance)
# 计算前三个主成分的载荷矩阵
loadings = components.T * eigenvalues_sqrt
# 创建 DataFrame 包含载荷矩阵
loadings_df = pd.DataFrame(loadings, columns=[f'主成分{i+1}' for i in range(n_components)], index=df.columns)
print("\n前三个主成分的载荷矩阵:")
print(loadings_df)
输出结果如下表所示
特征 | 主成分1 | 主成分2 | 主成分3 |
---|---|---|---|
食品 | 0.920446 | 0.091254 | 0.319862 |
衣着 | 0.303033 | -0.885615 | 0.255689 |
家庭设备 | 0.860957 | -0.076922 | -0.340396 |
医疗 | 0.734424 | -0.407452 | -0.368153 |
交通 | 0.890062 | 0.273958 | 0.215109 |
娱乐 | 0.931430 | -0.032099 | -0.238763 |
居住 | 0.748885 | 0.586351 | -0.026151 |
杂项 | 0.909586 | -0.113465 | 0.266125 |
- 第一主成分的高载荷特征主要集中在食品、家庭设备、娱乐、交通和杂项上,这些特征可能与生活质量、消费水平或生活方式相关。因此,主成分1可能表示一种综合生活支出水平或消费模式,涵盖了家庭主要的支出项目。
- 第二主成分的高载荷特征主要是衣着、居住和医疗,这些特征可能与个人生活的基础需求有关。负载荷的衣着和医疗表明该主成分可能代表一种生活基础设施支出,即与个人生活的基本支出相关的主成分。
- 第三主成分的载荷在多个特征上具有一定的权重,但没有非常高或低的负荷,这可能反映了支出模式的多样性或经济活动的综合效应,这可能在不同经济条件下对多种支出项产生不同的影响。
模型的检验
使用主成分来重建原始数据,比较重建数据与原始数据的差异,计算原始数据与重建数据之间的均方误差。
-
计算重建误差:
误差是原始数据与重建数据之间的差异,计算公式为:
误差 i j = X i j − X ^ i j \text{误差}_{ij}=X_{ij}-\hat{X}_{ij} 误差ij=Xij−X^ij
其中, X i j X_{ij} Xij是原始数据的第 i i i行第 j j j 列的值, X ^ i j \hat{X}_{ij} X^ij 是重建数据的第 i i i行第 j j j 列的值。 -
计算均方误差(MSE):
MSE 是所有误差的平方和的均值,计算公式为:
M S E = 1 n ⋅ m ∑ i = 1 n ∑ j = 1 m ( 误差 i j ) 2 \mathrm{MSE}=\frac1{n\cdot m}\sum_{i=1}^n\sum_{j=1}^m(\text{误差}_{ij})^2 MSE=n⋅m1i=1∑nj=1∑m(误差ij)2
其中, n n n 是数据的行数(样本数), m m m 是数据的列数(特征数)。
# 重建数据并计算误差
reconstructed_data = pca.inverse_transform(principal_components)
mse = mean_squared_error(scaled_data, reconstructed_data)
print(f"\n重建数据与原始数据的均方误差:{mse}")
重建数据与原始数据的均方误差(MSE)为 8.38e-31
,这是一个非常小的值,接近于零,表明主成分分析模型在重建数据时几乎没有损失信息,模型性能良好。