3

Given that the fitting function is of type: enter image description here

I intend to fit such function to the experimental data (x,y=f(x)) that I have. But then I have some doubts:

  • How do I define my fitting function when there's a summation involved?

  • Once the function defined, i.e. def func(..) return ... is it still possible to use curve_fit from scipy.optimize? Because now there's a set of parameters s_i and r_i involved compared to the usual fitting cases where one has few single parameters.

  • Finally are such cases treated completely differently?

Feel a bit lost here, thanks for any help.

  • Did you mean to also have $x_i$, or is there really a single $x$? If the latter, your parameters are not identified. But if the former, you can just use `scipy.optimize.leastsq`. – Alan May 19 '15 at 11:39
  • @Alan $x$ is the variable in this case, but a discrete one. –  May 19 '15 at 14:22
  • @Alan Worst part is that I don't even know how to define such function (i.e. being a sum really) in python... –  May 19 '15 at 17:17

2 Answers2

3

This is very well within reach of scipy.optimize.curve_fit (or just scipy.optimize.leastsqr). The fact that a sum is involved does not matter at all, nor that you have arrays of parameters. The only thing to note is that curve_fit wants to give your fit function the parameters as individual arguments, while leastsqr gives a single vector.

Here's a solution:

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

def f(x,r,s):
    """ The fit function, applied to every x_k for the vectors r_i and s_i. """
    x = x[...,np.newaxis]  # add an axis for the summation
    # by virtue of numpy's fantastic broadcasting rules,
    # the following will be evaluated for every combination of k and i.
    x2s2 = (x*s)**2
    return np.sum(r * x2s2 / (1 + x2s2), axis=-1)

# fit using curve_fit
popt,pcov = curve_fit(
    lambda x,*params: f(x,params[:N],params[N:]),
    X,Y,
    np.r_[R0,S0],
)
R = popt[:N]
S = popt[N:]

# fit using leastsq
popt,ier = leastsq(
    lambda params: f(X,params[:N],params[N:]) - Y,
    np.r_[R0,S0],
)
R = popt[:N]
S = popt[N:]

A few things to note:

  • Upon start, we need the 1d arrays X and Y of measurements to fit to, the 1d arrays R0 and S0 as initial guesses and Nthe length of those two arrays.
  • I separated the implementation of the actual model f from the objective functions supplied to the fitters. Those I implemented using lambda functions. Of course, one could also have ordinary def ... functions and combine them into one.
  • The model function f uses numpy's broadcasting to simultaneously sum over a set of parameters (along the last axis), and calculate in parallel for many x (along any axes before the last, though both fit functions would complain if there is more than one... .ravel() to help there)
  • We concatenate the fit parameters R and S into a single parameter vector using numpy's shorthand np.r_[R,S].
  • curve_fit supplies every single parameter as a distinct parameter to the objective function. We want them as a vector, so we use *params: It catches all remaining parameters in a single list.
  • leastsq gives a single params vector. However, it neither supplies x, nor does it compare it to y. Those are directly bound into the objective function.
burnpanck
  • 1,955
  • 1
  • 12
  • 36
  • 1
    Thank you so much for this answer, very helpful (and complicated haha). Trying to make it work with the curve_fit, so far I'm failing with the initial guess because I keep getting "Optimal parameters not found: Number of calls to function has reached maxfev = 20200." I guess it just means I have to keep trying different guesses. –  May 20 '15 at 10:54
  • Are you sure that this is the right function to fit to your data? The problem with that function is that for close $s_i$'s the the corresponding $r_i$'s are essentially degenerate: You can freely redistribute from one r_i to the other without changing the function. So you should be sure that all s_i end up distinct and correspondingly supply distinct start values. – burnpanck May 20 '15 at 11:04
  • Oh, interesting point, I'll try with this in mind now. Thanks –  May 20 '15 at 11:08
  • Again thanks a bunch for this. You may be interested in my other post as well, [here](http://stackoverflow.com/questions/30331984/fitting-data-with-integral-function), it would be interesting to see your method for that one. One last thing if I may, I really found your solution neat here, and I was wondering whether you have any literature or book recommendation in mind for these kinds of computations and methods in python, mainly ones that a physicist deals with regularly. Cheers –  May 26 '15 at 13:09
  • Unfortunately, I never read any books, rather just learning by doing. The numpy user guide has a section on broadcasting at [http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html], which is a very powerful tool. Then, it's really just a matter of reading the documentation whenever you need something and trying to be attentive to features and techniques that you don't need immediately, but are useful in another context. – burnpanck May 26 '15 at 19:15
0

In order to use scipy.optimize.leastsq to estimate multiple parameters, you need to pack them into an array and unpack them inside your function. You can then do anything you want with them. For example, if your s_i are the first 3 and your r_i are the next three parameters in your array p, you would just set ssum=p[:3].sum() and rsum=p[3:6].sum(). But again, your parameters are not identified (according to your comment), so estimation is pointless.

For an example of using leastsq, see the Cookbook's Fitting Data example.

Alan
  • 9,410
  • 15
  • 20
  • I think you misunderstood the OP: $f$ is a function of $x$ to fit to data $y$, i.e. he has many $x_k$'s to fit to $y_k$'s. As long as there are more $k$'s than $2N$, that fit problem is well posed. – burnpanck May 19 '15 at 23:25
  • @burnpanck I'm not understanding you. Consider set of values r1..rN and s1...sN. Now pick distinct i and j, and produce a new set of values by simply switching si with sj and ri with rj. As far as I can see, f(x) does not change, regardless of the value of x, and correspondingly no measure of fit will distinguish between these two sets. – Alan May 20 '15 at 12:42
  • Aha, I see, so it was me misunderstanding you. You are right of course. In this formulation, there are N! equivalent solutions. In that sense, the optimisation problem is only well posed up to permutations of the indices i. For a local numeric solver as `leastsq` that is no problem, as long as in the solution none of the si gets close to another sj. Being a local numeric solver, it will not know about the other equivalent solutions and just give you a random one. – burnpanck May 20 '15 at 13:00