k-means聚类算法的java实现

来源:互联网 发布:网络最热门表情包 编辑:程序博客网 时间:2024/06/06 09:36
      聚类分析是一种重要的人类行为,早在孩提时代,一个人就通过不断改进下意识中的聚类模式来学会如何区分猫狗、动物植物。目前在许多领域都得到了广泛的研究和成功的应用,如用于模式识别、数据分析、图像处理、市场研究、客户分割、Web文档分类等[1]。
   聚类就是按照某个特定标准(如距离准则)把一个数据集分割成不同的类或簇,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性也尽可能地大。即聚类后同一类的数据尽可能聚集到一起,不同数据尽量分离。

   聚类技术[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

原创粉丝点击