生信算法AI

K-Means算法

2019-02-18  本文已影响51人  任嘉平生愿

定义: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 聚类算法 - 匠心十年 - 博客园

上一篇 下一篇

猜你喜欢

热点阅读