K-Means算法

定义:K-Means 是一种基于距离的排他的聚类划分方法。

例如:身高体重划分

image

由于 K-Means 算法值针对给定的完整数据集进行操作,不需要任何特殊的训练数据

所以 K-Means 是一种无监督的机器学习

K-Means 实现

image

以身高和体重为例子,开始确定3个K点(大圆点),然后计算与K 的距离然后将多个数据集划分。

k 的选择一般基于经验值和多次实验结果。例如1.5米50kg算矮标准,1.5米60kg算矮胖。

算法实现

具体的算法步骤如下:

1.随机选择K个中心点
2.把每个数据点分配到离它最近的中心点;
3.重新计算每类中的点到该类中心点距离的平均值
4.分配每个数据到它最近的中心点;
5.重复步骤3和4,直到所有的观测值不再被分配或是达到最大的迭代次数(R把10次作为默认迭代次数)。

java代码实现
package KMeans;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;


public class MyKmeans {

    static Vector<Point>  li=new Vector<Point>();
    //static List<Point>  li=new ArrayList<Point>();
    static List<Vector<Point>> list=new ArrayList<Vector<Point>>(); //每次迭代保存结果,一个vector代表一个簇
    private final static Integer K=2; //选K=2,也就是估算有两个簇。
    private final static Double converge=0.001; //当距离小于某个值的时候,就认为聚类已经聚类了,不需要再迭代,这里的值选0.001

    //读取数据
    public static final void readF1() throws IOException {
        String filePath="simple_k-means.txt";
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(filePath);
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        for (String line = br.readLine(); line != null; line = br.readLine()) {
            if(line.length()==0||"".equals(line))continue;
            String[] str=line.split(" ");
            Point p0=new Point();
            p0.setX(Double.valueOf(str[0]));
            p0.setY(Double.valueOf(str[1]));
            li.add(p0);
            //System.out.println(line);
        }
        br.close();
    }
    //math.sqrt(double n)
    //扩展下,如果要给m开n次方就用java.lang.StrictMath.pow(m,1.0/n);
    //采用欧氏距离
    public static  Double DistanceMeasure(Point p1,Point p2){

        Double tmp=StrictMath.pow(p2.getX()-p1.getX(), 2)+StrictMath.pow(p2.getY()-p1.getY(), 2);
        return Math.sqrt(tmp);
    }

    //计算新的簇心
    public static Double CalCentroid(){
        System.out.println("------------------------------------------------");
        Double movedist=Double.MAX_VALUE;
        for(int i=0;i<list.size();i++){
            Vector<Point> subli=list.get(i);
            Point po=new Point();
            Double sumX=0.0;
            Double sumY=0.0;
            Double Clusterlen=Double.valueOf(subli.size());
            for(int j=0;j<Clusterlen;j++){
                Point nextp=subli.get(j);
                sumX=sumX+nextp.getX();
                sumY=sumY+nextp.getY();
            }
            po.setX(sumX/Clusterlen);
            po.setY(sumY/Clusterlen);
            //新的点与旧点之间的距离
            Double dist=DistanceMeasure(subli.get(0),po);
            //在多个簇心移动的过程中,返回移动距离最小的值
            if(dist<movedist)movedist=dist;
            list.get(i).clear();
            list.get(i).add(po);
            System.out.println("C"+i+"的簇心为:"+po.getX()+","+po.getY());
        }
        String test="ll";
        return movedist;
    }
    //本次的簇心
    //下一次移动的簇心

    private static Double move=Double.MAX_VALUE;//移动距离
    //不断地迭代,直到收敛
    public static void RecursionKluster(){
        for(int times=2;move>converge;times++){
            System.out.println("第"+times+"次迭代");
            //默认每一个list里的Vector第0个元素是质心
            for(int i=0;i<li.size();i++){
                Point p=new Point();
                p=li.get(i);
                int index = -1;

                double neardist = Double.MAX_VALUE;
                for(int k=0;k<K;k++){
                    Point centre=list.get(k).get(0);
                    double currentdist=DistanceMeasure(p,centre);
                    if(currentdist<neardist){
                        neardist=currentdist;
                        index=k;
                    }
                }

                System.out.println("C"+index+":的点为:"+p.getX()+","+p.getY());
                list.get(index).add(p);

            }
            //重新计算簇心,并返回移动的距离,最小的那个距离

            move=CalCentroid();
            System.out.println("各个簇心移动中最小的距离为,move="+move);
        }
    }

