KDTree空间搜索算法学习

目录

  • KDTree(K-Dimensional Tree)原理
  • 步骤
    • 空间索引建立例子[^1]
  • 相关包
  • 案例[^2]
    • 数据
    • KDTree 识别轨道衔接出行
    • 轨道衔接单车骑行范围分析
    • 结果保存

KDTree(K-Dimensional Tree)原理

将需要匹配的 K 维空间点建立 K 维树空间索引,在近邻匹配时,输入的点在树中快速检索,即可找到最近邻的点。
在建立空间索引二维树时,算法复杂度介于 O ( l o g 2 n ) O(log_2n) O(log2n) O ( n ) O(n) O(n)之间。

  • 最好的情况下,每次插入点能均匀分割剩余点,算法复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n)
  • 最坏的情况下,每次插入点其余点都在一个字空间中,树的深度为n,算法复杂度为 O ( n ) O(n) O(n)
    KDTree

步骤

  1. 空间索引建立
    • 给定或选择中心节点
    • 依次从各维度分割
    • 按照层次插入点,建立空间索引
  2. 最近邻搜索
    • 给定数据点,查询与其距离最近的点
    • 从根节点开始依次向下搜索,进行深度优先遍历,直到与待查询点处于同一子空间的叶子结点
    • 回溯搜索路径、判断路径上其它子节点空间中,是否可能有距离更近的点
      • 如过有可能,跳转到其他子空间去搜索,将其他子节点加入搜索路径
      • 待查询点为圆心,以待查询点到叶子结点的距离为半径作圆,如果不相交则不必去其他字空间搜索
    • 重复过程,直到搜索路径为空
      KDTree搜索

空间索引建立例子1

  • 二维样例:{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}
  • 根据x轴中位数,选择中心点为(7,2)
  • 首先在 x 轴维度上,比较其余点和中心点的大小,划分双支
    • 左支:{(2,3),(5,4),(4,7)}
    • 右支:{(9,6),(8,1)}
  • 更新分割轴y轴
  • 确定子节点
    • 左节点
      • 在左支中找到y轴的中位数(5,4)
      • 左支数据更新为{(2,3)},右支数据更新为{(4,7)}
    • 右节点
      • 在右支中找到y轴的中位数(9,6)
      • 左支数据更新为{(8,1)},右支数据为null。
  • 更新分割轴x轴
    • 确定(5,4)的子节点
      • 左节点:由于只有一个数据,所以,左节点为(2,3)
      • 右节点:由于只有一个数据,所以,右节点为(4,7)
    • 确定(9,6)的子节点

相关包

sklearn、SciPy、TransBigData、BioPython 中都提供了 KDTree 相关算法接口

  • class sklearn.neighbors.KDTree(X, leaf_size=40, metric=‘minkowski’, **kwargs)
    sklearn.neighbors.KDTree

  • class scipy.spatial.KDTree(data, leafsize=10, compact_nodes=True, copy_data=False, balanced_tree=True, boxsize=None)
    scipy.spatial.KDTree

  • transbigdata.ckdnearest(dfA_origin, dfB_origin, Aname=[‘lon’, ‘lat’], Bname=[‘lon’, ‘lat’])

  • classBio.KDTree.KDTree.KDTree(dim, bucket_size=1)

案例2

数据

共享单车数据

BIKE_IDDATA_TIMELOCK_STATUSLONGITUDELATITUDE
单车ID数据采集时间单车开关锁状态GPS经度GPS维度

共享单车数据

metro_station.csv地铁站数据

KDTree 识别轨道衔接出行

#定义函数,用cKDTree匹配点与点,点与线
import numpy as np
from scipy.spatial import cKDTree
import itertools
from operator import itemgetter
#定义KDTree的函数
def ckdnearest(dfA_origin,dfB_origin,Aname = ['slon','slat'],Bname = ['lon','lat']):
    gdA = dfA_origin.copy()
    gdB = dfB_origin.copy()
    from scipy.spatial import cKDTree
    #为gdB表的点建立KDTree
    btree = cKDTree(gdB[Bname].values)
    #在gdB的KDTree中查询gdA的点,dist为距离,idx为gdB中离gdA最近的坐标点
    dist,idx = btree.query(gdA[Aname].values,k = 1)
    #构建匹配的结果
    gdA['dist'] = dist
    gdA['index'] = idx
    gdB['index'] = range(len(gdB))
    gdf = pd.merge(gdA,gdB,on = 'index')
    #计算
    import CoordinatesConverter
    gdf['dist'] = CoordinatesConverter.getdistance(gdf[Aname[0]],gdf[Aname[1]],gdf[Bname[0]],gdf[Bname[1]])
    return gdf

#读取轨道站点数据
metro_station = pd.read_csv(r'data/metro_station.csv')
metro_station = metro_station.drop_duplicates(subset= 'stationnames')
metro_station = metro_station[['stationnames','lon','lat']]

#匹配起点最近的地铁站
metro_station.columns = ['o_station','o_lon','o_lat']
data_move_metro = ckdnearest(data_move_cleaned,metro_station,Aname = ['slon','slat'],Bname = ['o_lon','o_lat'])
data_move_metro = data_move_metro.rename(columns = {'dist':'o_dist'})

#匹配终点最近的地铁站
metro_station.columns = ['d_station','d_lon','d_lat']
data_move_metro = ckdnearest(data_move_metro,metro_station,Aname = ['elon','elat'],Bname = ['d_lon','d_lat'])
data_move_metro = data_move_metro.rename(columns = {'dist':'d_dist'})
data_move_metro.iloc[:2].T    

#筛选距离地铁站100米范围内的出行数据
data_move_metro = data_move_metro[(data_move_metro['o_dist']<100)|
                (data_move_metro['d_dist']<100)]
data_move_metro.iloc[:2].T

轨道衔接单车骑行范围分析

#选取某站点
station = '同济大学'
#提取地铁站衔接出行的另一端点分布
tmp1 = data_move_metro[(data_move_metro['o_station']==station)&(data_move_metro['o_dist']<=100)][['elon','elat']]
tmp2 = data_move_metro[(data_move_metro['d_station']==station)&(data_move_metro['d_dist']<=100)][['slon','slat']]
tmp1.columns = ['lon','lat']
tmp2.columns = ['lon','lat']
points = pd.concat([tmp1,tmp2])

#转换为GeoDataFrame
points = geopandas.GeoDataFrame(points)
points['geometry'] = geopandas.points_from_xy(points['lon'],points['lat'])

points.plot()

站点骑行衔接需求点分布

#读取轨道站点数据
metro_station = pd.read_csv(r'data/metro_station.csv')
metro_station = metro_station.drop_duplicates(subset= 'stationnames')
#提取这个站点的记录
thisstop = metro_station[metro_station['stationnames']==station]
thisstop = geopandas.GeoDataFrame(thisstop)
thisstop['geometry'] = geopandas.points_from_xy(thisstop['lon'],thisstop['lat'])
thisstop
stationnameslinenamelonlatgeometry
同济大学地铁10号线(新江湾城-虹桥火车站)121.50206731.284578POINT (121.50207 31.28458)
#取得这个站点的位置
lon_thisstop = thisstop['lon'].iloc[0]
lat_thisstop = thisstop['lat'].iloc[0]
#确定显示范围
bounds = [lon_thisstop-0.03,lat_thisstop-0.03,lon_thisstop+0.03,lat_thisstop+0.03]

import matplotlib.pyplot as plt
fig = plt.figure(1,(6,5),dpi = 300)
ax = plt.subplot(111)
plt.sca(ax)
#加载底图
import transbigdata as tbd
#绘制底图
tbd.plot_map(plt,bounds,zoom = 14,style = 4)
points.plot(ax = ax,markersize = 1)
#标注地铁站点
thisstop.plot(ax = ax,c = 'red',markersize = 8,zorder = 2,label = station+'地铁站')
plt.legend()
#设置显示范围
plt.axis('off')
ax.set_xlim(bounds[0],bounds[2])
ax.set_ylim(bounds[1],bounds[3])
plt.show()

共享单车衔接需求分布

#剔除距离过远的点,否则核密度估计可能会出错
points = points[(points['lon']>bounds[0])&
(points['lat']>bounds[1])&
(points['lon']<bounds[2])&
(points['lat']<bounds[3])]

import matplotlib.pyplot as plt
fig = plt.figure(1,(6,5),dpi = 300)
ax = plt.subplot(111)
plt.sca(ax)
#加载底图
import transbigdata as tbd
bounds = [lon_thisstop-0.05,lat_thisstop-0.05,lon_thisstop+0.05,lat_thisstop+0.05]
#绘制底图
tbd.plot_map(plt,bounds,zoom = 13,style = 4)
#标注地铁站点
thisstop.plot(ax = ax,c = 'blue',markersize = 8,zorder = 2,label = station+'地铁站')
plt.legend()
#设置显示范围
plt.axis('off')
ax.set_xlim(bounds[0],bounds[2])
ax.set_ylim(bounds[1],bounds[3])
plt.title(station+'地铁站共享单车衔接范围')
#设置色标
import matplotlib
cmapname = 'autumn_r'
cmap = matplotlib.cm.get_cmap(cmapname)
cax = plt.axes([0.08, 0.4, 0.02, 0.3])
#色标的标题
plt.title('核密度')
import seaborn as sns
#绘制二维核密度图
sns.kdeplot(points['lon'],points['lat'],#指定x与y坐标所在的列
            data = points,
            alpha = 0.8,#透明度
            gridsize = 180, #绘图精细度,越高越慢
            bw = 0.1,    #高斯核大小(经纬度),越小越精细
            cmap = cmap, #定义colormap
            ax = ax, #指定绘图位置
            shade=True, #等高线间是否填充颜色
            shade_lowest=False,#最底层不显示颜色
            cbar=True, #显示colorbar
            cbar_ax=cax,#指定colorbar位置
            zorder = 1 #控制绘图顺序,使其不会挡住地铁站点
           )
plt.show()

共享单车核密度可视化

结果保存

points[['lon','lat']].to_csv(r'data/bicycle_connection_points.csv',index = None)

bicycle_connection_points.csvbicycle_connection_points


  1. KD-Tree原理详解 ↩︎

  2. 《交通时空大数据分析、挖掘与可视化(Python版)》 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/599325.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unet简单结构概述

总体结构代码 class UNet(nn.Module):def __init__(self, n_channels, n_classes, bilinearFalse):super(UNet, self).__init__()self.n_channels n_channelsself.n_classes n_classesself.bilinear bilinearself.inc (DoubleConv(n_channels, 64))self.down1 (Down(64, …

软件设计师-应用技术-数据结构及算法题4

考题形式&#xff1a; 第一题&#xff1a;代码填空 4-5空 8-10第二题&#xff1a;时间复杂度 / 代码策略第三题&#xff1a;拓展&#xff0c;跟一组数据&#xff0c;把数据带入代码中&#xff0c;求解 基础知识及技巧&#xff1a; 1. 分治法&#xff1a; 基础知识&#xff1…

取消vscode go保存时自动格式化代码

go:v1.22.0 vscode go 插件&#xff1a;v0.41.4 setting.json formatOnSave&#xff1a; 保存文件时&#xff0c;是否执行格式化 codeActiosnOnSave&#xff1a;保存文件时&#xff0c;是否执行某些操作 organizeImports: 不再改动import&#xff08;&#xff09;里面的包

分类规则挖掘(三)

目录 四、贝叶斯分类方法&#xff08;一&#xff09;贝叶斯定理&#xff08;二&#xff09;朴素贝叶斯分类器&#xff08;三&#xff09;朴素贝叶斯分类方法的改进 五、其它分类方法 四、贝叶斯分类方法 贝叶斯 (Bayes) 分类方法是以贝叶斯定理为基础的一系列分类算法的总称。贝…

python中numpy库使用

array数组 生成array数组 将list转化为array数组 import numpy as np np.array([1,2],typenp.int32)其中dtype定义的是元素类型&#xff0c;np.int32指32位的整形 如果直接定义dtypeint 默认的是32位整形。 zeors和ones方法 zeros()方法&#xff0c;该方法和ones()类似&a…

Qt——入门基础

目录 Qt入门第一个应用程序 main.cpp widget.h widget.cpp widget.ui .pro Hello World程序 对象树 编辑框 按钮 Qt 窗口坐标系 Qt入门第一个应用程序 main.cpp 这就像一开始学语言时都会打印一个“Hello World”一样&#xff0c;我们先来看看创建好一个项目后&…

ModuleNotFoundError: No module named ‘PyQt5‘

运行python程序的时候报错&#xff1a;ModuleNotFoundError: No module named ‘PyQt5‘ 这是因为没有安装pyqt5依赖包导致的&#xff0c;安装一下即可解决该问题。 安装依赖 pip install PyQt5 -i https://pypi.tuna.tsinghua.edu.cn/simple 这里是使用的清华镜像源进行安装…

数据库系统原理实验报告5 | 数据查询

整理自博主本科《数据库系统原理》专业课自己完成的实验报告&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 专业课本&#xff1a; ———— 本次实验使用到的图形化工具&#xff1a;Heidisql 目录 一、实验目的 二、实验内容 1.找出读者所在城市是“shangh…

STM32G0存储器和总线架构

文章目录 前言一、系统架构二、存储器构成三、存储器地址映射四、存储器边界地址五、外设寄存器边界地址 前言 此文章是STM32G0 MCU的学习记录&#xff0c;并非权威&#xff0c;请谨慎参考。 STM32G0主流微控制器基于工作频率可达64 MHz的高性能Arm Cortex-M0 32位RISC内核。该…

GEE数据集——DeltaDTM 全球沿海数字地形模型数据集

DeltaDTM 全球沿海数字地形模型产品 简介 DeltaDTM 是全球沿岸数字地形模型&#xff08;DTM&#xff09;&#xff0c;水平空间分辨率为 1 弧秒&#xff08;∼30 米&#xff09;&#xff0c;垂直平均绝对误差&#xff08;MAE&#xff09;为 0.45 米。它利用 ICESat-2 和 GEDI …

内容安全(IPS入侵检测)

入侵检测系统&#xff08; IDS &#xff09;---- 网络摄像头&#xff0c;侧重于风险管理&#xff0c;存在于滞后性&#xff0c;只能够进行风险发现&#xff0c;不能及时制止。而且早期的IDS误报率较高。优点则是可以多点进行部署&#xff0c;比较灵活&#xff0c;在网络中可以进…

【java9】java9新特性之改进JavaDocs

Java9在JavaDocs方面的主要新特性是&#xff0c;其输出现在符合兼容HTML5标准。在之前的版本中&#xff0c;默认的HTML版本是 HTML4.01&#xff0c;但在Java9及之后的版本中&#xff0c;JavaDocs命令行工具将默认使用HTML5作为输出标记语言。这意味着&#xff0c;使用JavaDocs工…

Markdown 精简教程(胎教级教程)

文章目录 一、关于 Markdown1. 什么是 Markdown&#xff1f;2. 为什么要用 Markdown&#xff1f;3. 怎么用 Markdown&#xff1f;&#xff08;编辑软件&#xff09; 二、标题1. 常用标题写法2. 可选标题写法3. 自定义标题 ID4. 注意事项 三、段落四、换行五、字体选项1. 粗体2.…

跨境电商行业分析-商品出海的四大路径

1. 跨境电子商务模式和国内电子商务模式【区别】 最大的不同点有3个&#xff1a; 达成交易的双方是属于不同【关境】的交易主体商品通过众多电子商务平台/独立站等&#xff0c;进行支付结算通过国际物流的方式&#xff08;海运/铁路/空运/卡车&#xff09;进行报关、清关、派…

anconda创建虚拟环境,使用虚拟环境(基于win平台)

假设已经安装了anconda&#xff0c;打开anaconda的 shell。 查看已存在的虚拟环境&#xff0c;base是默认的&#xff0c;不用理会&#xff0c;后面的yolov5就是用户创建的 #查看有那些虚拟环境 (base) PS C:\Users\x> conda info -e # conda environments: # base …

如何判断代理IP质量?

由于各种原因&#xff08;从匿名性和安全性到绕过地理限制&#xff09;&#xff0c;代理 IP 的使用变得越来越普遍。然而&#xff0c;并非所有代理 IP 都是一样的&#xff0c;区分高质量和低质量的代理 IP 对于确保流畅、安全的浏览体验至关重要。以下是评估代理 IP 质量时需要…

计划订单转采购申请的增强点和可以增强的内容

MD15 MD14 计划订单转采购申请&#xff0c;涉及的增强点和增强内容 对于外协的采购申请&#xff0c;有时候需要对组件的内容做一些特殊的处理&#xff0c;但是处理组件清单的增强ME_COMPONENTS_UPDATE的增强点&#xff08;这个增强点对于手工创建的外协PR、外协PO,外协pr转外协…

Day21 代码随想录打卡|字符串篇---右旋转字符串

题目&#xff08;卡码网 T55&#xff09;&#xff1a; 字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k&#xff0c;请编写一个函数&#xff0c;将字符串中的后面 k 个字符移到字符串的前面&#xff0c;实现字符串的右旋转…

Pycharm无法链接服务器环境(host is unresponsived)

困扰了很久的一个问题&#xff0c;一开始是在服务器ubuntu20.04上安装pycharm community&#xff0c;直接运行服务器上的pycharm community就识别不了anaconda中的环境 后来改用pycharm professional也无法远程连接上服务器的环境&#xff0c;识别不了服务器上的环境&#xff…

[力扣题解]102.二叉树的层序遍历

题目&#xff1a;102. 二叉树的层序遍历 代码 迭代法 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {queue<TreeNode*> que;TreeNode* cur;int i, size;vector<vector<int>> result;if(root ! NULL){que.push(ro…