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。
很多其它文章請前往小胖軒.