QT+VS实现Kmeans聚类算法

1、Kmeans的定义

聚类是一个将数据集中在某些方面相似的数据成员进行分类组织的过程,聚类就是一种发现这种内在结构的技术,聚类技术经常被称为无监督学习k均值聚类是最著名的划分聚类算法,由于简洁和效率使得他成为所有聚类算法中最广泛使用的。

无监督学习通常用于聚类,通过样本件的相似性对数据集进行聚类,使类内差距最小化,类间差距最大化。

2、原理

首先需要弄清楚两个概念:簇和质心

簇: 直观上来看,簇是一组聚在一起的数据,在一个簇中的数据就认为是同一类。

质心: 簇中所有数据的均值通常被称为这个簇的质心。

如何求取质心:

 在一个二维平面中,一簇数据点的质心的横坐标就是这一簇数据点的横坐标的均值,质心的纵坐标就是这一簇数据点的纵坐标的均值。同理可推广至高维空间。

欧式距离计算公式:

二维平面上的欧式距离:

假设待求两点的二维平面坐标为a(,)和b(,),则其距离公式为:

==

3、实现的流程步骤 

  1. 首先随机选取样本中的K个点作为初始聚类中心(质心);
  2. 分别算出样本中其他数据点距离这K个聚类中心的距离,以最近距离的质心缩在的簇作为该数据点分类后的簇;
  3. 对上述分类完的样本再进行每个簇求平均值,求解出新的聚类质心;
  4. 与前一次计算得到的K个聚类质心比较,如果聚类质心发生变化,转过程b,否则转过程e;
  5. 当质心不再发生变化时,停止并输出聚类结果。

 4、实现结果

5、部分代码解析

(1)首先,为了提高分类精度,K个质心初始值的选取,采用人工确定的方法。先人为的选取K个初值,并写成txt格式,如下:

 格式:点号-X坐标-Y坐标

读取K值数据的函数如下:

void Kmeans::onBtReadK()
{QString fileName = QFileDialog::getOpenFileName(this, tr("打开"));QFile file(fileName);bool isOpen = 1;if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){isOpen = 0;QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));}QTextStream stream(&file);while (!stream.atEnd()){QString str = stream.readLine();QStringList list = str.split(",");Pointp k1;k1.no = list.at(0);k1.x = list.at(1).toDouble();k1.y = list.at(2).toDouble();k.push_back(k1);}//判断是否读取完毕if (stream.atEnd() && isOpen){QMessageBox box;box.setText("数据读取完毕");box.exec();}dd = readK;
}

 (2)读取K个初始值之后,需要读取整个样本的数据(样本数据格式同K值格式一致),读取函数如下:

void Kmeans::onBtReadData()
{K = ui.lineEdit->text().toInt();p.clear();//打开文件对话框QString fileName = QFileDialog::getOpenFileName(this, tr("打开"));QFile file(fileName);bool isOpen = 1;if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){isOpen = 0;QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));}//逐行读取文本文件QTextStream stream(&file);while (!stream.atEnd()){Pointp pt;QString str = stream.readLine();QStringList list = str.split(",");pt.no = list.at(0);pt.x = list.at(1).toDouble();pt.y = list.at(2).toDouble();p.push_back(pt);}file.close();//判断是否读取完毕if (stream.atEnd()&&isOpen){QMessageBox box;box.setText("数据读取完毕");box.exec();}
}

(3)在对话框中输入簇个数,然后点击“开始聚类”按钮,开始进行聚类。首先是计算每个样本到K个聚类中心的距离,并找出最小值,作为该样本点的聚类结果。代码如下:

//计算每个对象至聚类中心的距离
void Kmeans::CalDis()
{for (int i = 0; i < p.size(); i++){double s0 = 0; QString no; Dis ss; int t = 0;for (int j = 0; j < K; j++){double x1 = p.at(i).x;double y1 = p.at(i).y;double x2 = k.at(j).x;double y2 = k.at(j).y;double s1 = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));t++;if (t == 1){s0 = s1;no = k.at(j).no;}if (s1 < s0){s0 = s1;no = k.at(j).no;}}ss.s = s0;ss.no = p.at(i).no;ss.x = p.at(i).x;ss.y = p.at(i).y;ss.noK = no;S.push_back(ss);}
}

