Python 多维点的几何中值

Python 多维点的几何中值,python,numpy,scipy,Python,Numpy,Scipy,我有一个3D点阵列: a = np.array([[2., 3., 8.], [10., 4., 3.], [58., 3., 4.], [34., 2., 43.]]) 如何计算这些点的平均值?使用Weiszfeld的迭代算法计算几何中值是在Python中实现的,或者在下面从软件(CeCILL-C许可证)复制的函数中实现的 将numpy导入为np 输入数学 进口警告 def几何_中值(X,numIter=200): """ 计算点采样的几何中值。 几何中值坐标将在空间图像参考系统中表示(而不

我有一个3D点阵列:

a = np.array([[2., 3., 8.], [10., 4., 3.], [58., 3., 4.], [34., 2., 43.]])

如何计算这些点的平均值?

使用Weiszfeld的迭代算法计算几何中值是在Python中实现的,或者在下面从软件(CeCILL-C许可证)复制的函数中实现的

将numpy导入为np
输入数学
进口警告
def几何_中值(X,numIter=200):
"""
计算点采样的几何中值。
几何中值坐标将在空间图像参考系统中表示(而不是在真实世界的度量中)。
我们使用Weiszfeld算法(http://en.wikipedia.org/wiki/Geometric_median)
:参数:
-`X`(列表| np.array)-体素坐标(3xN矩阵)
-`numIter`(int)-限制搜索全局最优值的长度
:返回:
-np.数组((x,y,z)):坐标的几何中值;
"""
#--将“中间值”初始化为质心
y=np.平均值(X,1)
#--如果初始点在点集中,我们将其移动:
而(y[0]在X[0]中)和(y[1]在X[1]中)以及(y[2]在X[2]中):
y+=0.1
收敛=假#布尔测试收敛到全局最优
dist=[]#记录距离变化的列表
#--最小化“X”中每个点与中值之间距离的平方和。
i=0
而((非收敛)和(i3:

收敛=(abs(dist[i]-dist[i-2])我实现了Yehuda Vardi和Zhang Jin Hui Zhang的几何中值算法,在他们的论文中进行了描述。所有内容都是用numpy矢量化的,所以应该非常快。我没有实现权重-只有未加权的点

import numpy as np
from scipy.spatial.distance import cdist, euclidean

def geometric_median(X, eps=1e-5):
    y = np.mean(X, 0)

    while True:
        D = cdist(X, [y])
        nonzeros = (D != 0)[:, 0]

        Dinv = 1 / D[nonzeros]
        Dinvs = np.sum(Dinv)
        W = Dinv / Dinvs
        T = np.sum(W * X[nonzeros], 0)

        num_zeros = len(X) - np.sum(nonzeros)
        if num_zeros == 0:
            y1 = T
        elif num_zeros == len(X):
            return y
        else:
            R = (T - y) * Dinvs
            r = np.linalg.norm(R)
            rinv = 0 if r == 0 else num_zeros/r
            y1 = max(0, 1-rinv)*T + min(1, rinv)*y

        if euclidean(y, y1) < eps:
            return y1

        y = y1
将numpy导入为np
从scipy.spatial.distance导入cdist,欧几里德
def几何平均值(X,eps=1e-5):
y=np.平均值(X,0)
尽管如此:
D=cdist(X,[y])
非零=(D!=0)[:,0]
Dinv=1/D[非零]
Dinvs=np.和(Dinv)
W=Dinv/Dinvs
T=np.和(W*X[非零],0)
num_zeros=len(X)-np.sum(非零)
如果num_zeros==0:
y1=T
elif num_zeros==len(X):
返回y
其他:
R=(T-y)*Dinvs
r=np.linalg.norm(r)
如果r==0,则rinv=0,否则数值为零/r
y1=最大(0,1-rinv)*T+最小(1,rinv)*y
如果欧几里德(y,y1)

除了默认的SO许可条款外,如果您愿意,我还将在zlib许可下发布上述代码。

这个问题可以通过
scipy
中的
minimize
模块轻松地近似。在这个模块中,它提供了各种优化算法,从nelder mead到newton CG。nelder mead算法特别适用于ul如果你不想为高阶导数而烦恼,那么代价就是失去一些精度。然而,你只需要知道要最小化的函数就可以了

现在,参考问题中的相同数组,如果我们使用@orlp的方法,我们将得到:

geometric_median(a)
# array([12.58942481,  3.51573852,  7.28710661])
对于Nelder-mead方法,您将看到以下内容。要最小化的函数是与所有点的距离函数,即

代码如下:

from scipy.optimize import minimize
x = [point[0] for point in a]
y = [point[1] for point in a]
z = [point[2] for point in a]

x0 = np.array([sum(x)/len(x),sum(y)/len(y), sum(z)/len(z)])
def dist_func(x0):
    return sum(((np.full(len(x),x0[0])-x)**2+(np.full(len(x),x0[1])-y)**2+(np.full(len(x),x0[2])-z)**2)**(1/2))
res = minimize(dist_func, x0, method='nelder-mead', options={'xtol': 1e-8, 'disp': True})
res.x
# array([12.58942487,  3.51573846,  7.28710679])
请注意,我使用所有点的平均值作为alogrithm的初始值。结果非常接近@orlp的方法,更精确。正如我所提到的,您牺牲了一点,但仍然得到了非常好的近似值

内尔德-米德算法的性能 为此,我生成了一个
test_数组
,其中10000个点来自正态分布,集中在3.2。因此,几何中值应该非常接近[3.2,3.2,3.2]

np.random.seed(3)
test_array = np.array([[np.random.normal(3.2,20),
                        np.random.normal(3.2,20),
                        np.random.normal(3.2,20)] for i in np.arange(10000)])
对于@orlp的方法

%timeit geometric_median(test_array)
# 12.1 ms ± 270 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# array([2.95151061, 3.14098477, 3.01468281])
对于内尔德·米德

%timeit res.x
# 565 ms ± 14.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# array([2.95150898, 3.14098468, 3.01468276])

@orlp的方法很快,而Nelder-mead也不错。然而,Nelder-mead方法是通用的,而@orlp的方法是特定于几何中值的。你想选择的方法取决于你的目的。如果你只想得到一个近似值,我会选择Nelder。如果你想精确,那么@orlp的方法更快、更准确e、

这有帮助吗:?@EdChum不这么认为,我已经研究了
np.median
的实现,它似乎是按照
分区来实现的,这不适合几何中值。这看起来很慢,因为它是纯Python的-我正在寻找一个快速的numpy/scipy解决方案。@orlp,一个快速的numpy/scipy解决方案需要代码是可以矢量化的。乍一看,这是否可行并不明显。我想这里的问题是:你必须以多快的速度完成。我想写一个
cython
版本的gist已经给了你很好的速度。使用这个答案中建议的c实现可能会更快。@rth它实际上看起来是quite Vectoriable to me,我会试一试。@orlp是的,你是对的。使用
scipy.spatial.distance.cdist
进行距离计算应该会加快速度。尽管由于算法的迭代性质,无法避免
while
循环。顺便说一句,如果你成功优化了解决方案,请随意贡献它请访问OpenAlea软件。@orlp为什么纯Python速度慢?您是否尝试过使用类似的编译器?I am curr
%timeit res.x
# 565 ms ± 14.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# array([2.95150898, 3.14098468, 3.01468276])