算法描述
(1)任意选择k个数据对象作为初始聚类中心
(2)根据簇中对象的平均值,将每个对象赋给最类似的簇
(3)更新簇的平均值,即计算每个对象簇中对象的平均值
(4)计算聚类准则函数E
(5)重复2-4步骤,直到准则函数E值不再进行变化
代码
public class Cluster {public String clusterName; // 类簇名private Medoid medoid; // 类簇的质点private ArrayList<DataPoint> dataPoints; // 类簇中各样本点public Cluster(String clusterName) {this.clusterName = clusterName;this.medoid = null; // will be set by calling setCentroid()dataPoints = new ArrayList<DataPoint>();}public void setMedoid(Medoid c) {medoid = c;}public Medoid getMedoid() {return medoid;}public void addDataPoint(DataPoint dp) { // called from CAInstancedp.setCluster(this);// 标注该类簇属于某点,计算欧式距离this.dataPoints.add(dp);}public void removeDataPoint(DataPoint dp) {this.dataPoints.remove(dp);}public int getNumDataPoints() {return this.dataPoints.size();}public DataPoint getDataPoint(int pos) {return (DataPoint) this.dataPoints.get(pos);}public String getName() {return this.clusterName;}public ArrayList<DataPoint> getDataPoints() {return this.dataPoints;}
}
public class ClusterAnalysis {public Cluster[] clusters;// 所有类簇private int miter;// 迭代次数private ArrayList<DataPoint> dataPoints = new ArrayList<DataPoint>();// 所有样本点private int dimNum;//维度public ClusterAnalysis(int k, int iter, ArrayList<DataPoint> dataPoints,int dimNum) {clusters = new Cluster[k];// 类簇种类数for (int i = 0; i < k; i++) {clusters[i] = new Cluster(i+"");}this.miter = iter;this.dataPoints = dataPoints;this.dimNum=dimNum;}public int getIterations() {return miter;}public ArrayList<DataPoint>[] getClusterOutput() {ArrayList<DataPoint> v[] = new ArrayList[clusters.length];for (int i = 0; i < clusters.length; i++) {v[i] = clusters[i].getDataPoints();}return v;}public void startAnalysis(double[][] medoids) {setInitialMedoids(medoids);double[][] newMedoids=medoids;double[][] oldMedoids=new double[medoids.length][this.dimNum];while(!isEqual(oldMedoids,newMedoids)){for(int m = 0; m < clusters.length; m++){//每次迭代开始情况各类簇的点clusters[m].getDataPoints().clear();}for (int j = 0; j < dataPoints.size(); j++) {int clusterIndex=0;double minDistance=Double.MAX_VALUE;for (int k = 0; k < clusters.length; k++) {//判断样本点属于哪个类簇double eucDistance=dataPoints.get(j).testEuclideanDistance(clusters[k].getMedoid());if(eucDistance<minDistance){minDistance=eucDistance;clusterIndex=k;}}//将该样本点添加到该类簇clusters[clusterIndex].addDataPoint(dataPoints.get(j));}for(int m = 0; m < clusters.length; m++){clusters[m].getMedoid().calcMedoid();//重新计算各类簇的质点}for(int i=0;i<medoids.length;i++){for(int j=0;j<this.dimNum;j++){oldMedoids[i][j]=newMedoids[i][j];}}for(int n=0;n<clusters.length;n++){newMedoids[n]=clusters[n].getMedoid().getDimensioin();}this.miter++;}}private void setInitialMedoids(double[][] medoids) {for (int n = 0; n < clusters.length; n++) {Medoid medoid = new Medoid(medoids[n]);clusters[n].setMedoid(medoid);medoid.setCluster(clusters[n]);}}private boolean isEqual(double[][] oldMedoids,double[][] newMedoids){boolean flag=false;for(int i=0;i<oldMedoids.length;i++){for(int j=0;j<oldMedoids[i].length;j++){if(oldMedoids[i][j]!=newMedoids[i][j]){return flag;}}}flag=true;return flag;}
}
public class DataPoint {private double dimension[]; //样本点的维度private String pointName; //样本点名字private Cluster cluster; //类簇private double euDt;//样本点到质点的距离public DataPoint(double dimension[], String pointName) {this.dimension = dimension;this.pointName = pointName;this.cluster = null;}@Overridepublic String toString() {String result = "Point_id=" + pointName + " [";for (int i = 0; i < dimension.length; i++) {result += String.format("%.2f",dimension[i]) + " ";}return result.trim()+"] clusterId: "+cluster.clusterName;}public void setCluster(Cluster cluster) {this.cluster = cluster;}public double calEuclideanDistanceSum() {double sum=0.0;Cluster cluster=this.getCluster();ArrayList<DataPoint> dataPoints=cluster.getDataPoints();for(int i=0;i<dataPoints.size();i++){double[] dims=dataPoints.get(i).getDimensioin();for(int j=0;j<dims.length;j++){double temp=Math.pow((dims[j]-this.dimension[j]),2);sum=sum+temp;}}return Math.sqrt(sum);}public double testEuclideanDistance(Medoid c) {double sum=0.0;double[] cDim=c.getDimensioin();for(int i=0;i<dimension.length;i++){double temp=Math.pow((dimension[i]-cDim[i]),2);sum=sum+temp;}return Math.sqrt(sum);}public double[] getDimensioin() {return this.dimension;}public Cluster getCluster() {return this.cluster;}public double getCurrentEuDt() {return this.euDt;}public String getPointName() {return this.pointName;}
}
public class Medoid{private double dimension[]; // 质点的维度private Cluster cluster; //所属类簇private double etdDisSum;//Medoid到本类簇中所有的欧式距离之和public String toString() {String result =" [";DecimalFormat decimalFormat=new DecimalFormat("0.000000");for (int i = 0; i < dimension.length; i++) {result += decimalFormat.format(dimension[i]) + " ";}return result.trim()+"] clusterId: "+cluster.clusterName;}public Medoid(double dimension[]) {this.dimension = dimension;}public void setCluster(Cluster c) {this.cluster = c;}public double[] getDimensioin() {return this.dimension;}public Cluster getCluster() {return this.cluster;}public void calcMedoid() {// 取代价最小的点calcEtdDisSum();double minEucDisSum = this.etdDisSum;ArrayList<DataPoint> dps = this.cluster.getDataPoints();for (int i = 0; i < dps.size(); i++) {double tempeucDisSum = dps.get(i).calEuclideanDistanceSum();if (tempeucDisSum < minEucDisSum) {dimension = dps.get(i).getDimensioin();minEucDisSum=tempeucDisSum;}}}// 计算该Medoid到同类簇所有样本点的欧斯距离和private void calcEtdDisSum() {double sum=0.0;Cluster cluster=this.getCluster();ArrayList<DataPoint> dataPoints=cluster.getDataPoints();for(int i=0;i<dataPoints.size();i++){double[] dims=dataPoints.get(i).getDimensioin();for(int j=0;j<dims.length;j++){double temp=Math.abs(dims[j]-this.dimension[j]);sum=sum+temp;}}etdDisSum= sum;}
}
public class TestMain {public static List<double[]> readTxt(String fileName){List<double[]> list=new ArrayList<>();try {File filename = new File(fileName); // 读取input.txt文件InputStreamReader reader = new InputStreamReader(new FileInputStream(filename)); // 建立一个输入流对象readerBufferedReader br = new BufferedReader(reader);String line = "";line = br.readLine();while (true) {line = br.readLine();if(line==null) break;String[] temp=line.split(",");double[] c=new double[temp.length];for(int i=0;i<temp.length;i++){c[i]=Float.parseFloat(temp[i]);}list.add(c);}} catch (Exception e) {e.printStackTrace();}return list;}public static void writeTxt(String content){try { // 防止文件建立或读取失败,用catch捕捉错误并打印,也可以throw/* 读入TXT文件 */File writename = new File("src/km/output.txt"); // 相对路径,如果没有则要建立一个新的output。txt文件BufferedWriter out = new BufferedWriter(new FileWriter(writename));out.write(content); // \r\n即为换行out.flush(); // 把缓存区内容压入文件out.close(); // 最后记得关闭文件} catch (Exception e) {e.printStackTrace();}}public static void main (String args[]){ArrayList<DataPoint> dataPoints = new ArrayList<DataPoint>();List<double[]> list=readTxt("src/km/t2.txt");for(int i=0;i<list.size();i++){dataPoints.add(new DataPoint(list.get(i),String.valueOf(i)));}long s=System.currentTimeMillis();ClusterAnalysis ca=new ClusterAnalysis(5,200,dataPoints,5);double[][] cen={list.get(22),list.get(3),list.get(45),list.get(156),list.get(96)};ca.startAnalysis(cen);StringBuilder stringBuilder=new StringBuilder();ArrayList<DataPoint>[] v = ca.getClusterOutput();System.out.println("K-中心点聚类算法运行时间"+(System.currentTimeMillis()-s)+"ms");for (int ii=0; ii<v.length; ii++){ArrayList tempV = v[ii];stringBuilder.append("\n").append("-----------Cluster").append(ii).append("---------").append("\n");stringBuilder.append("Mid_Point: ").append(ca.clusters[ii].getMedoid()).append(" Points_num: "+ca.clusters[ii].getDataPoints().size()).append("\n");System.out.println(ca.clusters[ii].getMedoid()+" Points_num: "+ca.clusters[ii].getDataPoints().size());Iterator iter = tempV.iterator();while(iter.hasNext()){DataPoint dpTemp = (DataPoint)iter.next();stringBuilder.append(dpTemp).append("\n");}}writeTxt(stringBuilder.toString());}}