[Go]使用Golang对鸢尾花数据集进行k-means聚类

k-means算法是一种简单的迭代型聚类算法,采用距离作为相似性指标,从而发现给定数据集中的K个类,且每个类的中心是根据类中所有值的均值得到,每个类用聚类中心来描述。对于给定的一个包含n个d维数据点的数据集X以及要分得的类别K,选取欧式距离作为相似度指标,聚类目标是使得各类的聚类平方和最小,即最小化:

结合最小二乘法和拉格朗日原理,聚类中心为对应类别中各数据点的平均值,同时为了使得算法收敛,在迭代过程中,应使最终的聚类中心尽可能的不变。

聚类过程

  • 首先任取k个样本点作为k个簇的初始中心
  • 对每一个样本点,计算它们与k个中心的距离,把它归入距离最小的中心所在的簇
  • 等到所有的样本点归类完毕,重新计算k个簇的中心
  • 重复以上过程直至样本点归入的簇不再变动或变动范围极小

可视化演示:

  • 在每次完成聚类之后,生成图片并保存。
  • 最后生成一个k-means.html,其中使用JavaScript逐秒绘制过程图

聚类过程:

不同的颜色代表一个类,黑点代表聚簇点

第一次迭代(随机选点,还未聚类)

进行迭代,更新聚类点

最后一次迭代(按点已经完成聚类)

由于数据太多,可以在k-means.html中查看变化过程 

结果演示:

  • 输出聚簇点

  • 可视化演示

代码:使用了github.com/muesli/kmeans库,我对它的注释进行了翻译,对程序进行了部分修改,方便可视化演示


package main

import (
    "io"
    "os"
    "log"
    "fmt"
    "bufio"
    "io/ioutil"
    "strings"
    "strconv"
    "github.com/leeli73/go-kmeans-html-plotter/clusters"
    "github.com/leeli73/go-kmeans-html-plotter/kmeans"
)
// 读取数据
func InitData() clusters.Observations{
    fi, err := os.Open("data/iris.dat")
    if err != nil {
        log.Fatalln(err)
        return nil
    }
    defer fi.Close()

    var d clusters.Observations
    br := bufio.NewReader(fi)
    for {
        a, _, c := br.ReadLine()
        if c == io.EOF {
            break
        }
        temp := string(a)
        if temp[0] != '@'{
            data := strings.Split(temp,", ")
            num1,_ := strconv.ParseFloat(data[0], 64)
            num2,_ := strconv.ParseFloat(data[1], 64)
            num3,_ := strconv.ParseFloat(data[2], 64)
            num4,_ := strconv.ParseFloat(data[3], 64)
            d = append(d,clusters.Coordinates{
                num1,
                num2,
                num3,
                num4,
            })
        }
    }
    return d
}
func main() {
    d := InitData()
    //定义一个k-means
    km, _ := kmeans.New(0.01, kmeans.SimplePlotter{}) 
    //进行聚类运算
    clusters,files, _ := km.Partition(d, 4)

    //生成演示html
    strfiles := """ + files[0] + "","
    for i:=1;i<len(files) - 1;i++{
        strfiles = strfiles + """ + files[i] + "","
    }
    strfiles = strfiles + """ + files[len(files)-1] + """

    clusterout := []string{}
    for i, c := range clusters {
        log.Printf("Cluster: %d %+v", i, c.Center)
        str := fmt.Sprintf("Cluster: %d %+v", i, c.Center)
        clusterout = append(clusterout,str)
    }

    clustersstr := """ + clusterout[0] + "","
    for i:=1;i<len(clusterout)-1;i++{
        clustersstr = clustersstr + """ + clusterout[i] + "","
    }
    clustersstr = clustersstr + """ +clusterout[len(clusterout)-1] + """
    var Data,err = ioutil.ReadFile("data/web.html")
    if err != nil{
        log.Fatal(err)
    }
    html := string(Data)
    html = strings.Replace(html,"{{images}}",strfiles,-1)
    html = strings.Replace(html,"{{clusters}}",clustersstr,-1)
    ioutil.WriteFile("k-means.html",[]byte(html), 0644)
}


 
k-means.html
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Bootstrap 4 Website Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css">
    <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"></script>
    <script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script>
</head>

<body>

    <div class="container" style="margin-top:30px">
        <div class="row">
            <div class="col-sm-6">
                <h2>聚类点</h2>
                <div style="height: 500px">
                    <textarea style=" 100%; height: 100%" id="clusters"></textarea>
                </div>
            </div>
            <div class="col-sm-6">
                <h2>k-means聚类过程</h2>
                <div style="height: 500px">
                    <canvas id="show" style=" 100%;height: 100%"></canvas>
                </div>
            </div>
        </div>
    </div>

</body>
<script>
    Images = [{{images}}]
    Clusters = [{{clusters}}]
    window.onload = function () {
        var CANVAS = document.getElementById('show');
        context = CANVAS.getContext('2d');
        var ratio = getPixelRatio(context)
        var img = new Image();
        SetClusters()
        img.onload = function () {
            context.drawImage(img, 0, 0, 300 * ratio,150 * ratio);
        }
        var count = 0
        var interval = setInterval(function(){
            if(count < Images.length)
            {
                img.src = Images[count]
            }
            else
            {
                clearInterval(interval);
                return
            }
            count = count + 1
        },1000)
    }
    function getPixelRatio(context) {
        var backingStore = context.backingStorePixelRatio ||
            context.webkitBackingStorePixelRatio ||
            context.mozBackingStorePixelRatio ||
            context.msBackingStorePixelRatio ||
            context.oBackingStorePixelRatio ||
            context.backingStorePixelRatio || 1;

        return (window.devicePixelRatio || 1) / backingStore;
    }
    function SetClusters(){
        for(var i=0;i<Clusters.length;i++)
        {
            document.getElementById("clusters").value = document.getElementById("clusters").value + Clusters[i] + '
'
        }
    }
</script>

</html>

项目地址:https://github.com/leeli73/go-kmeans-html-plotter.git

kmeans项目地址:https://github.com/muesli/kmeans.git

 

原文地址:https://www.cnblogs.com/lee-li/p/11016730.html