news 2026/6/10 17:43:32

使用K-means算法对葡萄酒数据集进行聚类分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用K-means算法对葡萄酒数据集进行聚类分析

1.作者介绍

马一超,男,西安工程大学电子信息学院,2025级研究生

研究方向:模式识别与人工智能

电子邮件:2424102004@qq.com

2.K-Means算法介绍

2.1 算法原理

K-Means 是一种基于划分的聚类算法,其核心思想是通过迭代优化,将数据集划分为k个簇,使得簇内样本的相似度最大化,簇间样本的相似度最小化。算法通过不断更新簇中心(质心)和重新分配样本点来达到最优划分。

算法流程基于以下关键概念:

  1. 簇中心(Centroid):每个簇的代表点,通常为簇内所有样本的平均值。

  2. 簇分配(Assignment):将每个样本点分配到距离最近的簇中心。

  3. 簇更新(Update):根据当前簇内样本计算新的簇中心。

2.2 算法优缺点

优点:

  1. 简单易理解:算法直观,易于实现。

  2. 计算效率高:适合大规模数据集,尤其是样本维度不高时。

  3. 收敛速度快:通过迭代优化误差平方和(SSE)快速收敛。

  4. 结果可解释性强:每个簇都有明确的中心,便于理解和应用。

缺点:

  1. 需预设簇数k:簇数需要先确定,若选择不当会影响聚类效果。

  2. 对初始中心敏感:不同的初始质心可能导致不同的结果。

  3. 不适合非球形簇:算法假设簇为凸形或球形,无法处理复杂形状。

  4. 对离群点敏感:异常值会对簇中心计算产生较大影响。

  5. 高维数据问题:距离度量在高维空间可能失效,聚类效果下降。

2.3 算法实现流程

1.随机选择k个初始簇中心;

2.对每个样本点,计算其与k个簇中心的距离,并将其分配到距离最近的簇;

3.更新每个簇的中心为簇内所有样本的均值;

4.重复步骤 2-3,直到簇中心不再发生显著变化或达到迭代上限;

5.输出最终簇划分结果。

图 1 K-mean算法流程图

3.葡萄酒数据集分析

3.1数据集介绍

葡萄酒数据集是 sklearn 内置的经典多分类数据集,来源于 UCI Wine 数据集,主要用于根据葡萄酒的化学成分判断其所属类别。该数据集共有 178 个样本、13 个特征、3 个类别,每类样本数分别为 59、71、48。所有特征都是连续型、正数型数值特征

图 2葡萄酒数据集示例

3.2实验代码

# -*- coding:utf-8 -*- # Author: MYC # Date: 2026/5/28 # File: wine.py # Description: 葡萄酒聚类分析 """ 基于K-means聚类的葡萄酒数据集分析 功能: 1. 数据加载与基本信息查看 2. 缺失值检查 3. 标准化前后K-means聚类效果对比 4. k值选择(肘部法则、轮廓系数、CH指数、DB指数) 5. PCA降维可视化 6. 聚类结果评价 7. 聚类中心分析 8. 与层次聚类、DBSCAN、GMM进行比较 """ import numpy as np import pandas as pd from sklearn.datasets import load_wine from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN from sklearn.mixture import GaussianMixture from sklearn.metrics import ( silhouette_score, calinski_harabasz_score, davies_bouldin_score, adjusted_rand_score, normalized_mutual_info_score, confusion_matrix ) from scipy.optimize import linear_sum_assignment import matplotlib matplotlib.use("TkAgg") import matplotlib.pyplot as plt # 中文显示 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 聚类准确率函数 def clustering_accuracy(y_true, y_pred): y_true = np.asarray(y_true) # 将真实标签和预测标签转换为 NumPy 数组。 y_pred = np.asarray(y_pred) cm = confusion_matrix(y_true, y_pred) # 生成混淆矩阵 cm。 row_ind, col_ind = linear_sum_assignment(-cm) # 使用 linear_sum_assignment(-cm) 找到最大匹配。 matched_sum = cm[row_ind, col_ind].sum() return matched_sum / len(y_true) # 返回匹配样本数 / 总样本数,即准确率。 # 聚类评价函数 def evaluate_clustering(X, y_true, y_pred, method_name): unique_labels = np.unique(y_pred) # 获取预测聚类中唯一簇的数量。 if len(unique_labels) > 1 and len(unique_labels) < len(X): # 只有簇数量大于 1 且小于样本总数,才计算这些指标。否则返回 NaN。 silhouette = silhouette_score(X, y_pred) ch_score = calinski_harabasz_score(X, y_pred) db_score = davies_bouldin_score(X, y_pred) else: silhouette = np.nan ch_score = np.nan db_score = np.nan ari = adjusted_rand_score(y_true, y_pred) nmi = normalized_mutual_info_score(y_true, y_pred) # ARI、NMI 是无监督聚类的评价指标。 acc = clustering_accuracy(y_true, y_pred) # acc 使用前面定义的匈牙利匹配方法。 return { "方法": method_name, "簇数量": len(unique_labels), "轮廓系数": silhouette, "CH指数": ch_score, "DB指数": db_score, "ARI指数": ari, "NMI指数": nmi, "准确率": acc } # 加载数据 wine = load_wine() X = wine.data y = wine.target feature_names = wine.feature_names feature_dict = { 'alcohol': '酒精', 'malic_acid': '苹果酸', 'ash': '灰分', 'alcalinity_of_ash': '灰分碱度', 'magnesium': '镁', 'total_phenols': '总酚', 'flavanoids': '黄酮类化合物', 'nonflavanoid_phenols': '非黄酮酚', 'proanthocyanins': '原花青素', 'color_intensity': '颜色强度', 'hue': '色调', 'od280/od315_of_diluted_wines': 'OD280/OD315', 'proline': '脯氨酸' } feature_names_cn = [] for i in feature_names: result = feature_dict[i] feature_names_cn.append(result) df = pd.DataFrame(X, columns=feature_names_cn) df["类别"] = y print("=" * 60) print("葡萄酒数据集基本信息") print("=" * 60) print("样本数量:", X.shape[0]) print("特征数量:", X.shape[1]) print("\n特征名称:") print(feature_names_cn) print("\n前5行数据:") print(df.head()) print("\n缺失值检查:") print(df.isnull().sum()) # 数据标准化 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 对每个特征做均值 0 方差 1 的标准化 # 标准化前后比较 # 分别对未标准化和标准化数据进行 K-means 聚类,簇数设为 3。 kmeans_raw = KMeans(n_clusters=3, random_state=42, n_init=10) labels_raw = kmeans_raw.fit_predict(X) kmeans_scaled = KMeans(n_clusters=3, random_state=42, n_init=10) labels_scaled = kmeans_scaled.fit_predict(X_scaled) # 对比标准化前后聚类效果。 compare_standardization = pd.DataFrame([ evaluate_clustering(X, y, labels_raw, "未标准化K-means"), evaluate_clustering(X_scaled, y, labels_scaled, "标准化K-means") ]) print("\n标准化前后比较") print(compare_standardization) # k值选择 # 循环尝试不同 K 值。 # 记录 SSE(inertia)、轮廓系数、CH 指数、DB 指数,用于选择最佳 K。 k_values = range(2, 9) inertias = [] silhouette_scores = [] ch_scores = [] db_scores = [] for k in k_values: model = KMeans(n_clusters=k, random_state=42, n_init=10) labels = model.fit_predict(X_scaled) inertias.append(model.inertia_) silhouette_scores.append(silhouette_score(X_scaled, labels)) ch_scores.append(calinski_harabasz_score(X_scaled, labels)) db_scores.append(davies_bouldin_score(X_scaled, labels)) # 保存指标表 k_evaluation = pd.DataFrame({ "聚类数k": list(k_values), "误差平方和": inertias, "轮廓系数": silhouette_scores, "CH指数": ch_scores, "DB指数": db_scores }) print(k_evaluation) # 图1 肘部法则 plt.figure(figsize=(8, 5)) plt.plot(k_values, inertias, marker="o") plt.xlabel("聚类数k") plt.ylabel("误差平方和") plt.title("肘部法则选择最佳聚类数") plt.grid() plt.savefig( "01_肘部法则.png", dpi=300 ) plt.show() # 图2 轮廓系数 plt.figure(figsize=(8, 5)) plt.plot(k_values, silhouette_scores, marker="o") plt.xlabel("聚类数k") plt.ylabel("轮廓系数") plt.title("不同聚类数下的轮廓系数") plt.grid() plt.savefig("02_轮廓系数.png", dpi=300) plt.show() # 图3 CH指数 plt.figure(figsize=(8, 5)) plt.plot(k_values, ch_scores, marker="o") plt.xlabel("聚类数k") plt.ylabel("CH指数") plt.title("不同聚类数下CH指数") plt.grid() plt.savefig("03_CH指数.png", dpi=300) plt.show() # 图4 DB指数 plt.figure(figsize=(8, 5)) plt.plot(k_values, db_scores, marker="o") plt.xlabel("聚类数k") plt.ylabel("DB指数") plt.title("不同聚类数下DB指数") plt.grid() plt.savefig("04_DB指数.png", dpi=300) plt.show() # PCA降维 pca = PCA(n_components=2) X_pca = pca.fit_transform(X_scaled) print("\n累计解释方差:") print(pca.explained_variance_ratio_.sum()) # 真实类别 plt.figure(figsize=(8, 6)) plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y) plt.xlabel("主成分1") plt.ylabel("主成分2") plt.title("PCA降维后的真实类别") plt.colorbar(label="真实类别") plt.grid() plt.savefig("05_PCA真实类别.png", dpi=300) plt.show() # K-means聚类结果 plt.figure(figsize=(8, 6)) plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels_scaled) plt.xlabel("主成分1") plt.ylabel("主成分2") plt.title("PCA降维后的K-means聚类结果") plt.colorbar(label="聚类标签") plt.grid() plt.savefig("06_Kmeans聚类结果.png", dpi=300) plt.show() # 聚类中心分析 cluster_centers_original = scaler.inverse_transform(kmeans_scaled.cluster_centers_) # 将标准化后的聚类中心反变换回原始特征尺度 cluster_centers_df = pd.DataFrame(cluster_centers_original, columns=feature_names_cn) # 用 DataFrame 展示每个簇的中心特征值 print("\n聚类中心:") print(cluster_centers_df) # 算法对比 results = [] # 将多种聚类算法在同一数据集上对比。 models = [ ("K-means", KMeans(n_clusters=3, random_state=42, n_init=10)), ("层次聚类", AgglomerativeClustering(n_clusters=3)), ("DBSCAN", DBSCAN(eps=2.3, min_samples=5)), ("GMM", GaussianMixture(n_components=3, random_state=42)) ] for name, model in models: labels = model.fit_predict(X_scaled) results.append(evaluate_clustering(X_scaled, y, labels, name)) algorithm_compare = pd.DataFrame(results) print("\n不同聚类算法对比") print(algorithm_compare) # 保存结果 compare_standardization.to_csv("标准化较.csv", index=False, encoding="utf-8-sig") k_evaluation.to_csv("K值分析.csv", index=False, encoding="utf-8-sig") cluster_centers_df.to_csv("聚类中心.csv", index=False, encoding="utf-8-sig") algorithm_compare.to_csv("算法比较.csv", index=False, encoding="utf-8-sig") print("\n程序运行完成") print("结果文件已保存")

