k-means聚類JAVA實例


《mahout in action》第六章。

datafile/cluster/simple_k-means.txt數據集如下:

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

1. k-means聚類算法原理


1、從D中隨機取k個元素,作為k個簇的各自的中心。


2、分別計算剩下的元素到k個簇中心的相異度,將這些元素分別划歸到相異度最低的簇。


3、根據聚類結果,重新計算k個簇各自的中心,計算方法是取簇中所有元素各自維度的算術平均數。


4、將D中全部元素按照新的中心重新聚類。


5、重復第4步,直到聚類結果不再變化。


6、將結果輸出。

2. 舉例說明


2.1 從D中隨機取k個元素,作為k個簇的各自的中心。

private final static Integer K=2; //選K=2,也就是估算有兩個簇。
下面選1 1,2,1兩個點。
C0:1 1
C1:2 1

2.2 分別計算剩下的元素到k個簇中心的相異度,將這些元素分別划歸到相異度最低的簇。

結果為:
C0 : 1 1
C0:的點為:1.0,2.0
C1:  2 1
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



2.3 根據2.2的聚類結果,重新計算k個簇各自的中心,計算方法是取簇中所有元素各自維度的算術平均數。

采取歐區距離公式。
C0 新的簇心為:1.0,1.5
C1 新的簇心為:5.857142857142857,5.714285714285714

2.4 將D中全部元素按照新的中心重新聚類。

第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


2.5  重復第4步,直到聚類結果不再變化。

當距離小於某個值的時候,就認為聚類已經聚類了,不需要再迭代,這里的值選0.001
private final static Double converge=0.001;

------------------------------------------------
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

3. JAVA實現

package mysequence.machineleaning.clustering.kmeans;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import mysequence.machineleaning.clustering.canopy.Point;

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="datafile/cluster/simple_k-means.txt";
		BufferedReader br = new BufferedReader(new InputStreamReader(
        new FileInputStream(filePath)));
        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();
	}

}

4.運行結果:

C0:1 1
C1:2 1
第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

版權聲明:本文為博主原創文章,未經博主允許不得轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM