您的位置:首页 > 编程语言 > Java开发

k-means聚类java实现

2015-05-27 20:42 225 查看

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

以前做项目时候写的代码,数据是一维的,多维的也一样,把距离计算的改一改就行int term = Math.abs(dotlist.get(centerIndex[j]).x- dotlist.get(i).x);

[java]
view plaincopy

package uestc.dmlab.call;

import java.io.BufferedReader;
import java.io.FileReader;
import java.security.KeyStore.Entry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class Clustering {

/**
*
* @param fileName
* 文件中每个字段对应一个概率
* @param k
* 聚成k个类
* @param minDistance
* 聚类中心位移小于minDistance时停止迭代
* @return
*/
public static HashMap<String, Integer> cluster(String fileName, int k,
int minDistance) {
try {
BufferedReader br = new BufferedReader(new FileReader(fileName));
List<Dot> dotlist = new LinkedList<Dot>();
String line;
int count = 0;// 行数
while ((line = br.readLine()) != null) {
String s[] = line.split(",");
Dot dot = new Dot();
dot.isCenter = false;
dot.isVirtual = false;
dot.name = s[0];
// if(s.length<4){
// System.out.println(line);
// }
dot.x = Integer.parseInt(s[3]);
dotlist.add(dot);
count++;
}
if (count < k) {
k = count;
}

// 随机初始化k个聚类中心
int centerIndex[] = new int[k]; // 存储k个中心点在dotlist中的索引
int centerNum = k;
while (centerNum > 0) {
int index = new Random().nextInt(count);
if (!dotlist.get(index).isCenter) {
centerNum--;
dotlist.get(index).isCenter = true;
centerIndex[centerNum] = index;
}
}

// K个聚类
Cluster[] clusers = new Cluster[k];

boolean flag = true;
while (flag) {
flag = false;
clusers = new Cluster[k];
for (int i = 0; i < clusers.length; i++) {
clusers[i] = new Cluster();
}
//System.out.println(clusers.length);
// 找到离第i个点最近的聚类中心
for (int i = 0; i < dotlist.size(); i++) {
// 该点不是中心点也不是虚拟点就计算它与所有中心点的距离并取最小值
// if(!dotlist.get(i).isCenter&&!dotlist.get(i).isVirtual){
if (!dotlist.get(i).isVirtual) {
int distance = Integer.MAX_VALUE;
int c = 0;// 记录离该节点最近的中心点的索引
for (int j = 0; j < k; j++) {
int term = Math.abs(dotlist.get(centerIndex[j]).x
- dotlist.get(i).x);
if (distance > term) {
distance = term;
c = j;
}
}
clusers[c].dots.add(i);
}
}

// 重新计算聚类中心
for (int i = 0; i < k; i++) {
Cluster cluster = clusers[i];

if (cluster.dots.size() > 0) { //若该类中有点
int sum = 0;
for (int j = 0; j < cluster.dots.size(); j++) {
sum += dotlist.get(cluster.dots.get(j)).x;
}

Dot dot = new Dot();
dot.x = sum / cluster.dots.size();
dot.isCenter = true;
dot.isVirtual = true;

// 新旧聚类中心的距离
int term = Math.abs(dotlist.get(centerIndex[i]).x
- dot.x);
if (term > minDistance)
flag = true;

dotlist.add(dot);
centerIndex[i] = dotlist.indexOf(dot); // 第i个聚类的中心改变
}
}
}

// 生成分类映射
HashMap<String, Integer> map = new HashMap<String, Integer>();
for (Dot dot : dotlist) {
if (dot.isVirtual == false) {
int className = -1;
for (int i = 0; i < k; i++) {
if (clusers[i].dots.contains(dotlist.indexOf(dot)))
className = i;
}
map.put(dot.name, className);
}
}
return map;
} catch (Exception e) {
e.printStackTrace();
}
return new HashMap<String, Integer>();

}

public static void main(String[] args) {
Map<String, Integer> map = Clustering.cluster(
"C:/Documents and Settings/Administrator/桌面/123.txt", 2, 0);
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, Integer> entry = it.next();
System.out.println(entry.getKey()+","+entry.getValue());
}

}
}

class Dot {
String name;
int x;
boolean isCenter;
boolean isVirtual;
}

class Cluster {
// 记录了该类中点的索引值
LinkedList<Integer> dots = new LinkedList<Integer>();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: