在点周围画椭圆

4
我正在尝试使用matplotlib在图表上绘制一组点的椭圆。我希望能够获得类似于下面这个图像的效果: enter image description here 例如,一个组的数据集(例如红色组)可能如下所示:
[[-23.88315146  -3.26328266]  # first point
 [-25.94906669  -1.47440904]  # second point
 [-26.52423229  -4.84947907]]  # third point

我可以轻松地在图表上绘制点,但是我遇到了绘制椭圆的问题。
椭圆的直径为2 * 标准偏差,其中心点坐标为(x_mean, y_mean)。一个椭圆的宽度等于x标准偏差* 2。它的高度等于y标准偏差* 2
然而,我不知道如何计算椭圆的角度(您可以看到图片上的椭圆并不完全垂直)。
您有关于如何解决这个问题的想法吗?
注意:这个问题是LDA问题(线性判别分析)的简化版本。我试图将问题简化到最基本的表达方式。
3个回答

2
这是一个经过深入研究的问题。首先对你想要包含的点集进行 凸包 操作。然后按照文献中所述进行计算。以下提供两个来源。

"Smallest Enclosing Ellipses--An Exact and Generic Implementation in C++" (摘要链接).


          Ellipse


Charles F. Van Loan. "Using the Ellipse to Fit and Enclose Data Points." (PDF 下载).


1

我编写了一个简单的函数来实现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

0

这与数学比编程更相关 ;)

既然您已经有了尺寸,只想找到角度,那么这是我会做的事情(基于我的直觉):

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

一旦找到最佳拟合线,您可以使用斜率作为您的角度。

最小二乘线性回归

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

这里有一个视频解释它是如何工作的

假设您有一个数据集: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)进行转换。
将这个与您已经拥有的尺寸和位置结合起来,您就可以得到一个椭圆 ;)

这是我解决这个特定问题的方法,但可能有一千种不同的方法也可以工作,而且可能比我提出的更好(更复杂)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接