繁体   English   中英

围绕点绘制椭圆

[英]Draw ellipses around points

我正在尝试用 matplotlib 在图形上的一组点周围绘制椭圆。我想获得这样的东西:

在此处输入图像描述

一组数据集(例如红色组)可能如下所示:

[[-23.88315146  -3.26328266]  # first point
 [-25.94906669  -1.47440904]  # second point
 [-26.52423229  -4.84947907]]  # third point

我可以轻松地在图表上绘制点,但在绘制椭圆时遇到问题。

椭圆的直径为2 * standard deviation ,其中心坐标为(x_mean, y_mean) 一个椭圆的宽度等于x standard deviation * 2 它的高度等于y standard deviation * 2

但是,我不知道如何计算椭圆的角度(你可以在图片上看到椭圆不是完全垂直的)。

你知道怎么做吗?

注:本题是LDA问题(线性判别分析)的简化。 我试图将问题简化为最基本的表达方式。

这与数学有很大关系,而不是编程;)

既然你已经拥有尺寸并且只想找到角度,那么这就是我要做的事情(根据我的直觉):

尝试找到最适合给定点集(趋势线)的线,这也称为线性回归 有几种方法可以做到这一点,但最小二乘法是一个相对简单的方法(见下文)。

找到最佳拟合线后,可以使用斜率作为角度。

最小二乘线性回归

最小二乘线性回归方法用于找到趋势线的斜率,正是我们想要的。

这是一段视频,解释它如何工作的

假设你有一个数据集: data = [(x1, y1), (x2, y2), ...]

使用最小二乘法,您的斜率将是:

# I see in your example that you already have x_mean and y_mean
# No need to calculate them again, skip the two following lines
# and use your values in the rest of the example
avg_x = sum(element[0] for element in data)/len(data)
avg_y = sum(element[1] for element in data)/len(data)

x_diff = [element[0] - avg_x for element in data]
y_diff = [element[1] - avg_y for element in data]

x_diff_squared = [element**2 for element in x_diff]

slope = sum(x * y for x,y in zip(x_diff, y_diff)) / sum(x_diff_squared)

一旦你拥有了,你就快完成了。 斜率等于角度slope = tan(angle)的正切值

使用python的math模块angle = math.atan(slope)这将返回以弧度表示的角度。 如果你想要它在度数你必须使用math.degrees(angle)转换它

将它与你已经拥有的尺寸和位置相结合,你就得到了一个椭圆;)


这就是我如何解决这个特殊问题,但是可能有一千种不同的方法也可以工作,最终可能比我建议的更好(也更复杂)。

这是一个研究得很好的问题。 首先采用您希望包含的点集的凸包 然后执行文献中描述的计算。 我在下面提供两个来源。

“最小的封闭椭圆 - C ++中的精确和通用实现”( 摘要链接 )。


椭圆


Charles F. Van Loan。 “使用椭圆来拟合和包含数据点。” PDF下载 )。

我写了一个简单的 function 来实现 Mathieu David 的解决方案。 我确信有很多方法可以做到这一点,但这对我的应用程序有效。

    def get_ellipse_params(self, points):
        ''' Calculate the parameters needed to graph an ellipse around a cluster of points in 2D.

            Calculate the height, width and angle of an ellipse to enclose the points in a cluster.
            Calculate the width by finding the maximum distance between the x-coordinates of points
            in the cluster, and the height by finding the maximum distance between the y-coordinates
            in the cluster. Multiple both by a scale factor to give padding around the points when
            constructing the ellipse. Calculate the angle by taking the inverse tangent of the
            gradient of the regression line. Note that tangent solutions repeat every 180 degrees,
            and so to ensure the correct solution has been found for plotting, add a correction
            factor of +/- 90 degrees if the magnitude of the angle exceeds 45 degrees.

            Args:
                points (ndarray): The points in a cluster to enclose with an ellipse, containing n
                                  ndarray elements representing each point, each with d elements
                                  representing the coordinates for the point.

            Returns:
                width (float):  The width of the ellipse.
                height (float): The height of the ellipse.
                angle (float):  The angle of the ellipse in degrees.
        '''
        if points.ndim == 1:
            width, height, angle = 0.1, 0.1, 0
            return width, height, angle

        else:
            SCALE = 2.5
            width = np.amax(points[:,0]) - np.amin(points[:,0])
            height = np.amax(points[:,1]) - np.amin(points[:,1])
            
            # Calculate angle
            x_reg, y_reg = [[p[0]] for p in points], [[p[1]] for p in points]
            grad = LinearRegression().fit(x_reg, y_reg).coef_[0][0]
            angle = np.degrees(np.arctan(grad))

            # Account for multiple solutions of arctan
            if angle < -45: angle += 90
            elif angle > 45: angle -= 90

            return width*SCALE, height*SCALE, angle

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM