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

来源:互联网 发布:mac无法拷贝文件到u盘 编辑:程序博客网 时间:2024/06/09 01:24

一个用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 0
原创粉丝点击