聚类就是按照某个特定标准(如距离准则)把一个数据集分割成不同的类或簇,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性也尽可能地大。即聚类后同一类的数据尽可能聚集到一起,不同数据尽量分离。
聚类技术[2]正在蓬勃发展,对此有贡献的研究领域包括数据挖掘、统计学、机器学习、空间数据库技术、生物学以及市场营销等。各种聚类方法也被不断提出和改进,而不同的方法适合于不同类型的数据,因此对各种聚类方法、聚类效果的比较成为值得研究的课题。
在数据分析的术语之中,聚类和分类是两种技术。分类是指我们已经知道了事物的类别,需要从样品中学习分类的规则,是一种有指导学习;而聚类则是由我们来给定简单的规则,从而得到分类,是一种无指导学习。两者可以说是相反的过程。
K-means聚类算法过程如下:
1、选取K个点作为初始质心(随机);
2、对每个样本分别计算到K个质心的相似度或距离,将该样本划分到相似度最高或距离最短的质心所在类;
3、对该轮计算结果,计算每一个类别的质心,新的质心作为下一轮的质心;
4、判断算法是否满足终止条件(即:前后两个质心数组中前一组的质心是否在后一个质心组出现),满足则结束,否则继续2、3、4;
测试文本如下:
A 2 3
B 2 7
C 1 2
D 1 6
E 2 1
F 3 5
G 8 5
H 9 6
I 7 7
J 7 4
K 8 2
L 8 22
M 8 19
N 7 21
O 7 17
P 9 20
代码实现n个点的聚类:
public class Point {
private double X;
private double Y;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getX() {
return X;
}
public void setX(double x) {
X = x;
}
public double getY() {
return Y;
}
public void setY(double y) {
Y = y;
}
@Override
public boolean equals(Object obj) {
Point point = (Point) obj;
if (this.getX() == point.getX() && this.getY() == point.getY()) {
return true;
}
return false;
}
@Override
public String toString() {
return "(" + X + "," + Y + ")";
}
@Override
public int hashCode() {
return (int) (X+Y);
}
}
K-means聚类如下:
public class KmeansClustering {
private List<Point> dataset = null;
public KmeansClustering() throws IOException {
initDataSet();
}
/**
* 初始化数据集
*
* @throws IOException
*/
private void initDataSet() throws IOException {
dataset = new ArrayList<Point>();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(KmeansClustering.class.getClassLoader()
.getResourceAsStream("data.txt")));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
String[] s = line.split("\t");
Point point = new Point();
point.setX(Double.parseDouble(s[1]));
point.setY(Double.parseDouble(s[2]));
point.setName(s[0]);
dataset.add(point);
}
}
/**
* @param k
* 聚类的数目
*/
public Map<Point, List<Point>> kcluster(int k) {
// 随机从样本集合中选取k个样本点作为聚簇中心
// 每个聚簇中心有哪些点
Map<Point, List<Point>> nowClusterCenterMap = new HashMap<Point, List<Point>>();
for (int i = 0; i < k; i++) {
Random random = new Random();
int num = random.nextInt(dataset.size());
nowClusterCenterMap.put(dataset.get(num), new ArrayList<Point>());
}
// 上一次的聚簇中心
Map<Point, List<Point>> lastClusterCenterMap = null;
// 找到离中心最近的点,然后加入以该中心为map键的list中
while (true) {
for (Point point : dataset) {
double shortest = Double.MAX_VALUE;
Point key = null;
for (Entry<Point, List<Point>> entry : nowClusterCenterMap.entrySet()) {
double distance = distance(point, entry.getKey());
if (distance < shortest) {
shortest = distance;
key = entry.getKey();
}
}
nowClusterCenterMap.get(key).add(point);
}
// 如果结果与上一次相同,则整个过程结束
if (isEqualCenter(lastClusterCenterMap, nowClusterCenterMap)) {
break;
}
lastClusterCenterMap = nowClusterCenterMap;
nowClusterCenterMap = new HashMap<Point, List<Point>>();
// 把中心点移到其所有成员的平均位置处,并构建新的聚簇中心
for (Entry<Point, List<Point>> entry : lastClusterCenterMap
.entrySet()) {
nowClusterCenterMap.put(getNewCenterPoint(entry.getValue()),
new ArrayList<Point>());
}
}
return nowClusterCenterMap;
}
/**
* 判断前后两次是否是相同的聚簇中心,若是则程序结束,否则继续,直到相同
*
* @param lastClusterCenterMap
* @param nowClusterCenterMap
* @return bool
*/
private boolean isEqualCenter(Map<Point, List<Point>> lastClusterCenterMap, Map<Point, List<Point>> nowClusterCenterMap) {
if (lastClusterCenterMap == null) {
return false;
} else {
for (Entry<Point, List<Point>> entry : lastClusterCenterMap.entrySet()) {
if (!nowClusterCenterMap.containsKey(entry.getKey())) {
return false;
}
}
}
return true;
}
/**
* 计算新的中心
*
* @param value
* @return Point
*/
private Point getNewCenterPoint(List<Point> value) {
double sumX = 0.0, sumY = 0.0;
for (Point point : value) {
sumX += point.getX();
sumY += point.getY();
}
Point point = new Point();
point.setX(sumX / value.size());
point.setY(sumY / value.size());
return point;
}
/**
* 使用欧几里得算法计算两点之间距离
*
* @param point1
* @param point2
* @return 两点之间距离
*/
private double distance(Point point1, Point point2) {
double distance = Math.pow((point1.getX() - point2.getX()), 2) + Math.pow((point1.getY() - point2.getY()), 2);
distance = Math.sqrt(distance);
return distance;
}
public static void main(String[] args) throws IOException {
KmeansClustering kmeansClustering = new KmeansClustering();
Map<Point, List<Point>> result = kmeansClustering.kcluster(3);
for (Entry<Point, List<Point>> entry : result.entrySet()) {
System.out.println("===============聚簇中心为:" + entry.getKey() + "================");
for (Point point : entry.getValue()) {
System.out.println(point.getName());
}
}
}
}
聚类结果每次会受K值的影响,结果可能会产生不同。几个聚类结果如下:
第一次结果:
===============聚簇中心为:(4.545454545454546,4.363636363636363)================
A
B
C
D
E
F
G
H
I
J
K
===============聚簇中心为:(7.5,18.0)================
M
O
===============聚簇中心为:(8.0,21.0)================
L
N
P
第二次结果:
===============聚簇中心为:(1.8333333333333333,4.0)================
A
B
C
D
E
F
===============聚簇中心为:(7.8,19.8)================
L
M
N
O
P
===============聚簇中心为:(7.8,4.8)================
G
H
I
J
K
第三次结果:
===============聚簇中心为:(1.8333333333333333,4.0)================
A
B
C
D
E
F
===============聚簇中心为:(7.8,19.8)================
L
M
N
O
P
===============聚簇中心为:(7.8,4.8)================
G
H
I
J
K