(4)根据分类后的样本计算新的质心,如下:

//计算质心
void Kmeans::Calcentroid()
{centroid s;for (int i = 0; i < k.size(); i++){s.sx = 0; s.sy = 0; int iCt = 0;for (int j = 0; j < S.size(); j++){if (k.at(i).no == S.at(j).noK){s.sx = s.sx + S.at(j).x;s.sy = s.sy + S.at(j).y;iCt++;}}s.noK = k.at(i).no;s.sx = s.sx / iCt;s.sy = s.sy / iCt;dis.push_back(s);}
}

(5)然后判断新质心与旧质心之间的距离,若为0,则停止重新计算。

6、整体代码如下(输入的数据中不能包含负数,因为控件范围是从0开始的)

//Kmeans.cpp文件
#include "Kmeans.h"Kmeans::Kmeans(QWidget *parent): QWidget(parent)
{start = false;dd = to2K;ui.setupUi(this);connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(onBtReadData()));connect(ui.pushButton_2, SIGNAL(clicked()), this, SLOT(onBtCalKmeans()));connect(ui.pushButton_3, SIGNAL(clicked()), this, SLOT(onBtReadK()));
}void Kmeans::onBtReadData()
{K = ui.lineEdit->text().toInt();p.clear();//打开文件对话框QString fileName = QFileDialog::getOpenFileName(this, tr("打开"));QFile file(fileName);bool isOpen = 1;if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){isOpen = 0;QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));}//逐行读取文本文件QTextStream stream(&file);while (!stream.atEnd()){Pointp pt;QString str = stream.readLine();QStringList list = str.split(",");pt.no = list.at(0);pt.x = list.at(1).toDouble();pt.y = list.at(2).toDouble();p.push_back(pt);}file.close();//判断是否读取完毕if (stream.atEnd()&&isOpen){QMessageBox box;box.setText("数据读取完毕");box.exec();}
}void Kmeans::onBtReadK()
{QString fileName = QFileDialog::getOpenFileName(this, tr("打开"));QFile file(fileName);bool isOpen = 1;if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){isOpen = 0;QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));}QTextStream stream(&file);while (!stream.atEnd()){QString str = stream.readLine();QStringList list = str.split(",");Pointp k1;k1.no = list.at(0);k1.x = list.at(1).toDouble();k1.y = list.at(2).toDouble();k.push_back(k1);}//判断是否读取完毕if (stream.atEnd() && isOpen){QMessageBox box;box.setText("数据读取完毕");box.exec();}dd = readK;
}void Kmeans::toK()
{//随机选取k个初始聚类中心for (int i = 0; i < K; i++){Pointp k1;k1.no = i + 1;k1.x = p.at(i).x;k1.y = p.at(i).y;k.push_back(k1);}
}int Kmeans::onBtCalKmeans()
{K = ui.lineEdit->text().toInt();if (S.size()&&p.size()==S.size()){QMessageBox box;box.setText("已经计算完成");box.exec();return 0;}if (dd == to2K){toK();}CalDis();//SCalcentroid();//用到S,得dis//CKmeans();//用到dis,得new k.int iCount = 0;while (iCount < K){if (dis.size()){for (int i = 0; i < k.size(); i++){for (int j = 0; j < dis.size(); j++){if (k.at(i).no == dis.at(j).noK){//qDebug() <<"k:" <<k.at(i).no<< k.at(i).x << k.at(i).y;//qDebug() <<"dis:" <<dis.at(i).noK.toInt()<< dis.at(j).sx << dis.at(j).sy<<endl;double detaX = k.at(i).x - dis.at(j).sx;double detaY = k.at(i).y - dis.at(j).sy;double sk = sqrt(detaX * detaX + detaY * detaY);//qDebug() << sk;if (sk == 0){iCount++;}else{CKmeans();}}}}}dis.clear();S.clear();CalDis();Calcentroid();}start = true;qDebug() << "S" << S.size();drawPoint();QMessageBox box;box.setText("计算完成");box.exec();return 1;
}Kmeans::~Kmeans()
{}//计算质心
void Kmeans::Calcentroid()
{centroid s;for (int i = 0; i < k.size(); i++){s.sx = 0; s.sy = 0; int iCt = 0;for (int j = 0; j < S.size(); j++){if (k.at(i).no == S.at(j).noK){s.sx = s.sx + S.at(j).x;s.sy = s.sy + S.at(j).y;iCt++;}}s.noK = k.at(i).no;s.sx = s.sx / iCt;s.sy = s.sy / iCt;dis.push_back(s);}
}//计算每个对象至聚类中心的距离
void Kmeans::CalDis()
{for (int i = 0; i < p.size(); i++){double s0 = 0; QString no; Dis ss; int t = 0;for (int j = 0; j < K; j++){double x1 = p.at(i).x;double y1 = p.at(i).y;double x2 = k.at(j).x;double y2 = k.at(j).y;double s1 = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));t++;if (t == 1){s0 = s1;no = k.at(j).no;}if (s1 < s0){s0 = s1;no = k.at(j).no;}}ss.s = s0;ss.no = p.at(i).no;ss.x = p.at(i).x;ss.y = p.at(i).y;ss.noK = no;S.push_back(ss);}
}//将新的质心坐标赋值给k
void Kmeans::CKmeans()
{for (int i = 0; i < k.size(); i++){for (int j = 0; j < dis.size(); j++){if (k.at(i).no == dis.at(j).noK){k.at(i).x = dis.at(j).sx;k.at(i).y = dis.at(j).sy;}}}
}//绘图函数
void Kmeans::drawPoint()
{QPicture pp;pp.setBoundingRect(ui.label_2->rect());QPainter painterP(&pp);QPen pen;painterP.setRenderHint(QPainter::Antialiasing, true);Pointp p1;p1.no = p.at(0).no;p1.x = p.at(0).x;p1.y = p.at(0).y;for (int i = 1; i < p.size(); i++){if (p1.x > p.at(i).x){p1.x = p.at(i).x;}if (p1.y > p.at(i).y){p1.y = p.at(i).y;}}double xmin = p1.x;double ymin = p1.y;for (int i = 1; i < p.size(); i++){if (p1.x < p.at(i).x){p1.x = p.at(i).x;}if (p1.y < p.at(i).y){p1.y = p.at(i).y;}}double xmax = p1.x;double ymax = p1.y;int w=ui.label_2->width();int h=ui.label_2->height();double a = w/(xmax -xmin);double b1 = h/(ymax -ymin);for (int i = 0; i < k.size(); i++){int r = qrand() % 256;int g = qrand() % 256;int b = qrand() % 256;QColor color = QColor(r, g, b);for (int j = 0; j < S.size(); j++){if (k.at(i).no == S.at(j).noK){pen.setColor(color);painterP.setPen(pen);int radius = 5;double x = S.at(j).x;double y = S.at(j).y;x = (x - xmin)*a;y = (y - ymin)*b1;painterP.drawEllipse(x - radius, y - radius, radius * 2, radius * 2);}}}ui.label_2->setPicture(pp);
}
//Kmeans.h文件
#pragma once#include <QtWidgets/QWidget>
#include "ui_Kmeans.h"
#include<QFileDialog>
#include<QFile>
#include<QMessageBox>
#include<QTextStream>
#include<vector>
#pragma execution_character_set("UTF-8")
#include<qDebug>
#include<QPainter>
#include<QColor>
#include<QColorDialog>
#include<QPicture>struct Pointp
{double x;double y;QString no;
};struct Dis
{double x;double y;QString no;QString noK;double s;
};struct centroid
{QString noK;double sx;double sy;
};enum Pd
{readK,to2K,blank
};class Kmeans : public QWidget
{Q_OBJECTpublic:Kmeans(QWidget *parent = nullptr);~Kmeans();public slots:void onBtReadData();int onBtCalKmeans();void onBtReadK();void toK();public:std::vector<Pointp> p;//原始数据点std::vector<Pointp> k;//各簇质心坐标int K;std::vector<Dis> S;std::vector<centroid> dis;bool start;Pd dd;public:void Calcentroid();void CKmeans();void CalDis();void drawPoint();private:Ui::KmeansClass ui;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/650245.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux中配置文件目录为什么用etc来命名

在早期的 Unix 系统中&#xff0c;/etc 目录的名称确实来源于单词 “etcetera” 的缩写&#xff0c;最初意味着 “其他”&#xff0c;用来存放杂项或者不属于其他特定目录的文件。然而&#xff0c;随着时间的推移&#xff0c;/etc 目录的用途逐渐演变并专门化。 在现代的 Linux…

Android App开发-简单控件(1)——文本显示

本章介绍了App开发常见的几类简单控件的用法&#xff0c;主要包括&#xff1a;显示文字的文本视图、容纳视图的常用布局、响应点击的按钮控件、显示图片的图像视图等。然后结合本章所涉及的知识&#xff0c;完成一个实战项目“简单计算器”的设计与实现。 1.1 文本显示 本节介绍…

架构篇27:如何设计计算高可用架构?

文章目录 主备主从集群小结计算高可用的主要设计目标是:当出现部分硬件损坏时,计算任务能够继续正常运行。因此计算高可用的本质是通过冗余来规避部分故障的风险,单台服务器是无论如何都达不到这个目标的。所以计算高可用的设计思想很简单:通过增加更多服务器来达到计算高可…

【Docker】nacos集群搭建Nginx负载均衡

目录 一、mysql安装与基操 1.1 数据准备 1.2 创建mysql与数据表 二、Nacos集群部署 2.1 创建nacos及配置 2.2 创建Nginx容器 一、mysql安装与基操 1.1 数据准备 拉取mysql docker pull mysql:5.7(版本) 定义挂载目录 mkdir -p /mysql/{conf,data,script} 配置my.c…

JavaWeb:商品管理系统(Vue版)

文章目录 1、功能介绍2、技术栈3、环境准备3.1、数据库准备3.2、在新建web项目中导入依赖3.3、编写Mybatis文件3.4、编写pojo类3.5、编写Mybatis工具类3.6、导入前端素材&#xff08;element-ui & vue.js & axios.js&#xff09;3.7、前端页面 4、功能实现4.1、查询所有…

深度学习:Softmax回归

在前面&#xff0c;我们介绍了线性回归模型的原理及实现。线性回归适合于预测连续值&#xff0c;而对于分类问题的离散值则束手无策。因此引出了本文所要介绍的softmax回归模型&#xff0c;该模型是针对多分类问题所提出的。下面我们将从softmax回归模型的原理开始介绍&#xf…

浪花 - 响应拦截器(强制登录)

1. 配置响应拦截器 import axios from axios;const myAxios axios.create({baseURL: http://localhost:8080/api/, });myAxios.defaults.withCredentials true;// 请求拦截器 myAxios.interceptors.request.use(function (config) {// Do something before request is sentc…

把批量M3U8网络视频地址转为MP4视频

在数字媒体时代&#xff0c;视频格式的转换已成为一项常见的需求。尤其对于那些经常处理网络视频的用户来说&#xff0c;将M3U8格式的视频转换为更常见的MP4格式是一项必备技能。幸运的是&#xff0c;现在有了固乔剪辑助手这款强大的工具&#xff0c;这一过程变得异常简单。下面…

Android SharedPreferences源码分析

文章目录 Android SharedPreferences源码分析概述基本使用源码分析获取SP对象初始化和读取数据写入数据MemoryCommitResultcommitToMemory()commit()apply()enqueueDiskWrite()writeToFile() 主动等待写回任务结束 总结 Android SharedPreferences源码分析 概述 SharedPrefer…

2024初学编曲免费软件FL Studio21.2.2

FL Studio在业内也被称作“水果”软件&#xff0c;这是一款功能强大、简单易上手的专业编曲软件。软件中的音效插件库拥有超过25种音效插件&#xff0c;能够帮助激发我们的创作灵感。而FL Studio中文还推出了训练营课程&#xff0c;初学者可以在训练营中进行编曲知识的学习&…

Android消息推送 SSE(Server-Sent Events)方案实践

转载请注明出处&#xff1a;https://blog.csdn.net/kong_gu_you_lan/article/details/135777170 本文出自 容华谢后的博客 0.写在前面 最近公司项目用到了消息推送功能&#xff0c;在技术选型的时候想要找一个轻量级的方案&#xff0c;偶然看到一篇文章讲ChatGPT的对话机制是基…

探索半导体制造业中的健永科技RFID读写器的应用方案

一、引言 在当今高度自动化的工业环境中&#xff0c;无线射频识别&#xff08;RFID&#xff09;技术已经成为实现高效生产的重要一环。特别是在半导体制造业中&#xff0c;由于产品的高价值和复杂性&#xff0c;生产过程的追踪和管理显得尤为重要。健永科技RFID读写器以其出色…

Java程序设计实验7 | IO流

*本文是博主对Java各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 目录 一、实验…

【办公技巧】Excel只读模式怎么改成编辑模式?

Excel文件打开之后&#xff0c;大家可能经常会遇到文件处于只读模式的情况&#xff0c;那么我们应该如何将excel只读模式改成编辑模式呢&#xff1f;今天和大家分享几种情况的解决方法。 首先&#xff0c;大部分的只读模式&#xff0c;打开之后都是没有密码的&#xff0c;只是…

LabVIEW振动信号分析

LabVIEW振动信号分析 介绍如何使用LabVIEW软件实现希尔伯特-黄变换&#xff08;Hilbert-Huang Transform, HHT&#xff09;&#xff0c;并将其应用于振动信号分析。HHT是一种用于分析非线性、非平稳信号的强大工具&#xff0c;特别适用于旋转机械等复杂系统的振动分析。开发了…

一款强大的矢量图形设计软件:Adobe Illustrator 2023 (AI2023)软件介绍

Adobe Illustrator 2023 (AI2023) 是一款强大的矢量图形设计软件&#xff0c;为设计师提供了无限创意和畅行无阻的设计体验。AI2023具备丰富的功能和工具&#xff0c;让用户可以轻松创建精美的矢量图形、插图、徽标和其他设计作品。 AI2023在界面和用户体验方面进行了全面升级…

单片机学习笔记---矩阵键盘

目录 矩阵键盘的介绍 独立按键和矩阵按键的相同之处&#xff1a; 矩阵按键的扫描 代码演示 代码模块化移植 Keil自定义模板步骤&#xff1a; 代码编写 矩阵键盘就是开发板上右下角的这个模块 这一节的代码是基于上一节讲的LCD1602液晶显示屏驱动代码进行的 矩阵键盘的介…

新版UI界面影视小程序亲测无问题带详细搭建教程

新版UI界面影视小程序亲测无问题带详细搭建教程 环境php7.0 — fileinfo–redis–sg11 mysql5.5 apache2.4 添加站点php7.0—-创建ftp—-上传后端文件《后端文件修改&#xff0c;/maccms/wxapi/config/dbs.php–修改当前数据库》—-设置ssl—-打开数据库安装cms 安装好后管…

仰暮计划|“星星之火可以燎原,平凡人的一生同样值得称赞

传递助老之情&#xff0c;践行为老初心。为学习和发扬助老为老精神&#xff0c;我参与了康乐忆享实践队开展的以“仰暮计划”为主题的实践活动&#xff0c;在实践过程中了解老人的人生经历&#xff0c;传播尊老爱老思想。我与老人谭爷爷在谈论家常时&#xff0c;他拿出年轻时的…

【项目日记(五)】第二层: 中心缓存的具体实现(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…