C# | KMeans聚类算法的实现,轻松将数据点分组成具有相似特征的簇

news2024/10/6 17:27:33

在这里插入图片描述

C# KMeans聚类算法的实现

文章目录

  • C# KMeans聚类算法的实现
    • 前言
    • 示例代码
    • 实现思路
    • 测试结果
    • 结束语

前言

本章分享一下如何使用C#实现KMeans算法。在讲解代码前先清晰两个小问题:

  1. 什么是聚类?
    聚类是将数据点根据其相似性分组的过程,它有很多的应用场景,比如:图像分割、文本分类、推荐系统等等。在这些应用场景里面我们需要将数据点分成多个簇,每个簇内的数据点具有相似的特征,以便于我们能够更简单的处理数据。

  2. 什么是KMeans?
    KMeans算法是一种常用的聚类算法,它可以将数据点分组成具有相似特征的簇。

示例代码

话不多说,我们直接上代码:

    /// <summary>
    /// KMeans算法类
    /// </summary>
    public static class KMeans
    {
        /// <summary>
        /// 使用 KMeans 算法对 Point 数组进行聚类
        /// </summary>
        /// <param name="points">待聚类的 Point 数组</param>
        /// <param name="k">聚类的个数</param>
        /// <returns>聚类的结果</returns>
        public static List<List<PointD>> Cluster(PointD[] points, int k)
        {
            // 初始化簇心的位置
            Random random = new Random();
            PointD[] centroids = new PointD[k];
            for (int i = 0; i < k; i++)
            {
                centroids[i] = points[random.Next(points.Length)];
            }

            // 分配每个点到最近的簇心
            List<PointD>[] clusters = new List<PointD>[k];
            for (int i = 0; i < k; i++)
            {
                clusters[i] = new List<PointD>();
            }

            foreach (PointD point in points)
            {
                int closest = 0;
                var closestDistance = Distance(point, centroids[0]);
                for (int i = 1; i < k; i++)
                {
                    var distance = Distance(point, centroids[i]);
                    if (distance < closestDistance)
                    {
                        closest = i;
                        closestDistance = distance;
                    }
                }
                clusters[closest].Add(point);
            }

            // 重新计算簇心位置
            bool moved = true;
            while (moved)
            {
                moved = false;
                for (int i = 0; i < k; i++)
                {
                    PointD newCentroid = Centroid(clusters[i]);
                    if (!newCentroid.Equals(centroids[i]))
                    {
                        centroids[i] = newCentroid;
                        moved = true;
                    }
                }
                if (moved)
                {
                    // 重新分配每个点到最近的簇心
                    for (int i = 0; i < k; i++)
                    {
                        clusters[i].Clear();
                    }
                    foreach (PointD point in points)
                    {
                        int closest = 0;
                        var closestDistance = Distance(point, centroids[0]);
                        for (int i = 1; i < k; i++)
                        {
                            var distance = Distance(point, centroids[i]);
                            if (distance < closestDistance)
                            {
                                closest = i;
                                closestDistance = distance;
                            }
                        }
                        clusters[closest].Add(point);
                    }
                }
            }

            // 返回每个簇的点集合
            List<List<PointD>> result = new List<List<PointD>>();
            for (int i = 0; i < k; i++)
            {
                result.Add(clusters[i]);
            }
            return result;
        }

        private static double Distance(PointD a, PointD b)
        {
            var dx = a.X - b.X;
            var dy = a.Y - b.Y;
            return Math.Sqrt(dx * dx + dy * dy);
        }

        private static PointD Centroid(List<PointD> points)
        {
            double totalX = 0;
            double totalY = 0;
            foreach (PointD point in points)
            {
                totalX += point.X;
                totalY += point.Y;
            }
            var centerX = totalX / points.Count;
            var centerY = totalY / points.Count;
            return new PointD(centerX, centerY);
        }
    }

我们这里定义了一个双进度点的结构体PointD:

    public struct PointD 
    {
        public PointD(double x, double y) 
        {
            X = x;
            Y = y;
        }

        public double X { get; set; }
        public double Y { get; set; }

        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }

            PointD other = (PointD)obj;
            return X.Equals(other.X) && Y.Equals(other.Y);
        }
    }

实现思路

接下来详细讲解一下KMeans算法的思路。

KMeans类中包含名为Cluster的静态方法。该方法接收两个参数,【待聚类的Point数组】和【聚类的个数】。

第一步是要随机初始化簇心的位置(使用Random)。

第二步要将每个数据点分配到距离其最近的簇心中。思路是:对于每个数据点都计算其与所有簇心的距离(使用名为Distance的私有静态方法),找到最近的簇心,并将该数据点分配到该簇中。

第三步要重新计算每个簇心的位置(使用静态方法Centroid)。每个簇都计算其所有数据点的中心点作为该簇的新簇心。如果新簇心和旧簇心不同,则说明簇心已经发生了移动,我们需要重新分配每个数据点到距离其最近的簇心中。

第四步返回聚类结果,也就是每个簇内的数据点集合。

由于我们在第一步的时候就是用的Random来随机选择初始簇心,因此多次聚类的结果可能不一样。

测试结果

测试代码如下:

    [TestClass]
    public class KMeansTest
    {
        [TestMethod]
        public void TestCluster()
        {
            PointD[] points = new PointD[]
            {
                new PointD(1, 2),
                new PointD(2, 1),
                new PointD(3, 2),
                new PointD(2, 3),
                new PointD(5, 6),
                new PointD(6, 5),
                new PointD(7, 6),
                new PointD(6, 7),
            };
            int k = 3;
            List<List<PointD>> clusters = KMeans.Cluster(points, k);
            foreach (List<PointD> cluster in clusters)
            {
                Console.WriteLine("Cluster:");
                foreach (PointD point in cluster)
                {
                    Console.WriteLine("  ({0}, {1})", point.X, point.Y);
                }
            }
        }
    }

连续三次执行的结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结束语

通过本章的代码可以轻松实现KMeans算法对数据聚类。如果您觉得本文对您有所帮助,请不要吝啬您的点赞和评论,提供宝贵的反馈和建议,让更多的读者受益。

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

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

相关文章

章节1:信息收集

章节1:信息收集 1 信息收集概览 01 为什么要做信息收集&#xff1f; 渗透测试的流程 确定目标 信息收集 漏洞扫描 漏洞利用 形成报告 信息收集包括的内容 域名信息、IP段、开放的端口、网站架构、文件目录结构、软件版本、WAF、旁站、C段… 分类 域名相关信息IP相关…

Redis缓存数据库(四)

目录 一、概述 1、Redis Sentinel 1.1、docker配置Redis Sentinel环境 2、Redis存储方案 2.1、哈希链 2.2、哈希环 3、Redis分区(Partitioning) 4、Redis面试题 一、概述 1、Redis Sentinel Redis Sentinel为Redis提供了高可用解决方案。实际上这意味着使用Sentinel…

Java 与排序算法(1):冒泡排序

一、冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它的基本思想是通过不断交换相邻两个元素的位置&#xff0c;使得较大的元素逐渐往后移动&#xff0c;直到最后一个元素为止。冒泡排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)&…

《Kali渗透基础》02. 基本工具

kali渗透 1&#xff1a;基本工具1.1&#xff1a;NetCat1.1.1&#xff1a;命令参数1.1.2&#xff1a;示例 1.2&#xff1a;NCat1.2.1&#xff1a;命令参数1.2.2&#xff1a;示例 1.3&#xff1a;WireShark1.4&#xff1a;TCPdump1.4.1&#xff1a;命令参数1.4.2&#xff1a;示例…

C语言——函数

目录 1. 函数基本用法1.1 定义和三要素1.2 函数的声明和定义1.2.1 函数声明1.2.2 函数定义格式 1.3 函数调用1.4 函数传参1.4.1 值传递1.4.2 地址传递1.4.3 数组传递 1.5 函数和栈区 2. 开辟堆空间2.1 堆的概念2.2.malloc函数2.2.1 定义2.2.2 用法 2.3 free()函数定义注意&…

随机数发生器设计(一)

1 随机数发生器设计概述 密码行业的随机数发生器总体框架标准为GM/T 0103。随机数发生器可以分为硬件随机数发生器和软件随机数发生器。 硬件随机数发生器一般以组成部件的形式集成在安全芯片的内部&#xff0c;或者随机数发生器本身就是安全芯片。考虑到随机数发生器是密码产…

ChatGPT 能自己跑代码了!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; time leap, sci-fi, photorealistic, --niji 5 --ar 3:2 --s 1000 自 ChatGPT 发布以来&#xff0c;各行各业对其能力探索的举措一直没有停止。 很多大厂纷纷跟进&#xff0c;竞相推出自研…

Springboot +spring security,登录用户数据获取

一.简介 前面章节学习了登录表单的配置并且对源码进行了简单的分析&#xff0c;现在有个问题了&#xff0c;既然用户登录了&#xff0c;那么如何在接口中获取用户信息呢。这篇文章就来看下这个问题&#xff0c;代码中获取登录用户信息。 二.创建项目 如何创建一个SpringSecu…

笔记:BLIP源码之(1)数据集预处理【仅考虑Image-Text Retrieval on COCO】

BLIP&#xff1a;Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generat 论文的两个贡献如下&#xff1a; 从模型的角度&#xff1a;提出了 Encoder-Decoder (MED) 的多模态混合 An MED can operate either as a unimodal encode…

Js常识三

文章目录 作用域GCclosure变量和函数提升函数参数 作用域 GC Js Gc 算法 引用计数&#xff08;已淘汰&#xff09;标记清除 closure 一句话&#xff1a;内层函数 外层函数的变量 闭包作用&#xff1a;私有化数据&#xff0c;or 私有化状态 变量和函数提升 Js 祖传var变…

C语言结构体初级

目录 一、为什么要用结构体 二、使用结构体的具体形式 1.结构体类型的声明&#xff08;main函数外部&#xff09; 2.结构体变量的定义&#xff08;在main函数内或者外&#xff09; 3.结构体变量的初始化 4.结构体成员的访问 5.结构体的传参 跑了这么久&#xff0c;再坚…

分布式软件架构——单体架构

序言 当一项大工程需要大量人员共同开发&#xff0c;并保证它们分布在网络中的大量服务器节点能够同时运行&#xff0c;那么随着项目规模的增大、运行时间变长&#xff0c;它必然会受到墨菲定律的无情打击。 Murphy’s Law&#xff1a;Anything that can go wrong will go wro…

Qt文件系统源码分析—第四篇QLockFile

深度 本文主要分析Windows平台&#xff0c;Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次&#xff0c;再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice Q…

法规标准-ISO 17361标准解读

ISO 17361是做什么的&#xff1f; ISO 17361全称为智能交通系统-车道偏离警告系统性能要求和测试程序&#xff0c;其中主要描述了LDWS系统的功能要求及测试要求 系统功能 车道偏离警告系统的功能元件应符合图中的要求&#xff0c;抑制请求、车速检测、驾驶员偏好和其他附加功…

[CTF/网络安全] 攻防世界 simple_js 解题详析

[CTF/网络安全] 攻防世界 simple_js 解题详析 代码分析代码漏洞姿势String[fromCharCode]总结 题目描述&#xff1a;小宁发现了一个网页&#xff0c;但却一直输不对密码。(Flag格式为 Cyberpeace{xxxxxxxxx} ) 页面源代码&#xff1a; 代码分析 function dechiffre(pass_enc){…

StarRocks 集群模式搭建

一、StarRocks 集群模型搭建 上篇文章对 StarRocks 进行了简单的介绍及使用 Docker 进行了快速体验&#xff0c;本篇文章进行StarRocks 集群模型的搭建&#xff0c;下面是上篇文章的地址&#xff1a; StarRocks 极速全场景 MPP 数据库介绍及使用 部署规划 host主机名角色192.…

求解包含约束的最优化问题:拉格朗日乘子法和KKT条件

文章目录 无约束等式约束不等式约束KKT条件 无约束 之前梯度类算法中介绍的最速下降法、牛顿法和拟牛顿法&#xff0c;可以直接使用的条件之一为&#xff1a;决策变量都是无约束的。 用数学语言描述的话&#xff0c;可以表达为&#xff1a;决策变量为 x ( x 1 , x 2 , ⋅ ⋅…

LeetCode104. 二叉树的最大深度(递归非递归)

写在前面&#xff1a; 题目链接&#xff1a;LeetCode104.二叉树的最大深度 编程语言&#xff1a;C 题目难度&#xff1a;简单 一、题目描述 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子…

You Only Look Once:Unified,Real-Time Object Detection总结笔记

一、论文思想 1.将一个图像分成S*S个网格&#xff08;grid cell&#xff09;&#xff0c;如果某个object的中心落在这个网格中&#xff0c;则这个网络就负责预测这个object。 2.每个网格要预测B个bounding box&#xff0c;每个bounding box除了要预测位置之外&#xff0c;还要…

微服务技术(SpringCloud、Docker、RabbitMQ)

目录 一、微服务技术简介 二、服务拆分及远程调用 1.Eureka注册中心 2.Nacos注册中心 3.Nacos配置管理 4.http客户端Feign 三、统一网关Gateway 四、Docker 五、异步通信技术 六、ElasticSearch 一、微服务技术简介 微服务是分布式架构&#xff08;分布式&#xff…