在数据分析和机器学习领域,K-Means聚类是最经典的无监督分类算法,而PCA(主成分分析)则是高维数据降维的核心手段。将两者结合,既能解决高维数据聚类的“维度灾难”问题,又能提升聚类效率和可解释性。本文从原理拆解、代码实战到场景优化,全方位讲解K-Means聚类与PCA降维的使用方法,适合数据分析初学者和算法应用开发者学习。
一、核心概念:先搞懂K-Means与PCA的底层逻辑
1. K-Means聚类:无监督的“物以类聚”
K-Means是基于距离的聚类算法,核心目标是将数据划分为K个簇,使得同一簇内数据点距离尽可能近,不同簇间距离尽可能远。
核心步骤:
- 初始化:随机选择K个数据点作为初始聚类中心;
- 分配簇:计算每个数据点到K个中心的欧氏距离,将其分配到距离最近的簇;
- 更新中心:重新计算每个簇的均值,作为新的聚类中心;
- 迭代收敛:重复“分配簇-更新中心”,直到聚类中心不再变化或达到迭代次数上限。
关键特点:
- 优点:简单高效、易于实现,适合处理大规模数据;
- 缺点:对初始聚类中心敏感,需提前指定K值,对异常值和非球形簇效果差。
2. PCA降维:高维数据的“瘦身术”
PCA通过线性变换,将高维数据映射到低维空间,同时保留数据的主要信息(方差最大的方向)。比如将100维的特征压缩到2-3维,既简化计算,又能可视化数据分布。
核心步骤:
- 数据标准化:将各特征缩放到同一尺度(避免量纲影响);
- 计算协方差矩阵:反映特征间的线性相关程度;
- 求解特征值和特征向量:特征值越大,对应特征向量方向的方差越大;
- 选择主成分:选取前N个最大特征值对应的特征向量,构建投影矩阵;
- 数据投影:将原始数据乘以投影矩阵,得到降维后的数据。
关键特点:
- 优点:无监督、无参数限制,能有效降低数据维度;
- 缺点:解释性弱(主成分是原始特征的线性组合),对非线性数据降维效果差。
二、前置准备:环境搭建与数据准备
1. 安装必备库
本文使用numpy处理数值计算、pandas加载数据、sklearn实现算法、matplotlib可视化结果,安装命令:
# 一键安装
pip install numpy pandas scikit-learn matplotlib
# 指定版本(推荐稳定版)
pip install numpy==1.24.3 pandas==2.0.3 scikit-learn==1.2.2 matplotlib==3.7.1
2. 准备测试数据
我们使用sklearn内置的鸢尾花(Iris)数据集(4维特征,3类样本),既适合演示PCA降维,也能验证K-Means聚类效果:
import pandas as pd
from sklearn.datasets import load_iris
# 加载数据集
iris = load_iris()
# 转为DataFrame,方便查看
df = pd.DataFrame(
data=iris.data,
columns=['花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度']
)
# 添加真实标签(用于后续验证聚类效果)
df['真实标签'] = iris.target
print("数据集前5行:")
print(df.head())
print(f"数据集形状:{df.shape}") # (150, 5),150个样本,4个特征+1个标签
三、实战1:PCA降维实现(从4维到2维)
1. 完整PCA降维代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# 步骤1:提取特征数据并标准化
X = df[['花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度']].values
# 标准化(PCA对量纲敏感,必须做)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 步骤2:构建PCA模型,降维到2维
pca = PCA(n_components=2) # 指定降维后的维度
X_pca = pca.fit_transform(X_scaled)
# 步骤3:查看降维结果
print(f"原始数据维度:{X.shape}")
print(f"降维后数据维度:{X_pca.shape}") # (150, 2)
# 步骤4:查看主成分解释方差(评估降维效果)
explained_variance = pca.explained_variance_ratio_
print(f"各主成分解释方差占比:{explained_variance}")
print(f"累计解释方差占比:{np.sum(explained_variance):.2f}") # 鸢尾花数据约97%,说明2维保留了绝大部分信息
# 步骤5:可视化降维后的数据
plt.figure(figsize=(8, 6))
# 按真实标签着色
colors = ['red', 'green', 'blue']
labels = ['山鸢尾', '变色鸢尾', '维吉尼亚鸢尾']
for i in range(3):
plt.scatter(
X_pca[df['真实标签']==i, 0], # 第一主成分
X_pca[df['真实标签']==i, 1], # 第二主成分
c=colors[i],
label=labels[i],
alpha=0.8
)
plt.xlabel('第一主成分')
plt.ylabel('第二主成分')
plt.title('PCA降维后鸢尾花数据分布')
plt.legend()
plt.show()
2. 关键参数说明
n_components:可指定降维后的维度(如2),也可指定解释方差占比(如0.95,表示保留95%的信息):# 保留95%的信息,自动确定维度 pca = PCA(n_components=0.95) X_pca = pca.fit_transform(X_scaled) print(f"自动选择的维度数:{pca.n_components_}")explained_variance_ratio_:每个主成分解释的方差占比,累计值越高,降维保留的信息越多。
四、实战2:K-Means聚类实现(基于降维后数据)
1. 完整K-Means聚类代码
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
# 步骤1:使用PCA降维后的数据进行聚类
# 确定K值:鸢尾花数据真实类别数为3,这里指定K=3
kmeans = KMeans(
n_clusters=3, # 聚类数
init='k-means++', # 优化初始中心选择,避免随机初始化的弊端
max_iter=300, # 最大迭代次数
random_state=42 # 固定随机种子,保证结果可复现
)
# 训练模型并预测聚类标签
y_pred = kmeans.fit_predict(X_pca)
# 步骤2:评估聚类效果
# 1. 轮廓系数(范围[-1,1],越接近1聚类效果越好)
sil_score = silhouette_score(X_pca, y_pred)
print(f"聚类轮廓系数:{sil_score:.2f}") # 鸢尾花数据约0.55,聚类效果较好
# 2. 查看聚类中心
print("聚类中心(降维后空间):")
print(kmeans.cluster_centers_)
# 步骤3:可视化聚类结果
plt.figure(figsize=(8, 6))
# 按聚类标签着色
for i in range(3):
plt.scatter(
X_pca[y_pred==i, 0],
X_pca[y_pred==i, 1],
c=colors[i],
label=f'聚类{i+1}',
alpha=0.8
)
# 绘制聚类中心
plt.scatter(
kmeans.cluster_centers_[:, 0],
kmeans.cluster_centers_[:, 1],
c='black',
marker='*',
s=200, # 大小
label='聚类中心'
)
plt.xlabel('第一主成分')
plt.ylabel('第二主成分')
plt.title('K-Means聚类结果(PCA降维后)')
plt.legend()
plt.show()
2. 如何选择最优K值?
实际场景中无法提前知道K值,常用“肘部法则”和轮廓系数结合选择:
# 肘部法则:计算不同K值的平方和误差(SSE)
sse = []
sil_scores = []
k_range = range(2, 10) # 测试K=2到9
for k in k_range:
kmeans_temp = KMeans(n_clusters=k, init='k-means++', random_state=42)
kmeans_temp.fit(X_pca)
sse.append(kmeans_temp.inertia_) # 平方和误差
sil_scores.append(silhouette_score(X_pca, kmeans_temp.labels_))
# 可视化肘部法则曲线
plt.figure(figsize=(12, 5))
# 子图1:SSE曲线
plt.subplot(1, 2, 1)
plt.plot(k_range, sse, 'o-')
plt.xlabel('K值')
plt.ylabel('平方和误差(SSE)')
plt.title('肘部法则选择最优K值')
plt.axvline(x=3, color='red', linestyle='--') # 肘部位置
# 子图2:轮廓系数曲线
plt.subplot(1, 2, 2)
plt.plot(k_range, sil_scores, 'o-', color='green')
plt.xlabel('K值')
plt.ylabel('轮廓系数')
plt.title('轮廓系数选择最优K值')
plt.axvline(x=3, color='red', linestyle='--')
plt.show()
- 肘部法则:SSE随K增大逐渐下降,下降速度骤减的点即为最优K(鸢尾花数据K=3);
- 轮廓系数:选择轮廓系数最大的K值。
五、实战3:完整流程(原始数据→PCA降维→K-Means聚类)
将上述步骤整合,形成可复用的完整代码:
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
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
# 1. 加载并预处理数据
def load_and_preprocess_data():
iris = load_iris()
X = iris.data
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
return X_scaled, iris.target
# 2. PCA降维
def pca_dimension_reduction(X_scaled, n_components=2):
pca = PCA(n_components=n_components, random_state=42)
X_pca = pca.fit_transform(X_scaled)
explained_var = np.sum(pca.explained_variance_ratio_)
print(f"PCA降维后累计解释方差占比:{explained_var:.2f}")
return X_pca
# 3. K-Means聚类
def kmeans_clustering(X_pca, n_clusters=3):
kmeans = KMeans(
n_clusters=n_clusters,
init='k-means++',
max_iter=300,
random_state=42
)
y_pred = kmeans.fit_predict(X_pca)
sil_score = silhouette_score(X_pca, y_pred)
print(f"K-Means聚类轮廓系数:{sil_score:.2f}")
return y_pred, kmeans.cluster_centers_
# 4. 可视化结果
def visualize_result(X_pca, y_pred, centers):
plt.figure(figsize=(8, 6))
colors = ['red', 'green', 'blue']
for i in range(3):
plt.scatter(
X_pca[y_pred==i, 0],
X_pca[y_pred==i, 1],
c=colors[i],
label=f'聚类{i+1}',
alpha=0.8
)
plt.scatter(
centers[:, 0],
centers[:, 1],
c='black',
marker='*',
s=200,
label='聚类中心'
)
plt.xlabel('第一主成分')
plt.ylabel('第二主成分')
plt.title('PCA+K-Means聚类结果')
plt.legend()
plt.show()
# 主函数
if __name__ == '__main__':
# 执行完整流程
X_scaled, true_labels = load_and_preprocess_data()
X_pca = pca_dimension_reduction(X_scaled, n_components=2)
y_pred, centers = kmeans_clustering(X_pca, n_clusters=3)
visualize_result(X_pca, y_pred, centers)
六、避坑指南:常见问题与解决方案
1. PCA降维后效果差?
- 原因:未做数据标准化,特征量纲差异大;
- 解决:必须先使用
StandardScaler或MinMaxScaler标准化数据。
2. K-Means聚类结果不稳定?
- 原因:初始聚类中心随机选择;
- 解决:使用
init='k-means++'(默认),或设置random_state固定种子,也可增加n_init参数(默认10,多次初始化选最优)。
3. 无法确定最优K值?
- 解决:结合肘部法则和轮廓系数,避免仅依赖单一指标;对于业务数据,可结合业务场景(如用户分群需几类)确定K值。
4. 聚类结果与真实标签不符?
- 说明:K-Means是无监督算法,聚类标签仅代表类别划分,不与真实标签一一对应;
- 验证:可使用
adjusted_rand_score评估聚类标签与真实标签的一致性:from sklearn.metrics import adjusted_rand_score ari = adjusted_rand_score(true_labels, y_pred) print(f"调整兰德指数(ARI):{ari:.2f}") # 越接近1,一致性越高
七、应用场景与优化建议
1. 典型应用场景
- 客户分群:将高维用户行为数据(浏览、购买、点击)经PCA降维后,用K-Means划分客户群体;
- 图像压缩:将图像像素数据降维后聚类,减少存储和计算成本;
- 异常检测:聚类后远离所有中心的样本,可判定为异常数据。
2. 优化建议
- 处理异常值:K-Means对异常值敏感,聚类前用IQR或Z-score剔除异常值;
- 非线性降维:若PCA效果差,可尝试t-SNE、UMAP等非线性降维算法;
- 并行计算:大规模数据时,使用
sklearn.cluster.MiniBatchKMeans替代K-Means,提升速度; - 模型融合:结合多个K值的聚类结果,提升稳定性。