Advertisement

数据挖掘k-means聚类算法JAVA模拟

阅读量:

一个用java实现的模拟数据挖掘算法k-means的demo。因为老湿说期末如果能自己编写程序模拟课本中的任意算法,通过答辩就可以不用考试。对于我这种不规矩的学生,能不考试当然选择不考了。

进入正题:k-means聚类算法,简单点说就是给定N个坐标点,然后在其中选择k个点做为k个簇的初始中心点,然后计算所有点到各个簇的中心点的距离,将点分配到最近的簇,然后重新计算k个簇的新中心点,然后继续迭代,直到k个簇的中心点不再变化。

先来说下数据结构:

首先定义一个类用于表示坐标点,并且定义和点相关的方法:

复制代码
 //定义一个类用于表示每个坐标点

    
 class Point{
    
 	String name;
    
 	double x,y;
    
 	public Point(String name,double x,double y){
    
 		this.name=name;
    
 		this.x=x;
    
 		this.y=y;
    
 	}
    
 	public Point(){
    
 	}
    
  //计算两个坐标点之间的距离
    
 	public static double distance(Point a,Point b){
    
 		return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    
 	}	
    
  //计算每个坐标点距离哪个簇最近
    
 	public static int ClusterDistance(Point p,ArrayList<Cluster> array){
    
     int a=0;
    
 		for(int i=0;i<array.size();i++){
    
 			if(Point.distance(p,array.get(a).center)>Point.distance(p, array.get(i).center)){
    
 				a=i;
    
 			}
    
 		}
    
 		return a;	
    
 	}
    
  //将double数组中的所有坐标点存入Point数组中
    
    public static void PointAdd(ArrayList<Point> PointArray,  ArrayList<double[]> DoubleArray){
    
 	  for(int i=0;i<DoubleArray.size();i++){
    
 		  PointArray.add(new Point("p"+(i+1),DoubleArray.get(i)[0],DoubleArray.get(i)[1]));
    
 	  }
    
    }
    
 }
    
    
    
    

定义一个类用于表示簇以及相关的方法:

复制代码
  //定义一个类用于表示簇

    
 class Cluster{
    
  public Cluster(double x,double y){
    
 	 center.x=x;
    
 	 center.y=y;
    
  }
    
  //计算簇的新中心点坐标
    
  public static void NewCenter(Cluster cluster){
    
 	  double sumx=0,sumy=0;
    
 	  int i=0;
    
 	  for(Point ex:cluster.Array){
    
 		  sumx+=ex.x;
    
 		  sumy+=ex.y;
    
 		  i++;
    
 	  }
    
 	 if(cluster.center.x!=sumx/i||cluster.center.y!=sumy/i){
    
 	 cluster.center.x=sumx/i;
    
 	 cluster.center.y=sumy/i;
    
 	 cluster.changed=true;
    
 	 }
    
 	 else if(cluster.center.x==sumx/i&&cluster.center.y==sumy/i){
    
 		 cluster.changed=false;
    
 	 }
    
  }
    
 //判断所有簇的中心点是否不再发生变化
    
  public static boolean Changing(ArrayList<Cluster> array){
    
 	 boolean ex=false;
    
 	 for(Cluster e:array){
    
 		 if(e.changed==true){
    
 			 ex=true;
    
 		 }
    
 	 }
    
 	 return ex;
    
 	 
    
 	 
    
  }
    
  Point center=new Point();//簇中心点
    
  ArrayList<Point> Array=new ArrayList<Point>();	//簇中的坐标元素
    
  boolean changed=true;//用于判断该簇的中心点坐标是否发生变化
    
 }
    
    
    
    

因为初始的时候要从n个点里随机选择k点作为k个簇的中心点,所以定义一个随机选择函数:

复制代码
 //用于获取1~n之间的t个不重复的整数

    
 class GetRandom{
    
 	@SuppressWarnings("unchecked")
    
 	static public int[] fn(int n,int t)
    
 	{
    
 	    ArrayList numbers=new ArrayList();
    
 	    int[] number=new int[t];
    
 	    for(int i=0;i<n;i++){  //初始化数组
    
 	       numbers.add(i+1);
    
 	    } 
    
 	    for(int j=0;j<t;j++){
    
 	       int raNum=(int)(Math.random()*numbers.size());
    
 	       number[j]=Integer.parseInt(numbers.get(raNum).toString());
    
 	       numbers.remove(raNum);
    
 	    }
    
 	    return number;
    
 	}
    
 }
    
    
    
    

对于一些各个类都要用到的全局变量,我喜欢将其定义为一个类的静态成员,这样可以直接通过类名来调用。(不知道是不是一个坏习惯=.=)

复制代码
 //存放全局变量的类

    
 class Pub{
    
 	static StringBuffer str=new StringBuffer();//存放textArea的内容
    
 	static StringBuffer str1=new StringBuffer();//存放textArea1的内容
    
 	static ArrayList<Point> TotalArray=new ArrayList<Point>();  //定义用于存放坐标点的泛型数组
    
 	static ArrayList<Cluster> ClusterArray=new ArrayList<Cluster>();   //定义用于存放各个簇的点的泛型数组
    
 	static DrawPanel xyPanel=new DrawPanel();//坐标轴面板
    
 	static ArrayList<Color> ColorArray=new ArrayList<Color>();//存放颜色的泛型数组
    
 }
    
    
    
    

