Gert
Gert

Reputation: 55

scipy curve_fit returns initial estimates

To fit a hyperbolic function I am trying to use the following code:

import numpy as np
from scipy.optimize import curve_fit

def hyperbola(x, s_1, s_2, o_x, o_y, c):
    # x > Input x values
    # s_1 > slope of line 1
    # s_2 > slope of line 2
    # o_x > x offset of crossing of asymptotes
    # o_y > y offset of crossing of asymptotes
    # c   > curvature of hyperbola

    b_2 = (s_1 + s_2) / 2
    b_1 = (s_2 - s_1) / 2

    return o_y + b_1 * (x - o_x) + b_2 * np.sqrt((x - o_x) ** 2 + c ** 2 / 4)

min_fit = np.array([-3.0, 0.0, -2.0, -10.0, 0.0])
max_fit = np.array([0.0, 3.0, 3.0, 0.0, 10.0])
guess = np.array([-2.5/3.0, 4/3.0, 1.0, -4.0, 0.5])

vars, covariance = curve_fit(f=hyperbola, xdata=n_step, ydata=n_mean, p0=guess, bounds=(min_fit, max_fit))

Where n_step and n_mean are measurement values generated earlier on. The code runs fine and gives no error message, but it only returns the initial guess with a very small change. Also, the covariance matrix contains only zeros. I tried to do the same fit with a better initial guess, but that does not have any influence. Further, I plotted the exact same function with the initial guess as input and that gives me indeed a function which is close to the real values. Does anyone know where I make a mistake here? Or do I use the wrong function to make my fit?

Upvotes: 1

Views: 3682

Answers (1)

fuglede
fuglede

Reputation: 18221

The issue must lie with n_step and n_mean (which are not given in the question as currently stated); when trying to reproduce the issue with some arbitrarily chosen set of input parameters, the optimization works as expected. Let's try it out.

First, let's define some arbitrarily chosen input parameters in the given parameter space by

params = [-0.1, 2.95, -1, -5, 5]

Let's see what that looks like:

import matplotlib.pyplot as plt
xs = np.linspace(-30, 30, 100)
plt.plot(xs, hyperbola(xs, *params))

enter image description here

Based on this, let us define some rather crude inputs for xdata and ydata by

xdata = np.linspace(-30, 30, 10)
ydata = hyperbola(xs, *params)

With these, let us run the optimization and see if we match our given parameters:

vars, covariance = curve_fit(f=hyperbola, xdata=xdata, ydata=ydata, p0=guess, bounds=(min_fit, max_fit))
print(vars)  # [-0.1   2.95 -1.   -5.    5.  ]

That is, the fit is perfect even though our params are rather different from our guess. In other words, if we are free to choose n_step and n_mean, then the method works as expected.

In order to try to challenge the optimization slightly, we could also try to add a bit of noise:

np.random.seed(42)
xdata = np.linspace(-30, 30, 10)
ydata = hyperbola(xdata, *params) + np.random.normal(0, 10, size=len(xdata))
vars, covariance = curve_fit(f=hyperbola, xdata=xdata, ydata=ydata, p0=guess, bounds=(min_fit, max_fit))
print(vars)  #  [ -1.18173287e-01   2.84522636e+00  -1.57023215e+00  -6.90851334e-12   6.14480856e-08]
plt.plot(xdata, ydata, '.')
plt.plot(xs, hyperbola(xs, *vars))

enter image description here

Here we note that the optimum ends up being different from both our provided params and the guess, still within the bounds provided by min_fit and max_fit, and still provided a good fit.

Upvotes: 2

Related Questions