【Python】沃罗诺伊图 | KNN 最邻近算法 | Voronoi 函数

news2025/1/17 15:32:09

   猛戳!跟哥们一起玩蛇啊 👉 《一起玩蛇》🐍

写在前面:上一章我们介绍了介计算几何领域的德劳内三角剖分,我们提到过关于点集的很多种几何图都与德劳内三角剖分密切相关,其中最具代表的就是我们本章要介绍的 Voronoi 图 (即沃罗诺伊图) 。沃罗诺伊图有许多应用,包括计算机图形学、地理信息系统、计算机视觉等领域。


Ⅰ. 前置知识(Antecedent)

0x00 引入:什么是沃罗诺伊图?

介绍:沃罗诺伊图 (Voronoi Diagram) ,又称 "狄利克雷镶嵌" (Dirichlettessellation) ,又又称泰森多边形 (Thiessen polygon) ,又又又称维诺图。是由俄国数学家 格奥尔基 · 沃洛诺伊 所建立的空间分割算法,故命名为沃洛诺伊图。

其于1908年提出,在《莫斯科数学学报》上发表了一篇题为《距离函数和近似问题》的论文,在其中首次提出了沃罗诺伊图的概念,沃罗诺伊图后来被广泛应用于许多领域,并成为计算机图形学和地理信息系统等领域的重要工具。

性质:沃洛诺伊图是一种可以把点转化成区域的图表,这种图表中每个划分区域中的任何一点距离区域中心的距离都比距离其他区域中心点的距离更近。

  • 沃罗诺伊图是按照与种子点 (seed point) 的距离分割平面的示意图
  • 对于顶平面上的种子点 p1,p2,...p_n ,根据平面上的点与哪个 p_i 最接近来划分区域

0x01 德劳内三角剖分与沃罗诺伊图的关系

德劳内三角剖分和沃罗诺伊图诺是双重关系 (dual) ,只要知道一个,就可以得到另一个。

德劳内 \rightarrow 沃罗诺伊:

用每个种子点将入口三角分割后,连接被求出的三角形的外接园的中心就是博罗诺可以获得这个图像。

沃罗诺伊 \rightarrow 德劳内:

若将沃罗诺伊区域之间的种子点连接起来,就可以获得这些种子点的德劳内三角剖分。

0x02 KNN 最邻近算法 (K-Nearest Neighbor)

 (K-Nearest Neighbor)

定义:当输入新数据时,通过将现有数据与新数据进行比较,选出与新数据最接近的 k 个数据,并根据 k 个数据的 category 对输入数据进行分类,通常使用欧几里德距离。

欧几里得距离是两点在 n 维空间中的距离,其公式如下:

 

\textrm{KNN} 算法的工作流程如下:

  1. 确定最近邻数量 k 。
  2. 计算待分类样本与训练数据集中每个样本的距离。
  3. 按照距离的大小对训练数据集中的样本进行排序。
  4. 选取距离最小的 k 个样本。
  5. 确定 k 个样本中最多的分类类别。
  6. 将待分类样本分类为出现次数最多的类别。

优点:简单高效,学习数据中的噪声影响不大,训练速度可观。

缺点:研究人员根据每个数据的特性任意设置最佳邻域数(K)和要使用的距离尺度。测量新的观测值和每个学习数据之间的全部距离,计算耗时较长,对于给定的训练样本集,需要对整个数据集进行扫描,因此时间复杂度为 O(n),对于较大的数据集不太适用。

0x03 利用 make_classification 生成分类数据集

我们可以利用 sklearn.datasets 库中的 make_classification 去生成一些数据:

import numpy as np
import matplotlib .pyplot as plt
import cv2
from sklearn.datasets import make_classification

# 使用 make_classification 创建数据
X, y = make_classification (
    n_features = 2,             # 设置特征数为2
    n_samples = 200,            # 设置样本数量为200
    n_informative = 1,          # 指定有用特征数量为1
    n_redundant = 0,            # 指定冗余特征,0为无冗余特征
    n_clusters_per_class = 1,   # 指定每个类别簇数为1
    random_state = 3            # 指定随机数种子为3
)

plt.scatter (
    X[:, 0], X[:, 1],
    marker = 'o', c = y, s = 100, edgecolor = "k", linewidth = 2
)

plt.xlim(-3,3)
plt.ylim(-3,3)
plt.show()

🚩 运行结果:

💡 代码分析:上述代码中, make_classification 函数生成的数据储存在两个变量 X 和 y 中,其中 X 是一个二维数组,包含所有样本的两个特征,而 y 是一个一维数组,包含所有样本的类别。参数解释如下:

  • n_features 参数用于指定特征数,我们设置为了 2 个。
  • n_samples 参数用于指定样本数量,我们指定为 200 个。
  • n_informative 参数指定了有几个特征是有用的(即可以用来帮助分类的),这里设置为 1 表示只有一个特征是有用的。
  • n_redundant 参数指定了有几个特征是冗余的,这里设置为 0 表示没有冗余特征。
  • n_clusters_per_class 参数指定了每个类别中有几个簇(cluster),这里设置为 1 表示每个类别只有一个簇。
  • random_state 参数指定了随机数生成器的种子,可以用来确保生成的数据在不同的运行之间是相同的。

Ⅱ. 沃罗诺伊图(Voronoi Diagram)

0x00 通过 scipy.spatial 生成沃罗诺伊图

最简单的方法就是导入 scipy 库:

from scipy.spatial import Voronoi, voronoi_plot_2d

利用 scipy.spatial.Voronoi 函数生成,并用 scipy.spatial.voronoi_plot_2d 绘制。

from scipy.spatial import Voronoi, voronoi_plot_2d
import matplotlib.pyplot as plt

points = np.random.rand(30, 2)  # 生成随机点集
vor = Voronoi(points)           # 使用Voronoi函数生成
voronoi_plot_2d(vor)            # 绘制

plt.show()

🚩 运行结果:  (随机生成)

0x01 基于 make_classification 生成的数据生成沃罗诺伊图

在前置部分我们讲解了如何使用 make_classification 函数生成分类数据集。

现在我们利用刚才该函数生成的数据集,通过 Voronoi 和 voronoi_plot_2d 生成沃罗诺伊图。

💬 代码演示:基于 make_classification 创建的数据生成

import numpy as np
import cv2
import matplotlib .pyplot as plt
from sklearn.datasets import make_classification
from scipy.spatial import Voronoi, voronoi_plot_2d

X, y = make_classification (
    n_features = 2, 
    n_samples = 200, 
    n_informative = 1, 
    n_redundant = 0,
    n_clusters_per_class = 1,
    random_state = 3
)

from scipy.spatial import Voronoi, voronoi_plot_2d

vor = Voronoi(X, qhull_options = 'Qc')   
fig = voronoi_plot_2d(vor)   # 使用 voronoi_plot_2d 生成图
plt.scatter (
    X[:, 0], X[:, 1], 
    marker='o', c=y, s=45, edgecolor="k", linewidth=1 )

plt.show()

🚩 运行结果:

我们还可以尝试使用 annotate 函数给它们标上号:

''' 标号 '''
for i in vor.point_region:
    plt.annotate(str(i), (vor.points[i][0], vor.points[i][1]), size = 12)

0x02 沃罗诺伊图的应用

沃罗诺伊图最重要的条件是,要利用垂直等分线将尽可能接近的两点分割成一定包含一个点的平面,下面我介绍几种能够应用的场景:

寻找最近的便利设施

地图 APP 大家一定使用过,那种 "查看附近" 的功能,就可以运用到沃罗诺伊图。

比如寻找离我所在地最近的医院、药店等便利设施,以我在的地理上的位置为基准点,每个点都可以认为是提供生活所需服务的场所的位置。

病毒传播

流行病学调查中可用于寻找密切接触者,寻找第一个感染者和与他最近接触的人。

疾病感染地区分析

以饮用水泵为基准,划分使用该饮用水泵的人的区域构成。如果某口井被污染了,那么生活在那个领域的人很有可能都喝了被污染的水,可利用这些原理追踪感染源。

……

Ⅲ. 实战练习(Assignment)

