使用golang实现k-means

k-means聚类算法

K-Means是一种无监督算法,其目标是将数据进行分类。分类个数要求已知。

k-means流程

  1. 随机确定K个点作为质心、
  2. 找到离每个点最近的质心,将这个点分配到这个质心代表的簇里
  3. 再对每个簇进行计算,以点簇的均值点作为新的质心
  4. 如果新的质心和上一轮的不一样,则迭代进行2-3步骤,直到质心位置稳定

本文目标

  1. 在golang中实现k-means算法。
  2. 使用matplotlib绘制聚类散点图。
  3. 尝试并行处理。
  4. 与sklearn结果对比。

执行结果

左侧:输出
中间:本文效果
右侧:sklearn效果
500样本5中心
Q: 为什么样本一样,结果不同?
A: 两方面,首先算法的结束方法中阈值不同,然后是初始k均值点选择不同。

确定数据结构

我把kMeans封装成了对象,此外还有Point。实现如下

type (Point struct {X, Y float64}KMeans struct {points       []Pointk            intdistanceFunc distanceFuncavgPoints    []Point}distanceFunc func(p1, p2 Point) float64
)

生成随机样本

func generateRandomPoints(n int) []tool.Point {points := make([]tool.Point, n)for i := 0; i < n; i++ {points[i] = tool.Point{X: rand.Float64() * 100,Y: rand.Float64() * 100,}}return points
}

确定距离度量函数

	distance := func(p1, p2 tool.Point) float64 {return math.Sqrt(math.Pow(p1.X-p2.X, 2) + math.Pow(p1.Y-p2.Y, 2))}

初始化kmeans对象

func (kMeans *KMeans) Init(k int, points []Point, distanceFunc distanceFunc) {kMeans.k = kkMeans.points = pointskMeans.distanceFunc = distanceFunckMeans.avgPoints = make([]Point, kMeans.k)
}
func (kMeans *KMeans) initializeAvgPoints() {copy(kMeans.avgPoints, kMeans.points[:kMeans.k])
}

确定算法大致流程

func (kMeans *KMeans) Do(checkFunc func(oldCentroids, newCentroids []Point) bool) ([][]Point, int) {kMeans.initializeAvgPoints()var (clusters     [][]PointtmpAvgPoints []Pointcount        int)for {// 获取聚类含有哪些点clusters = kMeans.computeDistanceToAvgPoints()// 更新聚类中心tmpAvgPoints = kMeans.updateAvgPoints(clusters)// 检查质心位置的变化if checkFunc(kMeans.avgPoints, tmpAvgPoints) {break}// 更新质心位置kMeans.avgPoints = tmpAvgPointscount++}return clusters, count
}

计算聚类(尝试并发)

Q: 为什么可以并发?
A: 因为计算聚类时,对每个点的运算的独立的,依赖的数据不会在计算时修改。

func (kMeans *KMeans) computeDistanceToAvgPoints() [][]Point {type BakPoint struct {i intp Point}clusters := make([][]Point, len(kMeans.avgPoints))resultCh := make(chan BakPoint, len(kMeans.points))for _, point := range kMeans.points {computeMinDistanceForSignlePoint := func(point Point, avgPoints []Point, distanceFunc func(p1, p2 Point) float64, ch chan BakPoint) {minDistance := struct {d float64i int}{d: math.MaxFloat64,i: -1,}for i, avgPoint := range avgPoints {if d := distanceFunc(avgPoint, point); d < minDistance.d {minDistance.d = dminDistance.i = i}}ch <- BakPoint{p: point, i: minDistance.i}}go computeMinDistanceForSignlePoint(point, kMeans.avgPoints, kMeans.distanceFunc, resultCh)}for i := 0; i < len(kMeans.points); i++ {result := <-resultChclusters[result.i] = append(clusters[result.i], result.p)}close(resultCh)return clusters
}

更新K均值点

func (kMeans *KMeans) updateAvgPoints(clusters [][]Point) []Point {centroids := make([]Point, kMeans.k)for i, cluster := range clusters {sumX, sumY := 0.0, 0.0for _, point := range cluster {sumX += point.XsumY += point.Y}centroids[i].X = sumX / float64(len(cluster))centroids[i].Y = sumY / float64(len(cluster))}return centroids
}

使用matplotlib绘制图像

import matplotlib.pyplot as pltdef readFromFile():clusters = []current_cluster = []with open('points.txt', 'r') as file:for line in file:if line.strip() == "----":if current_cluster:clusters.append(current_cluster)current_cluster = []else:x, y = map(float, line.split())current_cluster.append((x, y))if current_cluster:clusters.append(current_cluster)return clustersclusters = readFromFile()for cluster in clusters:X = [point[0] for point in cluster]Y = [point[1] for point in cluster]plt.scatter(X, Y, marker='.')plt.xlabel('X')
plt.ylabel('Y')
plt.title('Clustered Scatter Plot')
plt.show()

使用sklearn计算

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeanspoints = []
with open('rawpoints.txt', 'r') as file:for line in file:x, y = map(float, line.split())points.append([x, y])X = np.array(points)n_clusters = 5
kmeans = KMeans(n_clusters=n_clusters)
kmeans.fit(X)
labels = kmeans.labels_
centroids = kmeans.cluster_centers_plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', marker='.')
plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', s=10, color='red', label='Centroids')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('K-Means Clustering')
plt.legend()
plt.show()

完整代码

项目结构如下

KMeans-golang
│  go.mod
│  main.go
│  raw.py
│  show.py
└─toolkmeans.go

main.go

package mainimport ("fmt""k-means/tool""math""math/rand""os""os/exec""time"
)func generateRandomPoints(n int) []tool.Point {points := make([]tool.Point, n)for i := 0; i < n; i++ {points[i] = tool.Point{X: rand.Float64() * 100,Y: rand.Float64() * 100,}}return points
}func wrapper(name string, fun func()) {start := time.Now()fun()elapsed := time.Since(start)fmt.Printf("%s 函数执行时间:%s\n", name, elapsed)fmt.Printf("%s 函数执行时间(纳秒):%dns\n", name, elapsed.Nanoseconds())
}func writeToFile(points [][]tool.Point) {file, err := os.Create("points.txt")if err != nil {fmt.Println("Failed to create file:", err)return}defer file.Close()for _, row := range points {for _, p := range row {_, err := fmt.Fprintf(file, "%f %f\n", p.X, p.Y)if err != nil {fmt.Println("Failed to write to file:", err)return}}_, err := fmt.Fprintf(file, "----\n")if err != nil {fmt.Println("Failed to write to file:", err)return}}fmt.Println("Data written to file successfully.")
}
func writeToFile2(points []tool.Point) {file, err := os.Create("rawpoints.txt")if err != nil {fmt.Println("Failed to create file:", err)return}defer file.Close()for _, p := range points {_, err := fmt.Fprintf(file, "%f %f\n", p.X, p.Y)if err != nil {fmt.Println("Failed to write to file:", err)return}}fmt.Println("Data written to file successfully.")
}
func main() {rand.Seed(time.Now().UnixNano())// 样本数据var points []tool.Pointwrapper("生成样本", func() {points = generateRandomPoints(100)})k := 5distance := func(p1, p2 tool.Point) float64 {return math.Sqrt(math.Pow(p1.X-p2.X, 2) + math.Pow(p1.Y-p2.Y, 2))}kMeansObj := new(tool.KMeans)kMeansObj.Init(k, points, distance)var (finalClusters [][]tool.Pointcount         int)wrapper("执行算法", func() {finalClusters, count = kMeansObj.Do(func(oldCentroids, newCentroids []tool.Point) bool {epsilon := 0.000001for i := 0; i < len(oldCentroids); i++ {if distance(oldCentroids[i], newCentroids[i]) > epsilon {return false}}return true})})fmt.Println("count: ", count)wrapper("写入文件", func() {writeToFile2(points)writeToFile(finalClusters)})go func() {command := exec.Command("C:\\Projects\\PycharmProjects\\deelLearn\\venv\\Scripts\\python.exe", "raw.py")command.Run()command.Wait()}()command := exec.Command("C:\\Projects\\PycharmProjects\\deelLearn\\venv\\Scripts\\python.exe", "show.py")command.Run()command.Wait()
}

raw.py

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeanspoints = []
with open('rawpoints.txt', 'r') as file:for line in file:x, y = map(float, line.split())points.append([x, y])X = np.array(points)n_clusters = 5
kmeans = KMeans(n_clusters=n_clusters)
kmeans.fit(X)
labels = kmeans.labels_
centroids = kmeans.cluster_centers_plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', marker='.')
plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', s=10, color='red', label='Centroids')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('K-Means Clustering')
plt.legend()
plt.show()

show.py

import matplotlib.pyplot as pltdef readFromFile():clusters = []current_cluster = []with open('points.txt', 'r') as file:for line in file:if line.strip() == "----":if current_cluster:clusters.append(current_cluster)current_cluster = []else:x, y = map(float, line.split())current_cluster.append((x, y))if current_cluster:clusters.append(current_cluster)return clustersclusters = readFromFile()for cluster in clusters:X = [point[0] for point in cluster]Y = [point[1] for point in cluster]plt.scatter(X, Y, marker='.')plt.xlabel('X')
plt.ylabel('Y')
plt.title('Clustered Scatter Plot')
plt.show()

tool/kmeans.go

package toolimport "math"type (Point struct {X, Y float64}KMeans struct {points       []Pointk            intdistanceFunc distanceFuncavgPoints    []Point}distanceFunc func(p1, p2 Point) float64
)func (kMeans *KMeans) Init(k int, points []Point, distanceFunc distanceFunc) {kMeans.k = kkMeans.points = pointskMeans.distanceFunc = distanceFunckMeans.avgPoints = make([]Point, kMeans.k)
}func (kMeans *KMeans) Do(checkFunc func(oldCentroids, newCentroids []Point) bool) ([][]Point, int) {kMeans.initializeAvgPoints()var (clusters     [][]PointtmpAvgPoints []Pointcount        int)for {// 获取聚类含有哪些点clusters = kMeans.computeDistanceToAvgPoints()// 更新聚类中心tmpAvgPoints = kMeans.updateAvgPoints(clusters)// 检查质心位置的变化if checkFunc(kMeans.avgPoints, tmpAvgPoints) {break}// 更新质心位置kMeans.avgPoints = tmpAvgPointscount++}return clusters, count
}func (kMeans *KMeans) initializeAvgPoints() {copy(kMeans.avgPoints, kMeans.points[:kMeans.k])
}func (kMeans *KMeans) computeDistanceToAvgPoints() [][]Point {type BakPoint struct {i intp Point}clusters := make([][]Point, len(kMeans.avgPoints))resultCh := make(chan BakPoint, len(kMeans.points))for _, point := range kMeans.points {computeMinDistanceForSignlePoint := func(point Point, avgPoints []Point, distanceFunc func(p1, p2 Point) float64, ch chan BakPoint) {minDistance := struct {d float64i int}{d: math.MaxFloat64,i: -1,}for i, avgPoint := range avgPoints {if d := distanceFunc(avgPoint, point); d < minDistance.d {minDistance.d = dminDistance.i = i}}ch <- BakPoint{p: point, i: minDistance.i}}go computeMinDistanceForSignlePoint(point, kMeans.avgPoints, kMeans.distanceFunc, resultCh)}for i := 0; i < len(kMeans.points); i++ {result := <-resultChclusters[result.i] = append(clusters[result.i], result.p)}close(resultCh)return clusters
}func (kMeans *KMeans) updateAvgPoints(clusters [][]Point) []Point {centroids := make([]Point, kMeans.k)for i, cluster := range clusters {sumX, sumY := 0.0, 0.0for _, point := range cluster {sumX += point.XsumY += point.Y}centroids[i].X = sumX / float64(len(cluster))centroids[i].Y = sumY / float64(len(cluster))}return centroids
}

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

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

相关文章

LeetCode343:整数拆分

题目描述 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 代码 动态规划 class Solution { public:int integerBreak(int n) {/*dp[i]&#xff1a;表示对…

vue3组件插槽

Index.vue: <script setup> import { ref, onMounted } from vue import Child from ./Child.vue import ./index.cssonMounted(() > {}) </script><template><div class"m-home-wrap"><Child>插槽</Child><div class&qu…

使用flutter开发一个U盘文件管理APP,只解析图片文件

今天教大家用flutter撸一个U盘文件管理APP,需求是这样的: 当我在Android设备上插入U盘后,我能在APP中打开U盘的文件目录,并且能进入对应目录的下一级目录,如果下级目录下有图片文件,我就对这个图片文件进行解析,并展示出来。 需求了解后,先上个效果图: 效果图看完后,…

【Java orm 框架比较】九 新增wood框架对比

【Java orm 框架比较】九 新增wood框架对比 本次新增wood 框架测试 测试数据存储、分页查询&#xff0c;文档及框架比较稳定半天时间加入测试使用 迁移到&#xff08;https://gitee.com/wujiawei1207537021/spring-orm-integration-compare&#xff09; orm框架使用性能比较…

Java设计模式 _行为型模式_命令模式

一、命令模式 1、命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种行为型模式&#xff0c;一种数据驱动的设计模式。命令模式中请求以命令的形式包裹在对象中&#xff0c;即将命令封装为类&#xff0c;从而可以使用不同的请求&#xff0c;队列等操作具体的对象…

k8s部署最新版zookeeper集群(3.9.2),并配置prometheus监控

目录 zookeeper集群部署创建zookeeper文件夹namespace.yamlscripts-configmap.yamlserviceaccount.yamlstatefulset.yamlsvc-headless.yamlsvc.yamlmetrics-svc.yaml执行部署 接入prometheus访问prometheus查看接入情况导入zookeeper监控模版监控展示 zookeeper集群部署 复制粘…

数据库权限管理

1.查看系统级权限&#xff08;global level) Select * from mysql.user\G; 2.查看数据库中所有表的权限 Select * from mysql.db\G 3.远程连接数据库 第一步在有数据库服务上的主机上&#xff1a;授权 grant all on *.* to root192.168.40.83 identified by Zxy20234; 第…

【数据结构】 二叉树的顺序结构——堆的实现

普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储 。 一、堆的概念及结构 父节点比孩子结点大 是大堆 父节点比孩子结点小 是小堆 堆的性质 堆中某…

java2Schema——jsonschema-generator使用

从JAVA代码生成Schema&#xff1a;https://github.com/victools/jsonschema-generator 引入依赖 <dependency><groupId>com.github.victools</groupId><artifactId>jsonschema-generator</artifactId><version>4.28.0</version><…

Centos7使用kubeadm搭建k8s集群(一主两从)----(mac版)

一、环境准备 1、下载centos7镜像 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 下载地址: centos安装包下载_开源镜像站-阿里云 选择对应的版本即可&#xff0c;我下载的&#xff1a;CentOS-7-x86_64-DVD-2207-02.iso 2、使用VirtualBox安装centos 选择新建&#xff0c…

Mac下安装ffmpeg

1、安装gedit brew install gedit2、配置环境变量&#xff0c;打开~/.zshrc&#xff0c;在末尾添加语句 export PATH$PATH:/usr/local/ffmpeg/bin3、执行语句&#xff0c;使环境变量生效 source ~/.zshrc 4、终端输入 ffmpeg &#xff0c;看环境变量是否配置成功。 至此&a…

现货黄金流程到何种程度?现货黄金在金融产品中的占比是多少?

踏入2024年以来&#xff0c;受美联储降息以及地缘局势紧张的影响&#xff0c;美元受压&#xff0c;避险情绪高涨&#xff0c;众多因素影响下黄金价格出现了强势的上涨&#xff0c;屡创历史新高。在上涨如此强劲的背景下&#xff0c;投资者希望通过黄金投资来实现资产增值。市场…

雷军-2022.8小米创业思考-6-互联网七字诀之口碑:口碑即定位,超预期才有口碑,品牌建设

第六章 互联网七字诀 专注、极致、口碑、快&#xff0c;这就是我总结的互联网七字诀&#xff0c;也是我对互联网思维的高度概括。 口碑 用户口碑是所有产品成功的关键因素&#xff0c;这是不言而喻的公理。 资源永远有限&#xff0c;对于创业公司尤其如此。只有专注&#xf…

等保测评数据库整改步骤

等保测评中的数据库整改步骤通常包括以下几个关键环节&#xff1a; 1. **身份鉴别** - 确保每个用户都有唯一身份标识&#xff0c;密码具有复杂度要求&#xff0c;并定期更换。 - 配置数据库系统鉴别失败处理功能&#xff0c;如限制非法登录次数和超时自动退出。 2. **…

C++语法|进程虚拟地址空间和函数调用栈

本文来自施磊老师的课程&#xff0c;老师讲的非常不错&#xff0c;我的笔记也是囫囵吞枣全部记下&#xff0c;但是我在这里推荐一本书&#xff0c;真的真的建议初学C或者想要进阶C的同学们看看&#xff1a;《CPU眼里的C/C》 文章目录 进程的虚拟地址空间和布局进程虚拟地址空间…

机器学习过拟合解决方案 - 正则化

在机器学习中&#xff0c;线性回归是一种常见的预测模型&#xff0c;旨在找到一个线性函数来尽可能准确地预测目标值。然而&#xff0c;当模型过于复杂&#xff0c;尤其是参数过多时&#xff0c;就会发生过拟合现象&#xff0c;即模型在训练数据上表现很好&#xff0c;但在新的…

ros 学习记录(二)URDF小车运动控制

URDF小车运动控制 准备工作创建 robot_xacro.launch 接上文&#xff0c;想用键盘控制小车在Gazebo中移动。 准备工作 名称版本ROSNoeticGazebo11.11.0 创建 robot_xacro.launch 通过运行这个launch文件&#xff0c;可以启动Gazebo仿真环境&#xff0c;并在仿真环境中加载和…

【ITK配准】第十四期 BSpline可变形配准样例

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享ITK配准中的BSpline可变形配准,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO BSpline可变形…

【八十五】【算法分析与设计】单调栈的全新版本,两个循环维护左小于和右小于信息,84. 柱状图中最大的矩形,85. 最大矩形

84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释&am…

nginx开启局域网https访问

需求 调试webRTC 功能,需要在局域网搭建https发给我协议; 实现 环境 局域网已部署有nginx; 部署可参考专栏文章 已安装Openssl 未安装可以执行 sudo yum install openssl 这个命令进行安装 生成证书和私钥 生成私钥:打开命令提示符或终端窗口,执行以下命令以生成私钥文件…