4、PCA实战(Python):使用sklearn进行PCA降维、explained_variance_ratio_的解读、二维可视化散点图

好,咱们直接进入正题。

前面聊了那么多PCA的理论,什么协方差矩阵、特征向量、投影空间……说实话,光看公式容易晕。我当年学的时候也是,推导了一遍觉得懂了,一上手写代码还是懵。

所以这一节,咱们直接撸代码。用Python的sklearn库,把PCA跑一遍。你会发现,其实核心代码就那么几行。

4.1 准备工作:导入库与数据集

我个人习惯,做任何数据分析之前,先把环境理清楚。这里我们用经典的鸢尾花(Iris)数据集。它只有4个特征,非常适合用来演示降维。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# 加载数据
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names

print("原始数据形状:", X.shape)
print("特征名称:", feature_names)

输出结果:

原始数据形状: (150, 4)
特征名称: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

嗯,150个样本,4个特征。目标是把这4维降到2维,方便可视化。

注意:PCA对特征的尺度非常敏感。如果特征的量纲不同(比如一个特征范围是0-1,另一个是0-1000),PCA会偏向方差大的特征。所以,一定要先做标准化。我曾经在这个坑里摔过一次,降维后的结果完全没法解释。

4.2 数据标准化

标准化这一步,说白了就是把每个特征变成均值为0、标准差为1的分布。用StandardScaler搞定。

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

print("标准化后数据均值:", np.mean(X_scaled, axis=0).round(6))
print("标准化后数据标准差:", np.std(X_scaled, axis=0).round(6))

输出:

标准化后数据均值: [ 0. -0.  0.  0.]
标准化后数据标准差: [1. 1. 1. 1.]

你看,均值接近0,标准差为1。完美。

4.3 执行PCA降维

核心代码来了。我们指定n_components=2,把4维降到2维。

pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

print("降维后数据形状:", X_pca.shape)

输出:

降维后数据形状: (150, 2)

就这么简单。现在每个样本只有2个特征了,可以直接画散点图。

4.4 explained_variance_ratio_ 的解读

这里有个非常重要的属性——explained_variance_ratio_。它告诉你每个主成分“解释”了多少原始数据的方差。

print("各主成分解释方差比例:", pca.explained_variance_ratio_)
print("累计解释方差比例:", np.sum(pca.explained_variance_ratio_))

输出:

各主成分解释方差比例: [0.72962445 0.22850762]
累计解释方差比例: 0.95813207

什么意思?

  • 第一个主成分(PC1)解释了约73%的方差
  • 第二个主成分(PC2)解释了约23%的方差
  • 两者加起来,保留了原始数据约96%的信息

你想想看,从4维降到2维,信息损失只有4%左右。这就是PCA的厉害之处。

核心解读:

explained_variance_ratio_ 的值越大,说明该主成分越重要。如果前几个主成分的累计比例超过85%,通常就可以认为降维效果不错。

我在项目中遇到过,有时候前两个主成分只能解释60%的方差,那就得考虑保留3个或4个主成分了。别死磕2维。

4.5 二维可视化散点图

好,数据降完了,咱们画个图看看效果。

plt.figure(figsize=(10, 6))
colors = ['navy', 'turquoise', 'darkorange']
target_names = iris.target_names

for color, i, target_name in zip(colors, [0, 1, 2], target_names):
    plt.scatter(X_pca[y == i, 0], X_pca[y == i, 1], 
                color=color, alpha=0.8, lw=2,
                label=target_name)

plt.xlabel('第一主成分 (PC1) - 解释方差 {:.2f}%'.format(pca.explained_variance_ratio_[0]*100))
plt.ylabel('第二主成分 (PC2) - 解释方差 {:.2f}%'.format(pca.explained_variance_ratio_[1]*100))
plt.title('PCA降维后的鸢尾花数据二维可视化')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

画出来的图,三个类别在二维平面上分得清清楚楚。setosa那一类完全独立,versicolor和virginica虽然有点重叠,但整体边界还是很明显的。

小技巧:

如果你发现降维后的图分类效果不好,别急着怀疑PCA。先检查一下标准化有没有做,再试试保留更多主成分。我习惯先画一个explained_variance_ratio_的累积曲线图,看看拐点在哪里。

4.6 完整代码汇总

为了方便你直接复制运行,我把完整代码贴出来:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# 1. 加载数据
iris = load_iris()
X = iris.data
y = iris.target

# 2. 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 3. PCA降维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# 4. 输出解释方差
print("解释方差比例:", pca.explained_variance_ratio_)
print("累计解释方差:", np.sum(pca.explained_variance_ratio_))

# 5. 可视化
plt.figure(figsize=(10, 6))
colors = ['navy', 'turquoise', 'darkorange']
target_names = iris.target_names

for color, i, target_name in zip(colors, [0, 1, 2], target_names):
    plt.scatter(X_pca[y == i, 0], X_pca[y == i, 1], 
                color=color, alpha=0.8, lw=2,
                label=target_name)

plt.xlabel('PC1 ({:.2f}%)'.format(pca.explained_variance_ratio_[0]*100))
plt.ylabel('PC2 ({:.2f}%)'.format(pca.explained_variance_ratio_[1]*100))
plt.title('PCA降维可视化')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

4.7 避坑指南

最后,分享几个我踩过的坑:

  • 忘记标准化:这是最常见的错误。不标准化的话,PCA会优先保留量纲大的特征,结果往往很离谱。
  • 只看累计方差:累计方差高不代表降维效果好。有时候90%的方差被第一个主成分占了,剩下10%里可能藏着关键信息。我建议至少保留2-3个主成分看看。
  • 过度解读主成分:降维后的坐标轴(PC1、PC2)是原始特征的线性组合,很难直接解释成某个具体特征。别硬解释。

嗯,这一节就到这儿。代码不多,但每一步都很关键。你动手跑一遍,感受一下从4维到2维的变化,比看十遍理论都管用。