K近鄰算法思想很是簡單,總結起來就是根據某種距離度量檢測未知數據與已知數據的距離,統計其中距離最近的k個已知數據的類別,以多數投票的形式肯定未知數據的類別。java
一直想本身實現knn的java實現,但限於本身的編程水平,java剛剛入門,因此就普遍搜索網上以實現的java代碼來研習。下面這個簡單的knn算法的java實現是在這篇博客中找到的:http://blog.csdn.net/luowen3405/article/details/6278764node
下面給出我對代碼的註釋,若是有錯誤請指正。算法
源程序共定義了三個class文件,分別是:public class KNNNode;public class KNN;public class TestKNN。編程
Description:數組
KNNNode: KNN結點類,用來存儲最近鄰的k個元組相關的信息 數據結構
KNN: KNN算法主體類 dom
TestKNN: KNN算法測試類 函數
首先,按照程序執行順序依次解釋class的思想。測試
一、 TestKNNthis
Method: public void read()
讀取文件中的數據,存儲爲數組的形式(以嵌套鏈表的形式實現)List<List<Double>> datas
程序主體執行:main
首先讀入訓練數據文件和測試數據文件的數據,而後輸出測試數據的類別。此程序中K=3,根據對這個數據集的瞭解,k=3時效果是最好的。Knn算法k的肯定一直是一個值得研究的problem。
二、 算法主體:KNN
此程序中比較一個難點是做者定義了一個大小爲k優先級隊列來存儲k個最近鄰節點。優先級隊列初始默認是距離越遠越優先,而後根據算法中的實現,將與測試集最近的k個節點保存下來。
三、 定義了一個數據節點數據結構:KNNNode
源碼以下:
package KNN; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.PriorityQueue; /** * KNN算法主體類 * @author Rowen * @qq 443773264 * @mail luowen3405@163.com * @blog blog.csdn.net/luowen3405 * @data 2011.03.25 */ public class KNN { /** * 設置優先級隊列的比較函數,距離越大,優先級越高 */ private Comparator<KNNNode> comparator = new Comparator<KNNNode>() { public int compare(KNNNode o1, KNNNode o2) { if (o1.getDistance() >= o2.getDistance()) { return 1; } else { return 0; } } }; /** * 獲取K個不一樣的隨機數 * @param k 隨機數的個數 * @param max 隨機數最大的範圍 * @return 生成的隨機數數組 */ public List<Integer> getRandKNum(int k, int max) { List<Integer> rand = new ArrayList<Integer>(k); for (int i = 0; i < k; i++) { int temp = (int) (Math.random() * max); if (!rand.contains(temp)) { rand.add(temp); } else { i--; } } return rand; } /** * 計算測試元組與訓練元組以前的距離 * @param d1 測試元組 * @param d2 訓練元組 * @return 距離值 */ public double calDistance(List<Double> d1, List<Double> d2) { double distance = 0.00; for (int i = 0; i < d1.size(); i++) { distance += (d1.get(i) - d2.get(i)) * (d1.get(i) - d2.get(i)); } return distance; } /** * 執行KNN算法,獲取測試元組的類別 * @param datas 訓練數據集 * @param testData 測試元組 * @param k 設定的K值 * @return 測試元組的類別 */ public String knn(List<List<Double>> datas, List<Double> testData, int k) { PriorityQueue<KNNNode> pq = new PriorityQueue<KNNNode>(k, comparator);//按照天然順序存儲容量爲k的優先級隊列 List<Integer> randNum = getRandKNum(k, datas.size()); // 創建一個列表,列表中保存的是訓練數據集中實例的個數 //計算當前一個測試數據實例與訓練數據集的距離,並按照距離來排序 for (int i = 0; i < k; i++) { int index = randNum.get(i); List<Double> currData = datas.get(index); String c = currData.get(currData.size() - 1).toString(); KNNNode node = new KNNNode(index, calDistance(testData, currData), c); pq.add(node); // System.out.println("距離"+node.getDistance()+"測試樣例"+index+"k值"+k); } //統計與測試實例距離最近的數據,而後將 for (int i = 0; i < datas.size(); i++) { List<Double> t = datas.get(i); double distance = calDistance(testData, t); KNNNode top = pq.peek(); if (top.getDistance() > distance) { pq.remove(); pq.add(new KNNNode(i, distance, t.get(t.size() - 1).toString())); } } return getMostClass(pq); } /** * 獲取所獲得的k個最近鄰元組的多數類 * @param pq 存儲k個最近近鄰元組的優先級隊列 * @return 多數類的名稱 */ private String getMostClass(PriorityQueue<KNNNode> pq) { Map<String, Integer> classCount = new HashMap<String, Integer>(); for (int i = 0; i < pq.size(); i++) { KNNNode node = pq.remove(); String c = node.getC(); if (classCount.containsKey(c)) { classCount.put(c, classCount.get(c) + 1); } else { classCount.put(c, 1); } } int maxIndex = -1; int maxCount = 0; Object[] classes = classCount.keySet().toArray(); for (int i = 0; i < classes.length; i++) { if (classCount.get(classes[i]) > maxCount) { maxIndex = i; maxCount = classCount.get(classes[i]); } } return classes[maxIndex].toString(); } }
package KNN; /** * KNN結點類,用來存儲最近鄰的k個元組相關的信息 * @author Rowen * @qq 443773264 * @mail luowen3405@163.com * @blog blog.csdn.net/luowen3405 * @data 2011.03.25 */ public class KNNNode { private int index; // 元組標號 private double distance; // 與測試元組的距離 private String c; // 所屬類別 public KNNNode(int index, double distance, String c) { super(); this.index = index; this.distance = distance; this.c = c; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public double getDistance() { return distance; } public void setDistance(double distance) { this.distance = distance; } public String getC() { return c; } public void setC(String c) { this.c = c; } }
package KNN; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.List; /** * KNN算法測試類 * @author Rowen * @qq 443773264 * @mail luowen3405@163.com * @blog blog.csdn.net/luowen3405 * @data 2011.03.25 */ public class TestKNN { /** * 從數據文件中讀取數據 * @param datas 存儲數據的集合對象 * @param path 數據文件的路徑 */ public void read(List<List<Double>> datas, String path){ try { BufferedReader br = new BufferedReader(new FileReader(new File(path))); String data = br.readLine(); List<Double> l = null; while (data != null) { String t[] = data.split(" "); l = new ArrayList<Double>(); for (int i = 0; i < t.length; i++) { l.add(Double.parseDouble(t[i])); // System.out.println(l); } datas.add(l); data = br.readLine(); } br.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 程序執行入口 * @param args */ public static void main(String[] args) { TestKNN t = new TestKNN(); String datafile = new File("").getAbsolutePath() + File.separator + "datafile"; String testfile = new File("").getAbsolutePath() + File.separator + "testfile"; // System.out.println(datafile); try { List<List<Double>> datas = new ArrayList<List<Double>>(); List<List<Double>> testDatas = new ArrayList<List<Double>>(); t.read(datas, datafile); t.read(testDatas, testfile); // System.out.println(datas); KNN knn = new KNN(); for (int i = 0; i < testDatas.size(); i++) { List<Double> test = testDatas.get(i); System.out.print("測試元組: "); for (int j = 0; j < test.size(); j++) { System.out.print(test.get(j) + " "); } System.out.print("類別爲: "); System.out.println(Math.round(Float.parseFloat((knn.knn(datas, test, 3))))); } } catch (Exception e) { e.printStackTrace(); } } }
附上待分類數據:
文件名字:datafile
0.1887 0.3276 -1 0.8178 0.7703 1 0.6761 0.4849 -1 0.6022 0.6878 -1 0.1759 0.8217 -1 0.2607 0.3502 1 0.2875 0.6713 -1 0.916 0.7363 -1 0.1615 0.2564 1 0.2653 0.9452 1 0.0911 0.4386 -1 0.0012 0.3947 -1 0.4253 0.8419 1 0.0067 0.4424 -1 0.8244 0.2089 1 0.3868 0.3592 -1 0.9174 0.216 -1 0.6074 0.3968 -1 0.068 0.5201 -1 0.9686 0.9937 1 0.0908 0.3658 1 0.3411 0.7691 -1 0.4609 0.4423 -1 0.1078 0.4501 1 0.3445 0.0445 -1 0.9827 0.7093 1 0.2428 0.3774 -1 0.0358 0.1971 -1 0.82 0.721 1 0.6718 0.6714 -1 0.6753 0.2428 -1 0.7218 0.4299 -1 0.3127 0.8329 1 0.0225 0.4162 1 0.5313 0.2187 1 0.7847 0.4243 -1 0.2518 0.6476 1 0.4076 0.5439 1 0.9063 0.4587 1 0.4714 0.2703 -1 0.7702 0.0196 -1 0.2548 0.3477 -1 0.0942 0.5407 1 0.1917 0.8085 -1 0.6834 0.7689 -1 0.1056 0.1097 1 0.9577 0.5303 -1 0.9436 0.0938 -1 0.6959 0.3181 1 0.4235 0.4484 1 0.6171 0.6358 1 0.5309 0.5447 1 0.8444 0.2621 -1 0.5762 0.8335 -1 0.281 0.772 1 0.224 0.15 -1 0.4243 0.704 -1 0.7384 0.7551 -1 0.4401 0.9329 1 0.2665 0.7635 1 0.5944 0.662 1 0.3225 0.3309 -1 0.4709 0.2648 1 0.6444 0.9899 -1 0.5271 0.9727 1 0.7788 0.4046 1 0.7302 0.2362 1 0.5181 0.6963 -1 0.5841 0.6073 1 0.7184 0.5225 1 0.6999 0.1192 1 0.3439 0.1194 1 0.6951 0.7413 -1 0.611 0.0636 1 0.4229 0.5822 1 0.4735 0.8878 -1 0.2891 0.3935 -1 0.3196 0.6393 1 0.1527 0.3912 -1 0.6385 0.9398 1 0.2904 0.679 1 0.4574 0.192 1 0.3251 0.1058 1 0.6377 0.5254 -1 0.5985 0.8699 1 0.4257 0.862 -1 0.2691 0.7904 -1 0.8754 0.1389 1 0.0336 0.6456 1 0.6544 0.6473 1
文件名稱:testfile
0.9516 0.0326 0.9203 0.5612 0.0527 0.8819 0.7379 0.6692 0.2691 0.1904 0.4228 0.3689 0.5479 0.4607 0.9427 0.9816 0.4177 0.1564 0.9831 0.8555 0.3015 0.6448 0.7011 0.3763 0.6663 0.1909 0.5391 0.4283 0.6981 0.4820 0.6665 0.1206 0.1781 0.5895 0.1280 0.2262 0.9991 0.3846 0.1711 0.5830
經過KNN算法對未知數據集分類,設置k=3,分類結果以下:
測試元組: 0.9516 0.0326 類別爲: -1 測試元組: 0.9203 0.5612 類別爲: -1 測試元組: 0.0527 0.8819 類別爲: -1 測試元組: 0.7379 0.6692 類別爲: -1 測試元組: 0.2691 0.1904 類別爲: -1 測試元組: 0.4228 0.3689 類別爲: -1 測試元組: 0.5479 0.4607 類別爲: -1 測試元組: 0.9427 0.9816 類別爲: 1 測試元組: 0.4177 0.1564 類別爲: 1 測試元組: 0.9831 0.8555 類別爲: -1 測試元組: 0.3015 0.6448 類別爲: -1 測試元組: 0.7011 0.3763 類別爲: -1 測試元組: 0.6663 0.1909 類別爲: -1 測試元組: 0.5391 0.4283 類別爲: -1 測試元組: 0.6981 0.482 類別爲: -1 測試元組: 0.6665 0.1206 類別爲: 1 測試元組: 0.1781 0.5895 類別爲: 1 測試元組: 0.128 0.2262 類別爲: 1 測試元組: 0.9991 0.3846 類別爲: -1 測試元組: 0.1711 0.583 類別爲: 1