练习:KNN & 沃罗诺伊图


 Google Colaboratory (K80 GPU)Jupyter Notebook:colab


在 \textrm{KNN}​ 中保存整个 train set 时,在创建沃罗诺伊图后编辑和推断近邻时,请确保性能相同。

过程:

  1. 创建由两个类组成的随机数据使用 sklearn 库的 make_classification 函数
  2. k=1​ 的 \textrm{KNN}​ 分类器中学习生成的数据 (Classifier1) ,使用 sklearn 库中的 NeighborsClassifier 函数。
  3. 使用数据生成全景图
  4. 对沃罗诺伊图执行 Nearest Neighbor editing
  5. k=1​ 的 \textrm{KNN}​分类器中学习已编辑数据 (Classifier2)
  6. 生成测试数据,比较两个分类器的性能,使用 sklearn 库的 accuratic_score 函数

* 提供基础框架,只需要在 TODO 位置填写代码即可!(完成第一部分和第二部分)

💭 框架提供:base code

  • 第一部分
import numpy as np
import matplotlib .pyplot as plt
import cv2
from sklearn.datasets import make_classification
from scipy.spatial import Voronoi, voronoi_plot_2d

X, y = make_classification(
    n_features=2, n_samples = 200, n_informative=1, n_redundant=0,
    n_clusters_per_class=1, random_state=3)
vor = Voronoi(X, qhull_options = 'Qc')

# 检查图中的邻居,如果只存在与当前类相同的类,则删除当前节点

# 声明要标记是否丢弃的变量
mark = np.zeros(len(X))

# 收集与当前区域相邻的区域的索引信息
# voronoi.ridge_point:存储相邻沃罗诺伊地区的索引。(idx1,idx2)的形式
def get_neighbor_region_idx(curr_idx, voronoi):
    '''
    TODO   在此处添加代码

    '''

    neighbor_pair = []
    for r in voronoi.ridge_points:
        if curr_idx in r:
            neighbor_pair.append(r)
    
    return neighbor_pair


for i in range(len(X)):
    # 收到与当前索引区域相邻的所有区域的信息
    neighbor_pair = get_neighbor_region_idx(i, vor)            
    
    #
    # print(neighbor_pair)
    # Ex ) 
    # [(0, 26), (0, 191), (0, 3), (0, 160), (0, 180), (0, 123), (0, 59)]
    # [(1, 23), (1, 184), (1, 127), (1, 113), (1, 152), (1, 131)]
    # [(2, 93), (2, 74), (2, 33), (2, 13), (2, 103)]
    # ...

    # 距离计算
    shortest_dist = 99999999
    shortest_idx = -1
    
    # 检查所有相邻区域的类
    isSame = True
    for j in range(len(neighbor_pair)):
        idx1 = neighbor_pair[j][0]
        idx2 = neighbor_pair[j][1]
        
        # 比较标签
        if y[idx1]!=y[idx2]: # 如果标签不同
            isSame = False  
            break
                    
    # 如果所有相邻地区都是同一类,则标记。
    # 标记完之后一次性扔掉
    if isSame == True:
        mark[i] = 1
        
# 新的点坐标数组
new_pt = np.where(mark<1)  # 留下未标记的


## 绘图
pt_remain = X[new_pt]
labels_remain = y[new_pt]

plt.scatter(pt_remain[:, 0], pt_remain[:, 1], marker='o', c=labels_remain,
            s=100, edgecolor="k", linewidth=2)
plt.xlim(-3,3)
plt.ylim(-3,3)
plt.show()

🚩 输出效果演示:

  • 第二部分:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

test_X, test_y = make_classification (
    n_features=2, n_samples =30, n_informative=1, 
    n_redundant=0, n_clusters_per_class=1, random_state=3)

# 编辑前的数据学习
knn_classifier1 = KNeighborsClassifier(n_neighbors=1)
''' 
TODO   在此处添加代码
'''

y_pred = knn_classifier1.predict(test_X) 
print(f'Accuracy:', (accuracy_score(test_y, y_pred)))


# 编辑后的数据学习
knn_classifier2 = KNeighborsClassifier(n_neighbors=1)
'''
TODO   在此处添加代码
'''

print(f'Accuracy:', (accuracy_score(test_y, y_pred)))

🚩 输出效果演示:

参考答案:

(为了不影响练习,如需查看答案,请自行展开查看)

  • 第一部分:
































knn_classifier1.fit(test_X, test_y)
  • 第二部分:








































knn_classifier2.fit(test_X, test_y)
y_pred = knn_classifier2.predict(test_X)

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2022.12.27
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

运营商展望来年再提2B市场,然而拯救4G/5G业务的都是手机用户

2022年即将结束&#xff0c;各个行业都开始进行年终盘点并对来年的业务进行规划&#xff0c;三大运营商也对此进行了展望&#xff0c;媒体报道指运营商对来年的发展偏向于2B市场&#xff0c;这已不是它们第一次提偏重企业市场了&#xff0c;然而现实来看拯救这些运营商的其实一…

详解SpringBean的作用域(Scopes)

文章目录一、SpringBean作用域总览二、"singleton" Bean作用域三、"prototype" Bean作用域1、验证singleton、prototype类型的Bean2、总结四、"request" Bean作用域1、配置2、简介五、"session" Bean作用域1、配置2、简介3、总结sessi…

《哈希表》

【一】哈希概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在查找一个元素时&#xff0c;必须要经过关键码的多次比较&#xff0c;顺序查找时间的复杂度为O(N),平衡树中为树的高度&#xff0c;即O(log2N),搜索的效率取决于搜…

JavaWeb技术栈概述

1.1 Web概述 Web是全球广域网&#xff0c;也称为万维网&#xff08;www&#xff09;&#xff0c;能够通过浏览器访问的网站。 在我们日常的生活中&#xff0c;经常会使用浏览器去访问百度、京东、传智官网等这些网站&#xff0c;这些网站统称为Web网站。如下就是通过浏览器访问…

实验三 第四章 MongoDB 副本集

一、实验目的&#xff1a; 了解MongoDB副本集 熟悉MongoDB副本集成员 掌握MongoDB副本集部署 掌握MongoDB副本集操作 理解副本集机制 二、实验环境&#xff1a; 一台win10系统的笔记本电脑 三、实验内容&#xff1a; 4.3部署副本集 4.3.1环境准备 创建的三台虚拟机配置如下&a…

python中的split()、rsplit()、splitlines()用法比较

