检查点是否比contains_point方法更快地在椭圆内

时间:2016-05-04 14:55:46

标签: python python-3.x matplotlib ellipse

我使用matplotlib 1.15.1,我尝试生成这样的散点图:

example

椭圆有固定尺寸,用中心坐标,宽度,高度和角度绘制(从外面提供):我不知道它们的等距是什么。

g_ell_center = (0.8882, 0.8882)
g_ell_width = 0.36401857095483
g_ell_height = 0.16928136341606
g_ellipse = patches.Ellipse(g_ell_center, g_ell_width, g_ell_height, angle=angle, fill=False, edgecolor='green', linewidth=2)

这个省略号应标记我的情节上的正常和半正常数据。 然后,我有一个~500点的数组,必须根据它们所属的椭圆进行着色。所以我尝试用contains_point方法检查每个点:

colors_array = []
colors_scheme = ['green', 'yellow', 'black']
for point in points_array:
    if g_ellipse.contains_point(point, radius=0):
        colors_array.append(0)
    elif y_ellipse.contains_point(point, radius=0):
        colors_array.append(1)
    else:
        colors_array.append(2)

最后,得出了一些观点:

plt.scatter(x_array, y_array, s=10, c=[colors_scheme[x] for x in colors_array], edgecolor="k", linewidths=0.3)

但contains_point非常慢!对于300点散点图,它工作了5分钟,我必须并行生成数千个散点图。也许有更快的方法? 附:整个项目绑定到matplotlib,我不能使用其他库。

2 个答案:

答案 0 :(得分:9)

考虑到椭圆的中心,宽度,高度和角度,这种方法应测试一个点是否在椭圆内。您可以找到相对于椭圆中心的点x和y坐标,然后将使用该角度的坐标转换为沿长轴和短轴的坐标。最后,您可以找到该点与单元格中心的归一化距离,其中椭圆上的距离为1,内部小于1,外部大于1。

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

fig,ax = plt.subplots(1)
ax.set_aspect('equal')

# Some test points
x = np.random.rand(500)*0.5+0.7
y = np.random.rand(500)*0.5+0.7

# The ellipse
g_ell_center = (0.8882, 0.8882)
g_ell_width = 0.36401857095483
g_ell_height = 0.16928136341606
angle = 30.

g_ellipse = patches.Ellipse(g_ell_center, g_ell_width, g_ell_height, angle=angle, fill=False, edgecolor='green', linewidth=2)
ax.add_patch(g_ellipse)

cos_angle = np.cos(np.radians(180.-angle))
sin_angle = np.sin(np.radians(180.-angle))

xc = x - g_ell_center[0]
yc = y - g_ell_center[1]

xct = xc * cos_angle - yc * sin_angle
yct = xc * sin_angle + yc * cos_angle 

rad_cc = (xct**2/(g_ell_width/2.)**2) + (yct**2/(g_ell_height/2.)**2)

colors_array = []

for r in rad_cc:
    if r <= 1.:
        # point in ellipse
        colors_array.append('green')
    else:
        # point not in ellipse
        colors_array.append('black')

ax.scatter(x,y,c=colors_array,linewidths=0.3)

plt.show()

enter image description here

注意,整个脚本需要0.6秒才能运行并处理500个点。这包括创建和保存数字等。

在我的macbook pro上计算点的距离和颜色需要0.00017秒。

答案 1 :(得分:2)

您当前的实施应该只调用contains_point 25,000到50,000次,这不是很多。所以,我猜测contains_point的实现是针对精确而非速度的。

由于你有一个点的分布,其中只有一小部分将在任何给定的椭圆中,因此大多数很少会在任何给定的椭圆附近,你可以很容易地使用直角坐标作为捷径来确定是否点足够接近椭圆,值得调用if point.x >= ellipse_left and point.x <= ellipse_right and _ point.y >= ellipse_top and point.y <= ellipse_bottom: if ellipse.contains_point(point, radius=0): ... use the contained point here

计算椭圆的左右x坐标和顶部和底部y坐标,可能需要一些填充来解释渲染差异,然后检查点是否在那些内,例如下面的伪代码:< / p>

contains_point

这种方法消除了大多数点的昂贵计算,允许简单的比较而不是排除明显的不匹配,同时保持计算的准确性,其中点足够接近它可能在椭圆中。如果是只有1%的点位于给定椭圆附近的任何位置,这种方法将消除99%的import org.joda.time.*; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.joda.time.LocalDate; LocalTime startTime2; LocalTime airTime2; LocalTime foamTime2; LocalTime scTime22; firstTime = airTime2; secondTime = localTime; return firstTime.compareTo(secondTime); 调用,而是用更快的比较替换它们。