接下来是图形界面,话说java的图形界面编写实在是有点**,虽然可以装一些可视化的插件来编辑,但还是不习惯(其实是我不会用T.T),不过自己用代码编写也有一个好处,就是可以把握各个细节,方便修改。

程序主要有3大面板(JPanel),一个是textArea1,用于显示所有坐标点,一个是textArea,用于显示算法的结果,还有一个是xyPanel,用于显示坐标轴和点。

算法部分:

算法部分主要写在按钮的监听事件中,当按下按钮时,执行相应的算法并把结果显示在对应的面板中,每次执行完算法都刷新一下xyPanel,更新坐标轴上的点。

复制代码
  //例题模拟按钮监听事件

    
    private class ExButtonListener implements ActionListener{
    
 	   public void actionPerformed(ActionEvent event){
    
 	   Pub.TotalArray.clear();
    
        Pub.TotalArray.add(new Point("p1",3,4));
    
        Pub.TotalArray.add(new Point("p2",3,6));
    
        Pub.TotalArray.add(new Point("p3",7,3));
    
        Pub.TotalArray.add(new Point("p4",4,7));
    
        Pub.TotalArray.add(new Point("p5",3,8));
    
        Pub.TotalArray.add(new Point("p6",8,5));
    
        Pub.TotalArray.add(new Point("p7",4,5));
    
        Pub.TotalArray.add(new Point("p8",4,1));
    
        Pub.TotalArray.add(new Point("p9",7,4));
    
        Pub.TotalArray.add(new Point("p10",5,5));
    
 	   Pub.str1.delete(0, Pub.str1.length());
    
 		  for(Point ex:Pub.TotalArray){
    
 			  Pub.str1.append(" "+ex.name+" ("+ex.x+","+ex.y+")\n");
    
 		  }
    
 		  textArea1.setText(Pub.str1.toString());
    
 		  textArea1.setFont(new Font("Serif",0,16));
    
 		  textArea1.setEditable(false);
    
 		  textArea.setText("");
    
 		  Pub.ClusterArray.clear();
    
 		  Pub.xyPanel.repaint();
    
 		  int k=2;
    
 			//在所有元素的数组里随机选择k个坐标点用于代表初始簇中心
    
 			Pub.ClusterArray.clear();
    
 			Pub.ClusterArray.add(new Cluster(4,5));
    
 			Pub.ClusterArray.add(new Cluster(5,5));
    
 			Pub.str.append("初始随机选择的"+k+"个簇中心点的坐标为:\n");
    
 			for(Cluster ex:Pub.ClusterArray){
    
 				Pub.str.append("("+ex.center.x+","+ex.center.y+")"+"\n");
    
 			}
    
 			for(Point ex:Pub.TotalArray){
    
 				Pub.ClusterArray.get(Point.ClusterDistance(ex,Pub.ClusterArray)).Array.add(ex);
    
 			}
    
 			if(Pub.TotalArray.size()!=k||k==1){
    
 			for(Cluster ex:Pub.ClusterArray){
    
 				Cluster.NewCenter(ex);
    
 			}}
    
 			int m=1;
    
 			ArrayList<Point> PointArray=new ArrayList<Point>();//用于存放要删除的点的数组
    
 			while(Cluster.Changing(Pub.ClusterArray)!=false){
    
 				  Pub.str.append("第"+(m++)+"次迭代的结果为:√\n");
    
 				    int ii=1;
    
 					for(Cluster ex:Pub.ClusterArray){
    
 						Pub.str.append("第"+(ii++)+"个簇的所有点为:\n");
    
 						for(Point p:ex.Array){
    
 							Pub.str.append(p.name+"("+p.x+","+p.y+")"+"\n");
    
 						}
    
 						Pub.str.append("新的中心点坐标为:"+"("+String.format("%.3f",ex.center.x)+","+String.format("%.3f",ex.center.y)+")"+"\n");
    
 						Pub.str.append("---------------------------------------------------------------\n");
    
 					}
    
 			      for(int j=0;j<k;j++){
    
 			    	  for(Point t:Pub.ClusterArray.get(j).Array){
    
 			    		  if(Point.ClusterDistance(t,Pub.ClusterArray)!=j){
    
 			    			  Pub.ClusterArray.get(Point.ClusterDistance(t, Pub.ClusterArray)).Array.add(t);
    
 			    			  PointArray.add(t);
    
 			    		  }
    
 			    	  }
    
 			    	  Pub.ClusterArray.get(j).Array.removeAll(PointArray);
    
 			    	  PointArray.clear();
    
 			      }
    
 			    for(Cluster ex:Pub.ClusterArray){
    
 			    	Cluster.NewCenter(ex);
    
 			    }	
    
 			}	
    
 		 textArea.setText(Pub.str.toString());
    
 		 textArea.setEditable(false);
    
 		 Pub.str.delete(0, Pub.str.length());
    
 		 Pub.xyPanel.repaint(); 
    
 		   
    
 	   }
    
    }
    
    
    
    

总体来讲,算法部分比较简单,其实就是比较点和点之间的距离和点的存储而已。这个程序用到了增强的for循环,泛型数组以及比较细节的一些小知识点。写完这个程序,发现书中的知识如此重要,有时候虽然只是一个小小的知识点,却可能在程序中发挥巨大作用。基础还是要继续加强!!!

最后附上程序的截图和源程序网盘地址:

此demo是用Eclipse写的,导出的项目文件的网盘地址:

http://pan.baidu.com/s/1i3GD8IL

全部评论 (0)

还没有任何评论哟~