Kmeans算是是聚類中的經典算法,過程如下:
選擇K個點作爲初始質心
repeat
將每個點指派到最近的質心,形成K個簇
重新計算每個簇的質心
until 簇不發生變化或達到最大迭代次數
算法中的K需要人爲的指定。確定K的做法有很多,比如多次進行試探,計算誤差,得出最好的K。這樣需要比較長的時間。我們可以根據Canopy算法來粗略確定K值(可以認爲相等)。看一下Canopy算法的過程:
(1)設樣本集合爲S,確定兩個閾值t1和t2,且t1>t2。
(2)任取一個樣本點p,作爲一個Canopy,記爲C,從S中移除p。
(3)計算S中所有點到p的距離dist
(4)若dist<t1,則將相應點歸到C,作爲弱關聯。
(5)若dist<t2,則將相應點移出S,作爲強關聯。
(6)重複(2)~(5),直至S爲空。
Canopy 個數完全可以作爲這個K值,一定程度上減少了選擇K的盲目性。下面通過Canopy算法對一些點進行計算Canopy的個數,如果僅僅計算K值,則T1沒有任何作用,之用指定T2即可,這裏使用所有點的平均距離的一半來作爲T2.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
package cn.edu.ustc.dm.cluster;
import java.util.ArrayList; import java.util.List; import cn.edu.ustc.dm.bean.Point; /** * Canopy算法 藉助canopy算法計算對應的Kmeans中的K值大小 * 其中對於計算K值來說,canopy算法中的T1沒有意義,只用設定T2(T1>T2) 我們這裏將T2設置爲平均距離 * * @author YD * */ public class Canopy { private List<Point> points = new ArrayList<Point>(); // 進行聚類的點 private List<List<Point>> clusters = new ArrayList<List<Point>>(); // 存儲簇 private double T2 = -1; // 閾值 public Canopy(List<Point> points) { for (Point point : points) // 進行深拷貝 this.points.add(point); } /** * 進行聚類,按照Canopy算法進行計算,將所有點進行聚類 */ public void cluster() { T2 = getAverageDistance(points); while (points.size() != 0) { List<Point> cluster = new ArrayList<Point>(); Point basePoint = points.get(0); // 基準點 cluster.add(basePoint); points.remove(0); int index = 0; while (index < points.size()) { Point anotherPoint = points.get(index); double distance = Math.sqrt((basePoint.x - anotherPoint.x) * (basePoint.x - anotherPoint.x) + (basePoint.y - anotherPoint.y) * (basePoint.y - anotherPoint.y)); if (distance <= T2) { cluster.add(anotherPoint); points.remove(index); } else { index++; } } clusters.add(cluster); } } /** * 得到Cluster的數目 * * @return 數目 */ public int getClusterNumber() { return clusters.size(); } /** * 獲取Cluster對應的中心點(各點相加求平均) * * @return */ public List<Point> getClusterCenterPoints() { List<Point> centerPoints = new ArrayList<Point>(); for (List<Point> cluster : clusters) { centerPoints.add(getCenterPoint(cluster)); } return centerPoints; } /** * 得到的中心點(各點相加求平均) * * @return 返回中心點 */ private double getAverageDistance(List<Point> points) { double sum = 0; int pointSize = points.size(); for (int i = 0; i < pointSize; i++) { for (int j = 0; j < pointSize; j++) { if (i == j) continue; Point pointA = points.get(i); Point pointB = points.get(j); sum += Math.sqrt((pointA.x - pointB.x) * (pointA.x - pointB.x) + (pointA.y - pointB.y) * (pointA.y - pointB.y)); } } int distanceNumber = pointSize * (pointSize + 1) / 2; double T2 = sum / distanceNumber / 2; // 平均距離的一半 return T2; } /** * 得到的中心點(各點相加求平均) * * @return 返回中心點 */ private Point getCenterPoint(List<Point> points) { double sumX = 0; double sumY = 0; for (Point point : points) { sumX += point.x; sumY += point.y; } int clusterSize = points.size(); Point centerPoint = new Point(sumX / clusterSize, sumY / clusterSize); return centerPoint; } /** * 獲取閾值T2 * * @return 閾值T2 */ public double getThreshold() { return T2; } /** * 測試9個點,進行操作 * @param args */ public static void main(String[] args) { List<Point> points = new ArrayList<Point>(); points.add(new Point(0, 0)); points.add(new Point(0, 1)); points.add(new Point(1, 0)); points.add(new Point(5, 5)); points.add(new Point(5, 6)); points.add(new Point(6, 5)); points.add(new Point(10, 2)); points.add(new Point(10, 3)); points.add(new Point(11, 3)); Canopy canopy = new Canopy(points); canopy.cluster(); //獲取canopy數目 int clusterNumber = canopy.getClusterNumber(); System.out.println(clusterNumber); //獲取canopy中T2的值 System.out.println(canopy.getThreshold()); } } |
以上代碼是對9個點使用Canopy算法進行計算,獲取Canopy數目,也即K。
更多文章請前往小胖軒.