如何计算python中x,y坐标的质心

时间:2013-10-30 14:25:58

标签: python points coordinate-systems centroid

我有很多x,y坐标,我根据它们之间的距离聚集了这些坐标。 现在我想计算每个x,y坐标簇的质心量度。 有没有办法做到这一点?

我的坐标格式为:

    coordinates_cluster = [[x1,x2,x3,...],[y1,y2,y3,...]]

每个群集的最小长度为3个点,所有点都可以包含负值和正值x和y值。 我希望有人可以帮助我。

最佳, 马丁

(我在Windows 7系统上使用带有canopy 1.1.1(32位)的python 2.7。)

3 个答案:

答案 0 :(得分:6)

我意识到这并不难,但这里是用于计算x,y坐标的质心的代码:

    >>> c = [[1,4,-5],[3,-2,9]] # of the form [[x1,x2,x3],[y1,y2,y3]]
    >>> centroide = (sum(c[0])/len(c[0]),sum(c[1])/len(c[1]))

    >>> centroide
    (0, 3)

答案 1 :(得分:1)

恕我直言,此处给出的公认答案不适用于典型的现实生活用例,在这些用例中,您要计算由一组 (x,y) 顶点(又名多边形)定义的形状的质心。所以请原谅我回答了一个大约 8 年前提出的问题,但它仍然在我的 SO 搜索中名列前茅,所以它也可能会出现在其他人身上。我并不是说在问题的特定情况下接受的答案是错误的,但我认为,大多数发现此线程的人实际上是根据不同的定义寻找质心。

质心未定义为顶点的算术平均值

...这与普遍看法相反。 我们不得不承认,通常通过质心,我们想到的是“图中所有点的算术平均位置。非正式地,它是形状的切口可以在针尖上完美平衡的点”(引用维基百科这里引用的实际文献)。请注意,它是图中的所有点,而不仅仅是顶点坐标的平均值。 这正是,如果您接受大多数 SO 答案,您会出错的地方,这意味着质心是顶点的 x 和 y 坐标的算术平均值,并将其应用于您可能通过执行实验收集的现实生活数据.
描述形状的点的密度可能会沿形状的线变化。这只是所述方法的许多可能限制之一。那么坐标的简单平均值肯定不是您想要的。我会用一个例子来说明这一点。

示例

click (not allowed to post images yet...)
在这里,我们看到一个由 8 个顶点组成的多边形。我们的直觉正确地告诉我们,我们可以在 (x,y)=(0,0) 处的针尖上平衡这个形状,使质心 (0,0)。但是在 (-1,1) 周围的区域中,我们用来描述这个多边形的点/顶点的密度高于沿线的其他区域。现在如果我们通过取顶点的平均值来计算质心,结果将被拉向高密度区域。
点“质心多边形”对应于真正的质心。这个点是通过实现这里描述的算法来计算的:https://en.wikipedia.org/wiki/Centroid#Of_a_polygon(唯一的区别:它返回面积的绝对值)
它适用于由 N 个顶点的 x 和 y 坐标描述的图形,如 X = x_0, x_1, ..., x_(N-1),对 Y 也是如此。这个图形可以是任何多边形,只要它是非自相交的并且顶点按出现的顺序给出。
这可用于计算例如matplotlib 轮廓线的“真实”质心。

代码

下面是上面例子的代码和算法的实现:

import matplotlib.pyplot as plt

def centroid_poly(X, Y):
    """https://en.wikipedia.org/wiki/Centroid#Of_a_polygon"""
    N = len(X)
    # minimal sanity check
    if not (N == len(Y)): raise ValueError('X and Y must be same length.')
    elif N < 3: raise ValueError('At least 3 vertices must be passed.')
    sum_A, sum_Cx, sum_Cy = 0, 0, 0
    last_iteration = N-1
    # from 0 to N-1
    for i in range(N):
        if i != last_iteration:
            shoelace = X[i]*Y[i+1] - X[i+1]*Y[i]
            sum_A  += shoelace
            sum_Cx += (X[i] + X[i+1]) * shoelace
            sum_Cy += (Y[i] + Y[i+1]) * shoelace
        else:
            # N-1 case (last iteration): substitute i+1 -> 0
            shoelace = X[i]*Y[0] - X[0]*Y[i]
            sum_A  += shoelace
            sum_Cx += (X[i] + X[0]) * shoelace
            sum_Cy += (Y[i] + Y[0]) * shoelace
    A  = 0.5 * sum_A
    factor = 1 / (6*A)
    Cx = factor * sum_Cx
    Cy = factor * sum_Cy
    # returning abs of A is the only difference to
    # the algo from above link
    return Cx, Cy, abs(A)

# ********** example ***********
X = [-1, -0.8,  -0.6,  1,   2,  1, -1,   -2]
Y = [ 1,    1,     1,  1, 0.5, -1, -1, -0.5]

Cx, Cy, A = centroid_poly(X, Y)

# calculating centroid as shown by the accepted answer
Cx_accepted = sum(X)/len(X)
Cy_accepted = sum(Y)/len(Y)

fig, ax = plt.subplots()
ax.scatter(X, Y, label='vertices')
ax.scatter(Cx_accepted, Cy_accepted, label="mean of vertices")
ax.scatter(Cx, Cy, label='centroid poly')

# just so the line plot connects xy_(N-1) and xy_0
X.append(X[0]), Y.append(Y[0])
ax.plot(X, Y, label='polygon')

ax.legend(bbox_to_anchor=(1, 1))
ax.grid(), ax.set_aspect('equal')

答案 2 :(得分:0)

如果您对计算几何或信号处理 [12] 中定义的质心感兴趣:

import numpy as np
# a line from 0,0 to 1,1
x = np.linspace(0, 1, 100)
y = np.linspace(0, 1, 100)
cx = np.dot(x, y) / np.sum(y)
  
    

0.67003367003367

  

enter image description here