3.3实验结果

图 3 肘部法则选择最佳聚类数

从肘部法则图可以看出:

  • 当 k=2 → k=3 时,误差平方和(SSE)下降最明显;

  • 当 k>3 后,SSE 下降速度明显减缓;

  • 曲线在 k=3 处出现明显“拐点”。

因此:

肘部法则认为最佳聚类数为 k=3。

这说明将数据划分为 3 个簇后,簇内离散程度已经大幅降低,继续增加聚类数带来的收益有限。

图 4 不同聚类数下的轮廓系数

轮廓系数取值范围:

  • 越接近 1 表示聚类效果越好;

  • 接近 0 表示簇间重叠;

  • 小于 0 表示分类错误。

从图中可以看到:

k

Silhouette

2

0.259

3

0.285

4

0.260

5

0.201

6

0.237

7

0.203

8

0.157

最高值出现在:

因此:

轮廓系数同样认为 k=3 时聚类质量最好。

图 5 不同聚类数下CH指数分析图

CH 指数定义为:

其特点:

  • 越大越好;

  • 表示簇间距离远且簇内更加紧凑。

结果如下:

k

CH

2

69.6

3

70.9

4

56.2

5

46.9

6

41.7

7

38.7

8

35.8

最大值出现在:

因此:

CH 指数也支持 k=3

图 6 不同聚类数下DB指数分析图

DB 指数定义为:

特点:

  • 越小越好;

  • 越小说明簇间分离度越高。

结果如下:

k

DB

2

1.53

3

1.39

4

1.80

5

1.81

6

1.55

7

1.66

8

1.74

最小值出现在:

因此:

DB 指数同样认为 k=3 是最佳聚类数。

图 7 PCA降维后的真实类别分布图

图 8 PCA降维后的K-Means聚类结果图

3.4其余实验对比

1.不同聚类算法:

表 1 不同聚类算法效果对比表

方法

簇数量

轮廓系数

CH指数

DB指数

ARI指数

NMI指数

准确率

K-means

3

0.2849

70.9400

1.3892

0.8975

0.8759

0.9663

层次聚类

3

0.2774

67.6475

1.4186

0.7899

0.7865

0.9270

DBSCAN

3

0.1736

28.3607

3.7157

0.3838

0.4764

0.6910

GMM

3

0.2849

70.9400

1.3892

0.8975

0.8759

0.9663

2.基于随机森林:

图 9 随机森林分类结果图

图 10 各维度占比结果图

随机森林实验代码:

# -*- coding-UTF-8 -*- # Author: MYC # Date: 2026/5/27 14:53 # File: Random Forest.py # Description: 随机森林 from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, classification_report import matplotlib.pyplot as plt import pandas as pd plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 加载数据 wine = load_wine() X = wine.data y = wine.target feature_names = wine.feature_names # 划分训练集测试集 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 随机森林模型 rf = RandomForestClassifier( n_estimators=200, random_state=42 ) rf.fit(X_train, y_train) # 预测 y_pred = rf.predict(X_test) # 准确率 acc = accuracy_score(y_test, y_pred) print("Accuracy:", acc) print("\n分类报告:") print(classification_report(y_test, y_pred)) # 特征重要性 importance = pd.Series( rf.feature_importances_, index=feature_names ).sort_values() plt.figure(figsize=(8,6)) importance.plot(kind="barh") plt.xlabel("特征重要性") plt.ylabel("特征名称") plt.title("随机森林特征重要性分析") plt.tight_layout() plt.show()

3.MLP神经网络:

图 11 MLP 训练损失曲线图

图 12 MLP 分类结果图

MLP实验代码:

# -*- coding-UTF-8 -*- # Author: MYC # Date: 2026/5/27 15:01 # File: MLP神经网络.py # Description: 轻量化的深度学习 from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neural_network import MLPClassifier from sklearn.metrics import accuracy_score, classification_report from sklearn.decomposition import PCA import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 加载数据 wine = load_wine() X = wine.data y = wine.target # 标准化(神经网络必须做) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 划分训练集 X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.2, random_state=42, stratify=y ) # 神经网络 model = MLPClassifier( hidden_layer_sizes=(64, 32), activation='relu', solver='adam', max_iter=1000, random_state=42 ) model.fit(X_train, y_train) # 预测 y_pred = model.predict(X_test) acc = accuracy_score(y_test, y_pred) print("Accuracy:", acc) print("\n分类报告:") print(classification_report(y_test, y_pred)) # 1. 绘制训练损失曲线 plt.figure(figsize=(8, 5)) plt.plot( model.loss_curve_, marker="o" ) plt.xlabel("迭代次数") plt.ylabel("损失值") plt.title("MLP神经网络训练损失曲线") plt.grid(True) plt.tight_layout() plt.savefig( "MLP训练损失曲线.png", dpi=300 ) plt.show() # 2. PCA降维可视化预测结果 pca = PCA(n_components=2) X_test_pca = pca.fit_transform(X_test) plt.figure(figsize=(8, 6)) plt.scatter( X_test_pca[:, 0], X_test_pca[:, 1], c=y_pred, s=80 ) plt.xlabel("主成分1") plt.ylabel("主成分2") plt.title("MLP神经网络预测结果PCA可视化") plt.colorbar(label="预测类别") plt.grid(True) plt.tight_layout() plt.savefig( "MLP预测结果PCA可视化.png", dpi=300 ) plt.show()
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 17:37:10

【第 6 篇:处理数据统计与分析问题的 Agent】

第 6 篇&#xff1a;处理数据统计与分析问题的 Agent 系列记录&#xff1a;《从零搭建企业级 LLM 应用》&#xff0c;这是第 6 篇 上一篇&#xff1a;记忆系统——长短期记忆与混合记忆 下一篇&#xff1a;API 调用 Agent——接入外部数据接口llm 一、为什么需要数据分析 Agent…

作者头像 李华
网站建设 2026/6/10 17:33:55

Java Swing 图形界面编程

一、Swing 是什么&#xff1f; Swing 是 Java 提供的图形用户界面&#xff08;GUI&#xff09;工具包&#xff0c;用来开发桌面应用程序&#xff08;如记事本、计算器、桌面管理系统&#xff09;。 Swing 是 javax.swing 包下的类&#xff0c;使用前需要导包。 二、Swing 核心继…

作者头像 李华
网站建设 2026/6/10 17:32:57

别再只看插槽数量了:鲲鹏服务器配置昇腾 A2/Duo,PCIe Lane 才是关键

项目实战经验: 鲲鹏服务器配置昇腾 Atlas 300I A2 / Atlas 300I Duo 时,不能只看“有几个 PCIe 插槽”。真正要先算的是:CPU lane 总数、硬盘占用、网卡占用、RAID/HBA 占用,以及 A2/Duo 实际跑 x16 还是 x8。 一、为什么不能只看 PCIe 插槽数量? 项目里经常会遇到这种情…

作者头像 李华
网站建设 2026/6/10 17:30:10

Vue2项目里手把手集成bpmn-process-designer流程设计器(含版本避坑指南)

Vue2项目实战&#xff1a;深度集成bpmn-process-designer流程设计器全攻略在传统企业级应用开发中&#xff0c;业务流程管理(BPM)系统的构建往往需要专业的流程设计工具支持。对于Vue2技术栈的开发者而言&#xff0c;如何在现有项目中快速集成一个功能完备且可定制的流程设计器…

作者头像 李华