matplotlib.Path.contains_points:“半径”参数定义不一致

2023-12-04

Problem:

函数中的radius参数包含_pointmatplotlib.path定义不一致。此函数检查指定点是否位于闭合路径的内部或外部。半径参数用于使路径变小/变大(取决于半径的符号)。这样,可以将靠近路径的点纳入考虑/不考虑。问题是,半径的符号取决于路径的方向(顺时针或逆时针)。 这不一致(在我看来)是存在的,因为在检查点是在路径内部还是外部时,路径的方向被忽略。从严格的数学意义上来说:路径上剩下的所有东西都包括在内。

简而言之:

如果路径逆时针方向,则正半径会考虑更多点。 如果路径是顺时针方向,则正半径会考虑较少的点。

Example:

在以下示例中,检查了 3 种情况 - 每种情况都针对顺时针和逆时针路径:

  1. 是一个包含正半径的点(靠近路径)
  2. 是一个包含负半径的点(靠近路径)
  3. 是否包含原点(位于两条路径的中间)

Code:

import matplotlib.path as path
import numpy as np


verts=np.array([[-11.5,  16. ],[-11.5, -16. ],[ 11.5, -16. ],[ 11.5,  16. ],[-11.5,  16. ]])

ccwPath=path.Path(verts, closed=True) 
cwPath=path.Path(verts[::-1,:], closed=True) 

testPoint=[12,0]


print('contains:         ','|\t', '[12,0], radius=3','|\t', '[12,0], radius=-3','|\t', '[0,0]|')

print('counterclockwise: ','|\t'
,'{0:>16s}'.format(str(ccwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(ccwPath.contains_point(testPoint,radius=-3) )),'|\t'
,ccwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius increases tolerance \t'
)

print('clockwise:        ','|\t'
,'{0:>16s}'.format(str(cwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(cwPath.contains_point(testPoint,radius=-3) )),'|\t'
,cwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius decreases tolerance \t'
)

Output:

contains:          |     [12,0], radius=3 |      [12,0], radius=-3 |     [0,0]|
counterclockwise:  |                 True |                  False |     True |  => radius increases tolerance 
clockwise:         |                False |                   True |     True |  => radius decreases tolerance 

凸路径的解决方案:

我想到的唯一想法是强制路径逆时针方向并根据此使用半径。

import matplotlib.path as path
import numpy as np


verts=np.array([[-11.5,  16. ],[-11.5, -16. ],[ 11.5, -16. ],[ 11.5,  16. ],[-11.5,  16. ]])

#comment following line out to make isCounterClockWise crash
#verts=np.array([[-11.5,  16. ],[-10,0],[-11.5, -16. ],[ 11.5, -16. ],[ 11.5,  16. ],[-11.5,  16. ]])

ccwPath=path.Path(verts, closed=True) 
cwPath=path.Path(verts[::-1,:], closed=True) 

testPoint=[12,0]

def isCounterClockWise(myPath):

        #directions from on vertex to the other
        dirs=myPath.vertices[1:]-myPath.vertices[0:-1]
        #rot: array of rotations at ech edge
        rot=np.cross(dirs[:-1],dirs[1:]) 
        if len(rot[rot>0])==len(rot):
            #counterclockwise
            return True
        elif len(rot[rot<0])==len(rot):
            #clockwise
            return False
        else:
            assert False, 'no yet implemented: This case applies if myPath is concave'

def forceCounterClockWise(myPath):
    if not isCounterClockWise(myPath):
        myPath.vertices=myPath.vertices[::-1]


forceCounterClockWise(cwPath)
print('contains:         ','|\t', '[12,0], radius=3','|\t', '[12,0], radius=-3','|\t', '[0,0]|')

print('counterclockwise: ','|\t'
,'{0:>16s}'.format(str(ccwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(ccwPath.contains_point(testPoint,radius=-3) )),'|\t'
,ccwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius increases tolerance \t'
)

print('forced ccw:      ','|\t'
,'{0:>16s}'.format(str(cwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(cwPath.contains_point(testPoint,radius=-3) )),'|\t'
,cwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius increases tolerance \t'
)

给出以下输出:

contains:          |     [12,0], radius=3 |      [12,0], radius=-3 |     [0,0]|
counterclockwise:  |                 True |                  False |     True |  => radius increases tolerance 
forced ccw:       |                  True |                  False |     True |  => radius increases tolerance 

代码注释中给出了此解决方案失败的示例(对于凹路径)。

我的问题:

  1. 有谁知道,为什么会出现这种不一致?
  2. 有没有更优雅的方法来规避这个问题?示例可能是:使用 contains_point 的其他库、以更智能/正确的方式使用半径参数或使用预定义函数查找路径的方向。

我认为这里唯一错误的假设是“沿途留下的一切都包括在内。”。反而,contains_point字面意思是闭合路径是否包含点。

The radius然后定义为

  • 当路径逆时针方向延伸时,将路径扩展至
  • 当路径顺时针旋转时缩小路径

这在以下示例中显示,其中对于(逆时针)路径,绘制了扩展/缩小区域中包含的点。 (红色=not contains_point, 蓝色 =contains_point)

enter image description here

import matplotlib.pyplot as plt
import matplotlib.path as path
import matplotlib.patches as patches
import numpy as np

verts=np.array([[-1,  1 ],[-1, -1 ],[ 1, -1 ],[ 1, 0 ],[ 1,  1],[-1,  1 ]])

ccwPath=path.Path(verts, closed=True) 
cwPath=path.Path(verts[::-1,:], closed=True) 

paths = [ccwPath, cwPath]
pathstitle = ["ccwPath", "cwPath"]
radii = [1,-1]

testPoint=(np.random.rand(400,2)-.5)*4

c = lambda p,x,r: p.contains_point(x,radius=r)

fig, axes = plt.subplots(nrows=len(paths),ncols=len(radii))

for j  in range(len(paths)):
    for i in range(len(radii)):
        ax = axes[i,j]
        r = radii[i]
        patch = patches.PathPatch(paths[j], fill=False, lw=2)
        ax.add_patch(patch)
        col = [c(paths[j], point[0], r) for point in zip(testPoint)]
        ax.scatter(testPoint[:,0], testPoint[:,1], c=col, s=8, vmin=0,vmax=1, cmap="bwr_r")
        ax.set_title("{}, r={}".format(pathstitle[j],radii[i]) )

plt.tight_layout()
plt.show()

一个似乎根本没有记录的特殊性是radius实际上扩大或缩小了路径radius/2.。如上图所示,半径为1, 之间的点-1.5 and 1.5包含在内而不是之间的点-2 and 2.

关于路径的方向,可能没有一个固定的方向。如果有 3 个点,则方向可以明确确定为顺时针、逆时针(或共线)。一旦你有了更多的点,方向的概念就没有很好的定义。

一个选项可能是检查路径是否“大部分是逆时针”。

def is_ccw(p):
    v = p.vertices-p.vertices[0,:]
    a = np.arctan2(v[1:,1],v[1:,0])
    return (a[1:] >= a[:-1]).astype(int).mean() >= 0.5

这将允许调整radius如果是“大部分顺时针”路径,

r = r*is_ccw(p) - r*(1-is_ccw(p))

这样,正半径始终会扩展路径,负半径始终会缩小路径。

完整示例:

import matplotlib.pyplot as plt
import matplotlib.path as path
import matplotlib.patches as patches
import numpy as np

verts=np.array([[-1,  1 ],[-1, -1 ],[ 1, -1 ],[ 1, 0 ],[ 1,  1],[-1,  1 ]])

ccwPath=path.Path(verts, closed=True) 
cwPath=path.Path(verts[::-1,:], closed=True) 

paths = [ccwPath, cwPath]
pathstitle = ["ccwPath", "cwPath"]
radii = [1,-1]

testPoint=(np.random.rand(400,2)-.5)*4

c = lambda p,x,r: p.contains_point(x,radius=r)

def is_ccw(p):
    v = p.vertices-p.vertices[0,:]
    a = np.arctan2(v[1:,1],v[1:,0])
    return (a[1:] >= a[:-1]).astype(int).mean() >= 0.5

fig, axes = plt.subplots(nrows=len(radii),ncols=len(paths))

for j  in range(len(paths)):
    for i in range(len(radii)):
        ax = axes[i,j]
        r = radii[i]
        isccw = is_ccw(paths[j]) 
        r = r*isccw - r*(1-isccw)
        patch = patches.PathPatch(paths[j], fill=False, lw=2)
        ax.add_patch(patch)
        col = [c(paths[j], point[0], r) for point in zip(testPoint)]
        ax.scatter(testPoint[:,0], testPoint[:,1], c=col, s=8, vmin=0,vmax=1, cmap="bwr_r")
        ax.set_title("{}, r={} (isccw={})".format(pathstitle[j],radii[i], isccw) )

plt.tight_layout()
plt.show()

enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

matplotlib.Path.contains_points:“半径”参数定义不一致 的相关文章

  • 以 str.format 切片字符串

    我想实现以下目标str format x y 1234 5678 print str x 2 str y 2 我能够做到这一点的唯一方法是 print 0 1 format str x 2 str y 2 现在 这是一个例子 我真正拥有的是
  • 在 Django 中使用 prefetch_lated 连接 ManyToMany 字段

    我可能遗漏了一些明显的东西 但我在连接 ManyToMany 字段以在 Django 应用程序中工作时遇到问题 我有两个模型 class Area models Model name CharField class Role models
  • 在 Python 中同时插入行

    我正在尝试对我的代码进行矢量化 但遇到了障碍 我有 nxd x 值数组 x1 xn 其中每一行 x1 有很多点 x11 x1d nxd y 值数组 y1 y2 y3 其中每一行 y1 有很多点 y11 y1d x 值的 nx1 数组 x 1
  • 如何将 UPX 与 pyinstaller 一起使用?

    如何将 UPX 与 pyinstaller 一起使用 我正在关注文档 我已经下载了UPX 我的文件如下所示 import csv import selenium import pandas print Hello 然后我运行 pyinsta
  • python中嵌套字典值的总和

    我有一本这样的字典 data 11L a 2 b 1 a 2 b 3 22L a 3 b 2 a 2 b 5 a 4 b 2 a 1 b 5 a 1 b 0 33L a 1 b 2 a 3 b 5 a 5 b 2 a 1 b 3 a 1 b
  • 将数字转换为整数列表[重复]

    这个问题在这里已经有答案了 我该如何写magic下面的函数 gt gt gt num 123 gt gt gt lst magic num gt gt gt gt gt gt print lst type lst 1 2 3
  • 使用 Python 将阿拉伯语或任何从右到左书写系统的字符串打印到 Linux 终端

    非常简单的例子是 city print city 我期望输出是 但实际上输出是相反的字符串 字母看起来有点不同 因为它们有开始 中间和结束形式 我无法将其粘贴到此处 因为复制粘贴会再次更正字符串的顺序 如何在 Linux 终端上正确打印阿拉
  • 按字符串子字符串的列过滤 Pandas 数据框

    我正在尝试使用列中的字符串值是数据框外部字符串的子字符串的条件来过滤数据框 下面的例子 df a b c hello bye hello reference str hello there output a c 一种方法可能是使用正则表达式
  • Python 中嵌套列表的排序和分组

    我有以下数据结构 列表的列表 4 21 1 14 2008 10 24 15 42 58 3 22 4 2somename 2008 10 24 15 22 03 5 21 3 19 2008 10 24 15 45 45 6 21 1 1
  • 如何使用appium自动化Android手机后退按钮

    我正在使用 Appium python 客户端库 对 Android 上的混合移动应用程序进行测试自动化 我无法找到任何方法来自动化或创建手势以使用 电话后退 按钮返回到应用程序的上一页 有没有可以使用的驱动函数 我尝试了 self dri
  • Pygooglevoice登录错误

    另一个人问了这个问题 但没有回复 所以我再问一遍 我正在尝试使用 pygooglevoice API 但是当我运行 SMS py 示例脚本时 它给了我一个登录错误 我已经安装了 Enthought python 我想也许我还需要安装其他东西
  • 如何从 Python 3.5 降级到 3.4

    我想安装 kivy 链接在这里 https kivy org docs installation installation windows html install win dist 用于项目 但是 当尝试使用 pip 安装它所依赖的包时
  • 在 python 中使用 subprocess.call 时如何将 stdout 重定向到文件?

    我正在从另一个 python 脚本 A 调用一个 python 脚本 B 使用 subprocess call 如何将 B 的标准输出重定向到指定的文件 我正在使用 python 2 6 1 传递一个文件作为stdout参数为subproc
  • 保存游戏最高分?

    我使用 pygame 在 python 中制作了一个非常简单的游戏 分数取决于玩家达到的级别 我将级别作为变量称为score 我想在游戏开始或结束时显示顶级 我会更乐意显示多个分数 但我见过的所有其他线程都太复杂 我无法理解 所以请保持简单
  • Tkinter 如何根据此组合框自动更新第二个组合框

    我在 Tkinter Python 中遇到了组合框更新的问题 我有两个组合框 组合框A with values A B C and 组合框B 我想要的是 当值A在组合框中选择A然后在组合框中B显示值 1 2 3 当值B在组合框中选择A然后在
  • 如何使用资源模块来衡量函数的运行时间?

    我想使用Python代码测量函数的CPU运行时间和挂钟运行时间 此处建议资源模块 如何以 Python 代码 不是从终端 的形式分别测量函数的 CPU 运行时间和挂钟运行时间 https stackoverflow com q 192046
  • Python 包安装:pip 与 yum,还是两者一起安装?

    我刚刚开始管理 Hadoop 集群 我们使用 Bright Cluster Manager 直至操作系统级别 CentOS 7 1 然后使用 Ambari 以及适用于 Hadoop 的 Hortonworks HDP 2 3 我不断收到安装
  • Pandas:按日历周分组,然后绘制真实日期时间的分组条形图

    EDIT 我找到了一个非常好的解决方案并将其发布在下面作为答案 结果将如下所示 您可以为此问题生成一些示例数据 codes list ABCDEFGH dates pd Series pd date range 2013 11 01 201
  • 混合两个列表的Pythonic方法[重复]

    这个问题在这里已经有答案了 我有两个长度为 n 和 n 1 的列表 a 1 a 2 a n b 1 b 2 b n 1 我想要一个函数作为结果给出一个列表 其中包含两个中的替代元素 即 b 1 a 1 b n a n b n 1 以下方法有
  • 重新安装后使用 pandas dataframes 时出现问题

    我已经重新安装了 Python 和 Anaconda 现在面临以下问题 在我将 pkl 文件加载到数据帧并尝试 查看 该文件后 如下所示 df pd read pickle example pkl df 我收到错误 AttributeErr

随机推荐