0

Given data points in the xy plane, I would like to use scipy.optimize.leastsq to find fit parameters for an ellipse (which cannot be written as a function of x and y). I tried setting the entire equation equal to zero, and then fitting this function, but the fit is failing to converge with error output

"The relative error between two consecutive iterates is at most 0.000000."

The code is shown below, as well as the output. The fitter clearly does not find any reasonable parameters. My question is whether or not this is a problem with scipy.optimize.leastsq, or whether the "trick" of setting the function equal to zero and instead fitting that is not valid.

from scipy.optimize import leastsq, curve_fit
import numpy as np

import matplotlib.pyplot as plt

def function(x,y,theta,smaj,smin):
    xp = np.cos(theta)*x - np.sin(theta)*y
    yp = np.sin(theta)*x + np.cos(theta)*y

    z = ((xp)**2)/smaj**2 + ((yp)**2)/smin**2

    return z

def g(x,y,smaj,smin):
    return x*x/smaj**2 + y*y/smin**2


def window(array,alt,arange):
        arr = [array[i] for i,a in enumerate(alt) if a > arange[0] and a < arange[1]]
        return np.asarray(arr)

def fitter(p0,x,y,func,errfunc,err):
# the fitter function   
    out = leastsq(errfunc,p0,args=(x,y,func,err),full_output=1)
    pfinal = out[0]
    covar  = out[1]
    mydict = out[2]
    mesg   = out[3]
    ier    = out[4]
    resids = mydict['fvec']
    chisq  = np.sum(resids**2)
    degs_frdm     = len(x)-len(pfinal) 
    reduced_chisq = chisq/degs_frdm
    ls = [pfinal,covar,mydict,mesg,ier,resids,chisq,degs_frdm,reduced_chisq]
    print('fitter status: ', ier, '-- aka -- ', mesg)
    i = 0
    if covar is not None: 
        if (ier == 1 or ier == 2 or ier == 3 or ier == 4):
            for u in pfinal:
                print ('Param', i+1, ': ',u, ' +/- ', np.sqrt(covar[i,i])) 
                i = i + 1
            print ('reduced chisq',reduced_chisq)

        else:
            print('fitter failed')

    return ls

def func(x,y,p):
    x = x-p[3]
    y = y-p[4]
    xp = np.cos(p[0])*(x) - np.sin(p[0])*(y)
    yp = np.sin(p[0])*(x) + np.cos(p[0])*(y)
    z = ((xp)**2)/p[1]**2 + ((yp)**2)/p[2]**2 - 1

    return z

def errfunc(p,x,y,func,err):

    return (y-func(x,y,p))/err    


t = np.linspace(0,2*np.pi,100)
xx = 5*np.cos(t); yy = np.sin(t)


p0 = [0,5,1,0,0]
sigma = np.ones(len(xx))
fit = fitter(p0,xx,yy,func,errfunc,sigma)

params     = fit[0]
covariance = fit[1]
residuals  = fit[5]    
t = np.linspace(0,2*np.pi,100)
xx = 5*np.cos(t); yy = np.sin(t)
plt.plot(xx,yy,'bx',ms = 4)



xx = np.linspace(-10,10, 1000)
yy = np.linspace(-5, 5, 1000)
newx = []
newy = []
for x in xx:
    for y in yy:
        if 0.99 < func(x,y,params) < 1.01:
          #if g(x,y,5,1) == 1:
            newx.append(x)
            newy.append(y)


plt.plot(newx,newy,'kx',ms = 1)
plt.show()        

enter image description here The blue crosses are the actual data, and the black line is the fitters guess at the parameters.

Jbag1212
  • 167
  • 1
  • 5
  • Rewrite the ellipse function in terms of, e.g. `phi`, then fit for your semi-major and -minor axis (and possibly an offset)`, and finally translate back to x and y as necessary. Browse through https://en.wikipedia.org/wiki/Ellipse as necessary (look for the "Parametric representation" section). –  Nov 05 '17 at 00:44
  • As for question asked in the title: no, you can't. Perhaps if your "function" is tabulated, but even then the fitting routine runs into problems with values outside the table (where you'd have to interpolation or extrapolation, and thus would be describing a functional form). As per my previous comment, however, an ellipse can be written perfectly fine with as a function of a single parameter. –  Nov 05 '17 at 00:48
  • 1
    I show that 'yes you can' in https://stackoverflow.com/questions/41225893/drawing-elliptical-orbit-in-python-using-numpy-matplotlib/41232062#41232062 – f5r5e5d Nov 05 '17 at 03:21

0 Answers0