一.算法介紹
核密度估計(Kernel Density Estimation)是一種用于估計數據分布的非參數統計方法。它可以用于多種目的和應用,包括:
- 數據可視化:核密度估計可以用來繪制平滑的密度曲線或熱力圖,從而直觀地表示數據的分布情況。它可以幫助我們觀察數據集中的高密度區域、低密度區域以及變化趨勢。
- 異常檢測:通過核密度估計,我們可以識別數據中的異常點或離群值。異常點通常表現為低密度區域或與其他數據點明顯不同的區域。
- 概率密度計算:核密度估計可以用于計算給定數值的概率密度。通過將新數據點帶入核密度估計函數,可以估計出該點在數據分布中的密度。
- 模式識別:核密度估計可以用于識別數據中的模式或聚類。通過觀察密度最高的區域,可以推斷數據的聚類情況或潛在的模式。
- 預測建模:核密度估計可以用于構建概率模型,進而進行預測。例如,在分類問題中,可以使用核密度估計來估計每個類別的概率密度,然后根據新的數據點所屬的密度來進行分類預測。
根據具體的應用需求,我們可以靈活地使用核密度估計來分析和理解數據集的特征和結構,可能的用途包括針對社區規劃分析房屋密度或犯罪行為,或探索道路或公共設施管線如何影響野生動物棲息地。
每個點位可以設置 weight 字段賦予某些要素比其他要素更大的權重,該字段還允許使用一個點表示多個觀察對象。例如,一個地址可以表示一棟六單元的公寓,或者在確定總體犯罪率時可賦予某些罪行比其他罪行更大的權重。
二.算法計算原理
本算法以四次核函數為基礎,四次核函數的特點是具有平滑的曲線形狀,具有較寬的窗口,對數據點的貢獻在距離較遠時會迅速減小。由于其平滑性和較大的支持范圍,四次核函數在核密度估計中被廣泛使用。
在核密度估計中,通過將核函數應用于每個數據點,并對所有數據點的貢獻進行求和,可以計算出在每個位置上的密度估計值。四次核函數的結果可視為在核密度估計中每個位置的密度貢獻權重。較大的結果表示該位置的密度較高,而較小或接近零的結果表示該位置的密度較低。
本算法中主要利用核密度公式計算空間范圍內的核密度值,根據核密度值生成 png 或 jpg 格式的熱力圖,或者將整個空間切割成網格,用網格中心點參與核密度計算生成 geojson 文件,以供進一步空間探索分析。
/*** 計算單個核密度* @param radius 半徑* @param dist 兩點的距離* @param weight 權重* @return*/public static double computeKernel(double radius, double dist, double weight){return (3 / Math.PI) * weight * Math.pow((1 - Math.pow(dist / radius,2)), 2);}
創新性說明:
- 1.算法會自適應數據中的空間點位范圍,此范圍可根據參數bufferSize 設置緩沖區擴展,以獲取數據范圍外的點參與計算。
- 2.根據空間范圍每隔特定步長創建虛擬點位或劃分網格,靈活性較高,步長越小則結果在地圖分布上的精度越高,步長參數step(米) 可選,如果沒有設置, 則默認在空間范圍內自適應創建一百萬左右虛擬點或網格。
- 3.采用多線程的方式進行核密度計算,速度更快。
- 4.可將結果值進行歸一化處理,核密度計算出來的結果值主要用于觀察數據分布,但是各個結果值之間相差范圍較大,不易觀察數據分布,歸一化后能更清晰觀察不同區域間的分布情況。
- 5.可根據核密度值的大小根據不同需求生成熱力圖或 geojson 文件。可在geojson文件上做進一步探索。
三.算法程序
1. 核心流程代碼
從csv中獲取源數據點信息, 獲取坐標范圍,如果需要緩沖區, 則設置緩沖區, 獲取步長長度(默認一百萬個像素點或網格),然后根據核密度信息創建圖片或geojson
// 輸入文件路徑String inputPath ="D:\\測試數據.csv";// 輸出文件路徑String outPath ="D:\\測試數據.geojson";// String outPath ="D:\\測試數據.jpg";// 經度字段String lonKey = "lon";// 緯度字段String latKey = "lat";// 權重字段String weightKey = "";// 影響半徑double radius = 300.0;// 緩沖區double bufferSize = 0.1;// 生成的網格長度(單位: 米)int step = 0;int type;if (outPath.endsWith("png") || outPath.endsWith("jpg")){type = 0;}else if (outPath.endsWith("geojson")){type = 1;}else {throw new RuntimeException("輸出文件格式只能是 png、jpg 或者 geojson");}// 從csv中獲取源數據點信息List<EntryPoint> entryPoints = EntryPoint.formatToEntryPoints(inputPath, lonKey, latKey, weightKey, radius);// 獲取坐標范圍double[] coordsScope = KernelUtils.getCoordsScope(entryPoints);// 如果需要緩沖區, 則設置緩沖區if (bufferSize != 0){coordsScope = KernelUtils.getBufferScope(coordsScope[0], coordsScope[1], coordsScope[2], coordsScope[3], bufferSize);}// 獲取默認的步長長度, 默認一百萬個像素點或網格if (step ==0){step = KernelUtils.getDefaultSize(coordsScope);}// 根據核密度信息創建圖片或geojsonkernel(coordsScope, entryPoints, step, radius, type, outPath);
/*** 核密度方法* @param coordsScope 坐標范圍* @param entryPoints 從csv中獲取源數據點信息* @param step 步長長度* @param radius 影響半徑* @param type 輸出文件類型*/public static void kernel(double[] coordsScope, List<EntryPoint> entryPoints, int step, double radius, int type, String path){// 獲取網格坐標系的lon, lat的列表List<Double[]> coords = KernelUtils.getKennelPointCoords(coordsScope[0], coordsScope[1],coordsScope[2],coordsScope[3], step);Progress.progress( progress++);int width = coords.get(0).length;int high = coords.get(1).length;if (type == 1){// 生產 geojson 網格結果generatorGridGeojson(coords, entryPoints, width-1, high-1, radius, path, step);}else {// 生產熱力圖圖片generatorThermalMap(coords, entryPoints, width, high, radius, path, step);}}
2.創建面的 geojson 文件
/*** 根據核密度信息創建面的 geojson 文件* @param coords 虛擬數據點經緯度列表* @param entryPoints 數據點* @param width 橫向點位數量* @param high 縱向點位數量* @param radius 影響半徑*/public static void generatorGridGeojson(List<Double[]> coords, List<EntryPoint> entryPoints,int width, int high, double radius, String path, int step){// 獲取所有中心點位的數據List<PixelPoint> pixelPoints = KernelUtils.getGridCenters(coords);// 進行核密度計算, 并記錄受到影響的網格信息KernelResult kernelResult = kernelCompute(entryPoints, pixelPoints, width, high, radius);Double[][] matrix = kernelResult.getMatrix();Double max = kernelResult.getMax();Double min = kernelResult.getMin();// 生產面的 geojson 文件writeToFile(KernelUtils.jointGridGeojson(matrix, max, min, coords), path);System.out.println(String.format("計算完成, 生成 geojson 文件, 參與計算網格 %d 個, 受影響網格 %d 個, 相鄰網格間距 %s 米",pixelPoints.size(), KernelUtils.effectiveGrid, step));}
3.熱力圖圖片
/*** 根據核密度信息創建熱力圖圖片* @param coords 虛擬數據點經緯度列表* @param entryPoints 數據點* @param width 橫向點位數量* @param high 縱向點位數量* @param radius 影響半徑*/public static void generatorThermalMap(List<Double[]> coords, List<EntryPoint> entryPoints,int width, int high, double radius, String path, int step){// 獲得所有點位List<PixelPoint> pixelPoints = KernelUtils.spliceKennelPoints(coords);// 進行核密度計算, 并記錄受到影響的網格信息KernelResult kernelResult = kernelCompute(entryPoints, pixelPoints, width, high, radius);Double[][] matrix = kernelResult.getMatrix();Double max = kernelResult.getMax();Double min = kernelResult.getMin();// 生產熱力圖ImageGenerator.generatorImage(matrix, max, min, path);System.out.println(String.format("計算完成, 生成圖片 像素: %d x %d, 相鄰像素點實際代表距離 %s 米", width, high, step));}
4.計算所有點位的核密度
/*** 計算所有點位的核密度* @param entryPoints 數據點信息* @param pixelPoints 創建的虛擬像素點* @param radius 影響半徑* @return*/public static KernelResult kernelCompute(List<EntryPoint> entryPoints, List<PixelPoint> pixelPoints, int width, int high, double radius){List<Double> values = new ArrayList<>();double affectLat = KernelUtils.getLatDist(radius);// 記錄受到影響的網格Double[][] matrix = new Double[high][width];// 建立線程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(30, 30, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(Integer.MAX_VALUE));// 線程等待計數器CountDownLatch countDownLatch = new CountDownLatch(pixelPoints.size());// 創建鎖, 使計算數據具有線程間可見性Lock lock = new ReentrantLock();int stepPosition = pixelPoints.size() / 75;for (int i = 0; i < pixelPoints.size(); i++){PixelPoint pixelPoint = pixelPoints.get(i);Double kennelLon = pixelPoint.getLon();Double kennelLat = pixelPoint.getLat();threadPool.execute(() -> {// 開始計算每個網格受到其他所有點所影響的核密度double kernel = 0.0;for (int j = 0; j < entryPoints.size(); j++){EntryPoint entryPoint = entryPoints.get(j);double lon = entryPoint.getLon();double lat = entryPoint.getLat();if (Math.abs(lon - kennelLon) > entryPoint.getAffectLon() || Math.abs(lat - kennelLat) > affectLat){continue;}// 獲取權重, 默認為 1.0double weight = 1.0;if (entryPoint.getWeight() != null){weight = entryPoint.getWeight();}// 計算網格中心點與源數據點的距離double distance = KernelUtils.getDistance(lon, lat, kennelLon, kennelLat);// 影響半徑大于距離的點直接去掉if (distance <= radius){// 計算每個網格所受影響的核密度kernel += computeKernel(radius, distance, weight);}}lock.lock();// 為中心點實體類賦予核密度的值Double value = 1 / Math.pow(radius, 2) * kernel;matrix[pixelPoint.getI()][pixelPoint.getJ()] = value;values.add(value);lock.unlock();countDownLatch.countDown();if (countDownLatch.getCount() % stepPosition == 0 && progress < 80){Progress.progress(progress++);}});}// 等待所有任務執行完畢try {countDownLatch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}// 關閉線程池threadPool.shutdown();return new KernelResult(matrix, Collections.max(values), Collections.min(values));}
5.可執行 jar 包
該程序可打為可執行jar包, 文件夾中的: kernel.jar
運行環境: jdk 1.8
執行示例:
java -jar kernel.jar 杭州市超市營業額.csv 杭州市超市營業額熱力.jpg 經度 緯度 利潤 2000.0 0.1 0
java -jar kernel.jar 杭州市超市營業額.csv 杭州市超市營業額分布.geojson 經度 緯度 利潤 2000.0 0.1 0
java -jar kernel.jar 測試數據.csv 測試數據.jpg lon lat "" 300.0 0.1 0
java -jar kernel.jar 測試數據.csv 測試數據.geojson lon lat "" 300.0 0.1 0
參數 | 參數位置 | 參數說明 |
---|---|---|
inputPath | 1 | 輸入的csv文件路徑 |
outPath | 2 | 輸出的文件路徑,程序根據文件后綴選擇生產的文件類型,只允許 jpg、png、geojson 三種文件。 |
lonKey | 3 | 輸入文件中的經度字段名 |
latKey | 4 | 輸入文件中的緯度字段名 |
weightKey | 5 | 輸入文件中的權重字段名,沒有則輸入”” |
radius | 6 | 影響半徑,單位米,影響半徑越長,周圍空間受該數據的影響越廣,需根據不同的輸入數據情況調整 |
bufferSize | 7 | 空間緩沖區,可擴大數據空間范圍,一般0.1即可,即擴大 10% 的區域 |
step | 8 | 空間劃分步長,步長越小則參與計算的空間點數據越多,計算量越大,結果數據越精確, 需根據不同的輸入數據情況調整,當值為0時,程序則適配生成一百萬個點或網格參與計算,注:盡量不要在城市級別范圍設置過低步長 |
四.執行結果展示
熱力圖示例:
平臺分析示例:
杭州市超市營業額區域性分析-熱力圖:
杭州市超市營業額區域性分析-平臺分析:
五、應用場景
-
金融風險評估:核密度算法可以用于評估某種投資方式的風險程度。將歷史數據輸入核密度估計器中,可以得出該投資方式在不同風險水平下的收益概率密度分布。這有助于金融機構更好地了解風險和收益之間的平衡。
-
生態學:核密度算法可用于研究動植物的棲息地和遷徙模式。將動植物的觀察數據輸入核密度估計器中,可以得出它們在不同地點出現的概率密度分布,幫助科學家更好地了解動植物的棲息地范圍和活動規律。
-
交通流量預測:核密度算法可以用于預測道路上的交通流量。將歷史交通流量數據輸入核密度估計器中,可以得出在不同時間段內和不同位置上的交通流量概率密度分布。這有助于交通管理人員更好地規劃道路、優化路線和管理交通擁堵。
-
模式識別:核密度算法可以使用于人臉識別、圖像處理等領域。將輸入數據的特征值輸入核密度估計器中,可以得出不同特征值下相應數據的概率密度分布。這可用于識別圖像中不同物體的特征值,例如人臉的輪廓和眼睛的位置,從而實現自動化識別。