为什么列表的元组失败作为optimize.leasztsq的参数?

时间:2015-08-27 14:25:26

标签: python scipy format least-squares

我使用scipy.optimize中的函数leastsq来拟合3D坐标中的球体坐标和半径。

所以我的代码看起来像这样:

def distance(pc,point):

    xc,yc,zc,rd = pc
    x ,y ,z     = point
    return np.sqrt((xc-x)**2+(yc-y)**2+(zc-z)**2)

def sphere_params(coords):

    from scipy import optimize

    err = lambda pc,point : distance(pc,point) - pc[3]

    pc = [0, 0, 0, 1]
    pc, success = optimize.leastsq(err, pc[:], args=(coords,))

    return pc

(感谢:how do I fit 3D data。)

我开始使用变量coords作为元组列表(每个元组是x,y,z坐标):

>> coords
>> [(0,0,0),(0,0,1),(-1,0,0),(0.57,0.57,0.57),...,(1,0,0),(0,1,0)]

导致我出错:

>> pc = sphere_params(coords)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "/home/michel/anaconda/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 374, in leastsq
     raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m))
TypeError: Improper input: N=4 must not exceed M=3

其中N是存储在pc中的参数数量,M是数据点数量。这使我看起来没有给出足够的数据点而我的列表coords实际上重新组合了351个元组而不是pc中的4个参数!

从我在minipack中读到的内容,实际的罪魁祸首似乎是这一行(来自_check_func()):

res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))

除非我弄错了,否则我会将其转化为

res = atleast_1d(distance(*(pc[:len(pc)],) + args)

但是我正在努力理解这与_check_func()函数的其余部分有什么关系。

我最终将coords更改为数组,然后将其作为sphere_param()coords = np.asarray(coords).T的参数,它开始正常工作。我真的很想理解数据格式为什么会给我带来麻烦!

事先,非常感谢您的回答!

编辑:我注意到我对“距离”和“错误”功能的使用是强烈的真的不明智和误导,在原始代码中并非如此,所以它不是问题。现在更有意义。

3 个答案:

答案 0 :(得分:0)

虽然我还没有充分使用此功能,但我可以告诉coords按原样传递给distance函数。如果允许错误检查,至少会这样。实际上,错误检查可能会尝试执行此操作,并在distance引发错误时引发错误。所以试试吧。

In [91]: coords=[(0,0,0),(0,0,1),(-1,0,0),(0.57,0.57,0.57),(1,0,0),(0,1,0)]

In [92]: distance([0,0,0,0],coords)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-92-113da104affb> in <module>()
----> 1 distance([0,0,0,0],coords)

<ipython-input-89-64c557cd95e0> in distance(pc, coords)
      2 
      3         xc,yx,zx,rd = pc
----> 4         x ,y ,z     = coords
      5         return np.sqrt((xc-x)**2+(yc-y)**2+(zc-z)**2)
      6 

ValueError: too many values to unpack (expected 3)

这就是3来自你的x, y, z = coords

distance([0,0,0,0],np.array(coords))

同样的错误。

distance([0,0,0,0],np.array(coords).T)

超越了该问题(3行可以拆分为3个变量),引发了另一个错误:NameError: name 'yc' is not defined

这看起来像是你给我们的代码中的一个错误,顽皮,顽皮!。

纠正:

In [97]: def distance(pc,coords):

        xc,yc,zc,rd = pc
        x ,y ,z     = coords
        return np.sqrt((xc-x)**2+(yc-y)**2+(zc-z)**2)
   ....: 

In [98]: distance([0,0,0,0],np.array(coords).T)
Out[98]: array([ 0.        ,  1.        ,  1.        ,  0.98726896,  1.        ,  1.        ])

# and wrapping the array in a tuple, as `leastsq` does
In [102]: distance([0,0,0,0],*(np.array(coords).T,))
Out[102]: array([ 0.        ,  1.        ,  1.        ,  0.98726896,  1.        ,  1.        ])

我得到一个5元素的数组,每个&#39;点&#39;在coords。这就是你想要的吗?

您在哪里明白leastsq一次将coords一个元组提供给lambda

  

args:元组           func的任何额外参数都放在这个元组中。

通常使用这些optimize函数,您希望在一组条件上执行操作,然后需要迭代这些条件,在每个条件上调用优化。或者如果你想一次优化整个集合,那么你需要编写你的函数(错误等)来同时处理整个集合。

答案 1 :(得分:0)

您的err功能必须获取完整的坐标列表并返回完整的距离列表。然后leastsq将获取错误列表,将它们平方并求和,并最小化该平方和。

scipy.spatial.distance中也有距离函数,所以我建议:

from scipy.spatial.distance import cdist
from scipy.optimize import leastsq

def distance_cdist(pc, coords):
    return cdist([pc], coords).squeeze()

def distance_norm(pc, points):
    """ pc must be shape (D+1,) array
        points can be (N, D) or (D,) array """
    c = np.asarray(pc[:3])
    points = np.atleast_2d(points)
    return np.linalg.norm(points-c, axis=1)

def sphere_params(coords):
    err = lambda pc, coords: distance(pc[:3], coords) - pc[3]
    pc = [0, 0, 0, 1]
    pc, success = leastsq(err, pc, args=(coords,))
    return pc

coords = [(0,0,0),(0,0,1),(-1,0,0),(0.57,0.57,0.57),(1,0,0),(0,1,0)]
sphere_params(coords)

答案 2 :(得分:0)

所以这是我从以前的帮助中得出的结论:

import numpy as np
from scipy.optimize import leastsq

def a_dist(a,B):
    # works with - a : reference point - B : coordinates matrix
    return np.linalg.norm(a-B, axis=1) 

def parametric(coords):

    err = lambda pc,point : a_dist(pc,point) - 18

    pc = [0, 0, 0] # Initial guess for the parameters
    pc, success = leastsq(err, pc[:], args=(coords,))

    return pc

它绝对适用于元组列表和形状数组(N,3)

>>  cluster #it's more than 6000 point you won't have the same result
>>  [(4, 30, 19), (3, 30, 19), (5, 30, 19), ..., (4, 30, 3), (4, 30, 35)]
>>  sphere_params(cluster)
>>  array([ -5.25734467,  20.73419249,   9.73428766])

>>  np.asarray(cluster).shape
>>  (6017,3)
>>  sphere_params(np.asarray(cluster))
>>  array([ -5.25734467,  20.73419249,   9.73428766])

将此版本与Askewchan相结合,即:

def sphere_params(coords):
    err = lambda pc, coords: distance(pc[:3], coords) - pc[3]
    pc = [0, 0, 0, 1]
    pc, success = leastsq(err, pc, args=(coords,))
    return pc

也行得正常,说实话我没有花时间尝试你的解决方案。我绝对不会把半径作为拟合参数。我发现它根本不健壮(即使6000个数据点也不足以获得正确的曲率!)。

当与我的第一个代码比较时,我仍然不太确定出了什么问题,我可能搞砸了全局/局部变量,尽管我真的不记得在我的任何函数中使用任何“全局”语句。 / p>