1 split() 从左向右切割2 rsplit() 从右向左分割3 splitlines() 根据换行符切割4 rsplit()妙用split(分隔符&#xff0c;分割几次)从左向右寻找&#xff0c;分割元素并放入列表中&#xff0c;该分隔符丢弃&#xff1b; rsplit(分隔符&#xff0c;分割几次)从右向左寻找&#x…

算法的复杂性分析

算法的复杂性分析 文章目录算法的复杂性分析0、 算法评价的基本原则1、影响程序运行时间的因素2、算法复杂度2.1 算法的时间复杂度2.2 渐进表示法2.2.1 运行时间的上界2.2. 运行时间的下界2.2.3 运行时间的准确界3、总结4、参考0、 算法评价的基本原则 评价一个算法的好坏实际就…

第二证券|市场短期盘整 不改中期向好格局

12月初以来A股商场全体走势偏弱&#xff0c;上星期首要指数更是五连跌&#xff0c;上证指数重回3100点下方。关于商场近期的再度调整&#xff0c;本周组织观念显现&#xff0c;疫情带来的扰动或是首要原因。 不过&#xff0c;大都组织以为&#xff0c;心情面要素对商场的影响仅…

面试官:ui组件可以自动加载,那么业务组件可以吗?

大厂面试题分享 面试题库 前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 背景 笔者在最近在公司接手了一个老的对内使用的项目&#xff0c;接手后体验了下 发现首屏加载比较慢。分析了下大概的原因是main.js挂…

算法进阶:双指针(一)c++leetcode例题

82. 删除排序链表的重复元素 力扣传送&#xff1a; https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/description/ 给一个排好序的链表&#xff0c;删除把链表中出现的所有的重复的项&#xff1a; 1 2 2 3 3 3 4 5 -----> 1 4 5 这道题有很多种解法&a…

Unity3D——第一人称FPS生存游戏(resident zombies)

游戏源文件和游戏试玩程序:链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Ln2tFizqEO_uEoQhuxvgrQ?pwdl6w0 提取码&#xff1a;l6w0 游戏思路前身搭建: 用一些正方体和胶囊做出来的基础场景&#xff0c; 人物设计:红色的胶囊体是敌人&#xff0c;手持枪械是…

一文说透BIO以及非阻塞IO

目录1. 一次I/O到底经历了什么2. 什么是Socket3. 阻塞I/O&#xff08;Blocking I/O&#xff0c;BIO&#xff09;3.1. 客户端的socket流程3.1.1. 何为socket&#xff1f;3.1.2. 何为连接&#xff1f;3.2. 服务端的socket流程3.2.1. 创建socket3.2.2. 绑定端口号3.2.3. listen()的…

项目实战之旅游网(六)认证与授权

一.编写相关页面 在本项目中&#xff0c;我们使用Spring Security 进行认证和授权&#xff0c;首先我们先编写相关页面。 1.编写登录页面admin_ login.html 2.编写登录失败页面admin_ fail.html 3.编写权限不足页面no_ permission.html 略过。知道其功能即可。 二.编写配置…

数字孪生城市可视化大屏设计,智慧楼宇开源项目

纵观城市发展历史&#xff0c;技术的革命必然会带动城市内部的变革。当前&#xff0c;以数字孪生为代表的前言信息技术飞速发展&#xff0c;必然会使社会对数字城市的深度和广度有着更为清晰的认知。加快构建数字孪生城市管理平台&#xff0c;通过三维可视化大屏直观展示&#…

图表控件LightningChart.NET 系列教程(六):许可证管理介绍(中)

LightningChart.NET SDK 是一款高性能数据可视化插件工具&#xff0c;由数据可视化软件组件和工具类组成&#xff0c;可支持基于 Windows 的用户界面框架&#xff08;Windows Presentation Foundation&#xff09;、Windows 通用应用平台&#xff08;Universal Windows Platfor…

Apache ShardingSphere-Proxy <5.3.0 存在身份认证绕过漏洞

漏洞描述 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c;ShardingSphere-Proxy 是支持 MySQL、PostgreSQL 和 openGauss 协议的数据库代理模块。 ShardingSphere-Proxy 5.3.0 之前的版本中在使用 MySQL 作为后端数据库时&#xff0c;在客户端认证失败后没有…

Linux进程状态与优先级

Ⅰ. OS进程状态的概念 进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。 在三态模型中&#xff0c;进程状态分为 运行态&#xff0c;就绪态&#xff0c;阻塞态。 在五态模型中&#xff0c;进程状态分为 新建态、终止态&#xff0c;运行态&a…

服务雪崩预防Sentinel

服务雪崩效应 在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了 问题&#xff0c;调用这个服务就会出现线程阻塞的情况&#xff0c;此时若有大量的请求涌入&#xff0c;就会出现多条线程阻塞等 待&#xff0c;进而导致服务瘫痪。 由于…

【HTML+CSS+JavaScript】实现鼠标点击烟花效果

文章目录【HTMLCSSJavaScript】实现鼠标点击烟花效果&#xff08;爆炸型、心型、圆形&#xff09;一. 效果图二. 鼠标点击烟花效果 - 心型实现代码(1) HTML部分代码(2) CSS部分代码(3) 内部的JavaScript部分代码三. 鼠标点击烟花效果 - 圆型实现代码(1) HTML部分代码(2) CSS部分…

【mysql】优化系列文章之一-索引

mysql优化系列 不是教程&#xff0c;不是官方文档&#xff0c;而是自己实战的点滴记录&#xff0c;不一定适合新手和系统学习者 第一章 mysql索引 文章目录mysql优化系列前言1、Mysql索引2、B Tree2.1.特点2.2. 结构分解2.3. 例题分析2.4. 验证索引2.5.索引插入耗时3. MySQL 中…