如何计算3D(或n-D)重心的最佳方法?

29

我在工作中的一个项目中需要计算三维空间点集的质心。目前,我采用一种看似简单但却很朴素的方法来计算 -- 即取每个点集的平均值,如下所示:

centroid = average(x), average(y), average(z)

其中xyz是浮点数数组。 我似乎记得有一种方法可以获得更精确的质心,但我没有找到一个简单的算法来实现这一点。 有人有任何想法或建议吗? 我正在使用Python进行此操作,但我可以从其他语言的示例进行调整。


我认为你的问题措辞可能不够优化。你问如何计算重心(而且每个人都告诉你“你做对了”),但你所寻找的可能是一个体积中心点,它不受样本点数量的影响,而是受它们在空间中的分布影响(正如@AlejoHausner的回答所建议的)。 - Chris
这个答案是否符合您的需求? - JeeyCi
9个回答

19

与通常的说法相反,定义(和计算)点云中心点的方法是不同的。第一种也是最常见的解决方法已经由你提出,我不会争辩这有任何问题:

centroid = average(x), average(y), average(z)

“问题”在于它会根据点的分布“扭曲”你的中心点。例如,如果你假设所有的点都在一个立方体或其他几何形状内,但大部分点都放在上半部分,你的中心点也将朝那个方向移动。

作为替代,你可以使用每个维度的数学中间值(极值的平均值)来避免这种情况:

middle = middle(x), middle(y), middle(z)

当你不太关心点的数量,更关心全局边界框时,可以使用此方法,因为它只是围绕你的点的边界框的中心。

最后,你也可以在每个维度上使用median(中间元素):

median = median(x), median(y), median(z)

现在,这将有点相反于middle,实际上可以帮助你忽略点云中的异常值并根据你的点的分布找到中心点。

寻找“好”的中心点更加稳健的方式也许是忽略每个维度的最高和最低10%,然后计算averagemedian。如你所见,可以用不同的方法定义中心点。下面我将展示两个2D点云的例子,考虑这些建议。

深蓝色点是平均质心。 绿色显示中位数。 红色显示中间值。

在第二张图片中,您将看到我之前所说的情况:绿点“更靠近”点云的最密集部分,而红点则距离它更远,考虑到点云的最极端边界。

在此输入图片描述 在此输入图片描述


12
你提到了“一种获取更准确质心的方法”。也许你在谈论一个不受异常值影响的质心。例如,美国的平均家庭收入可能非常高,因为少数非常富有的人使平均水平失真,他们就是“异常值”。因此,统计学家使用中位数。获取中位数的一种方式是对值进行排序,然后选择列表中间的值。
也许你正在寻找类似于此的内容,但适用于二维或三维点。问题是,在二维及以上,你无法排序,没有自然顺序。尽管如此,有方法可以摆脱异常值。
一种方法是找到点的凸包。凸包具有所有点在点集的“外部”。如果这样做,并且扔掉在凸包上的点,你将扔掉异常值,并且剩下的点会给出一个更加“代表性”的质心。你甚至可以多次重复这个过程,结果就像剥洋葱一样。实际上,它被称为“凸壳剥离”。

所以如果我理解正确的话,如果质心就像线性集合的平均值,那么凸包剥离是否能得到类似于中位数的点? - Marcel Levy
你是说你不能简单地分别对每个维度进行排序并使用平均值以外的其他方法吗? - Chris

12

是的,但对于某些应用程序可能不够好,因此需要注意细微差别。没有简单的方法可以提出一个对所有微妙方法都欢迎的问题。作者接受上面的答案就是一个例子。请包容那些超出理论数学范畴的方法,因为它们通常在现实中最有用。 - Can H. Tartanoglu

3
您是否考虑过使用提高准确度的求和方法——Kahan求和?

不,我并不是想在求平均值之前得到更准确的总和,如果你是这个意思的话。我只是想知道我是否正确地计算了质心。谢谢,不过--我甚至都没听说过这个。 - Marcel Levy

2
潜在更高效:如果你要计算多次,通过保留两个常驻变量,可以大大加快计算速度。
N  # number of points
sums = dict(x=0,y=0,z=0)  # sums of the locations for each point

每当创建或删除点时,就会更改N和sums。这将使计算的时间复杂度从O(N)变为O(1),但代价是每次创建、移动或删除点时需要进行更多的工作。


0
如果你的n维向量在一个列表里 [[a0, a1, ..., an],[b0, b1, ..., bn],[c0, c1, ..., cn]],只需将该列表转换为数组,然后像这样计算质心:
import numpy as np

vectors = np.array(Listv)
centroid = np.mean(vectors, axis=0)

0

“更准确的质心”我认为质心是按照您计算的方式定义的,因此不存在“更准确的质心”。


0

是的,那就是正确的公式。

如果你有大量的点,你可以利用问题的对称性(无论是圆柱形、球形还是镜像)。否则,你可以借鉴统计学的方法,对随机选取的一些点进行平均,这样就会有一些误差。


具体来说,随机选取的部分样本点的平均值是整个数据集平均值的无偏估计。 - Gregg Lind

-1

没错,你正在计算质心或平均向量。


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