    public static void Kluster(){

        for(int k=0;k<K;k++){
            Vector<Point> vect=new Vector<Point>();
            Point p=new Point();
            p=li.get(k);
            vect.add(p);
            list.add(vect);
        }
        System.out.println("第1次迭代");
        //默认每一个list里的Vector第0个元素是质心
        for(int i=K;i<li.size();i++){
            Point p=new Point();
            p=li.get(i);
            int index = -1;

            double neardist = Double.MAX_VALUE;
            for(int k=0;k<K;k++){
                Point centre=list.get(k).get(0);
                double currentdist=DistanceMeasure(p,centre);
                if(currentdist<neardist){
                    neardist=currentdist;
                    index=k;
                }
            }

            System.out.println("C"+index+":的点为:"+p.getX()+","+p.getY());
            list.get(index).add(p);

        }

    }

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        //读取数据
        readF1();
        //第一次迭代
        Kluster();
        //第一次迭代后计算簇心
        CalCentroid();
        //不断迭代,直到收敛
        RecursionKluster();
    }

}

package KMeans;


import lombok.Data;

@Data
public class Point {
    double X;
    double Y;
}

simple_k-means.txt

1 1  
2 1  
1 2  
2 2  
3 3  
8 8  
8 9  
9 8  
9 9 

运行结果

第1次迭代
C0:的点为:1.0,2.0
C1:的点为:2.0,2.0
C1:的点为:3.0,3.0
C1:的点为:8.0,8.0
C1:的点为:8.0,9.0
C1:的点为:9.0,8.0
C1:的点为:9.0,9.0
------------------------------------------------
C0的簇心为:1.0,1.5
C1的簇心为:5.857142857142857,5.714285714285714
第2次迭代
C0:的点为:1.0,1.0
C0:的点为:2.0,1.0
C0:的点为:1.0,2.0
C0:的点为:2.0,2.0
C0:的点为:3.0,3.0
C1:的点为:8.0,8.0
C1:的点为:8.0,9.0
C1:的点为:9.0,8.0
C1:的点为:9.0,9.0
------------------------------------------------
C0的簇心为:1.6666666666666667,1.75
C1的簇心为:7.971428571428572,7.942857142857143
各个簇心移动中最小的距离为,move=0.7120003121097943
第3次迭代
C0:的点为:1.0,1.0
C0:的点为:2.0,1.0
C0:的点为:1.0,2.0
C0:的点为:2.0,2.0
C0:的点为:3.0,3.0
C1:的点为:8.0,8.0
C1:的点为:8.0,9.0
C1:的点为:9.0,8.0
C1:的点为:9.0,9.0
------------------------------------------------
C0的簇心为:1.777777777777778,1.7916666666666667
C1的簇心为:8.394285714285715,8.388571428571428
各个簇心移动中最小的距离为,move=0.11866671868496578
第4次迭代
C0:的点为:1.0,1.0
C0:的点为:2.0,1.0
C0:的点为:1.0,2.0
C0:的点为:2.0,2.0
C0:的点为:3.0,3.0
C1:的点为:8.0,8.0
C1:的点为:8.0,9.0
C1:的点为:9.0,8.0
C1:的点为:9.0,9.0
------------------------------------------------
C0的簇心为:1.7962962962962965,1.7986111111111114
C1的簇心为:8.478857142857143,8.477714285714285
各个簇心移动中最小的距离为,move=0.019777786447494432
第5次迭代
C0:的点为:1.0,1.0
C0:的点为:2.0,1.0
C0:的点为:1.0,2.0
C0:的点为:2.0,2.0
C0:的点为:3.0,3.0
C1:的点为:8.0,8.0
C1:的点为:8.0,9.0
C1:的点为:9.0,8.0
C1:的点为:9.0,9.0
------------------------------------------------
C0的簇心为:1.799382716049383,1.7997685185185184
C1的簇心为:8.495771428571429,8.495542857142857
各个簇心移动中最小的距离为,move=0.003296297741248916
第6次迭代
C0:的点为:1.0,1.0
C0:的点为:2.0,1.0
C0:的点为:1.0,2.0
C0:的点为:2.0,2.0
C0:的点为:3.0,3.0
C1:的点为:8.0,8.0
C1:的点为:8.0,9.0
C1:的点为:9.0,8.0
C1:的点为:9.0,9.0
------------------------------------------------
C0的簇心为:1.7998971193415638,1.7999614197530864
C1的簇心为:8.499154285714287,8.499108571428572
各个簇心移动中最小的距离为,move=5.49382956874724E-4

Process finished with exit code 0

K-Means 聚类算法 - 匠心十年 - 博客园

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容