0

I am trying to use curve_fitting for a defined function of the form below:

Z = (Rth(1 - np.exp(- x/tau))

I want to calculate 1st four values of parameters Rth and tau. At the moment, it works fine If i use the whole function like this:

 Z = (a * (1- np.exp (- x / b))) + (c * (1- np.exp (- x / d)))+ (e * (1- np.exp (- x / f)))  + (g * (1- np.exp (- x / f)))

But this is certainly not the nice way to do it for example if i have a really long function with more than 4 exponential terms and I want to get all the parameters. How can I adjust it so that it returns specific number of values of Rth and tau after curve fitting?

For example, If I want to get 16 parameters from a 8 term exponential function, I don't have to write full 8 terms but just a general form and it gives the desired output.

Thank you.

UQ07
  • 39
  • 8
  • Hi, what do you mean by after. Do you want to make it arbitrary number? Second point: Your two functions are not of same type. I expect a typo in the first. Third point:dont fit `exp( -x / b )` but `exp( -b * x)` as b might become zero during the fitting. – mikuszefski Oct 13 '21 at 12:06
  • @mikuszefski I am actually trying to retrieve Rth and tau from a 4 term exponential function as in the second code line (I am using a,b,c.. instead). Scipy curve fit gives me 8 optimized parameters values which is what I need. But my question is to make a general function like I have in 1st code line and then I specify the number of terms by 4 and it gives the same 8 parameters but in a more concise way. Regarding the third point: I have to fit it like that to relate to previous data. – UQ07 Oct 13 '21 at 13:00
  • It was a typo, so i edited the first line. Sorry – UQ07 Oct 13 '21 at 13:00
  • If you're abele to provide starting values it can be generalized using `least_squares()` instead of `curve_fit()` as shown [here](https://stackoverflow.com/a/61725255/803359) – mikuszefski Oct 13 '21 at 13:25
  • Where is the problem in fitting `exp( - b x )` and checking `1 / b` of the result? – mikuszefski Oct 13 '21 at 13:26
  • Good point, I will use this inverse. thank you – UQ07 Oct 13 '21 at 13:31
  • @mikuszefski unfortunately I don't have starting values, secondly I couldn't comprehend the answer on the provided link. Can I do it using curve_fit() by any chance? – UQ07 Oct 13 '21 at 13:39
  • Well, `curve_fit()` needs a fixed structure, as it checks the number of parameters to fit from the call pattern. Moreover, it makes a guess for the initial parameters, namely 1. For `least_sq` you would do this manually. – mikuszefski Oct 14 '21 at 09:22
  • Thank you, I managed to solve it using the curve_fit – UQ07 Oct 14 '21 at 11:48
  • why not post your solution as answer? – mikuszefski Oct 14 '21 at 12:29

2 Answers2

0

Using least_squares it is quite simple to get an arbitrary sum of functions.

import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import least_squares

def partition( inList, n ):
    return zip( *[ iter( inList ) ] * n )

def f( x, a, b ):
    return a * ( 1 - np.exp( -b * x ) )

def multi_f( x, params ):
    if len( params) % 2:
        raise TypeError
    subparams = partition( params, 2 )
    out = np.zeros( len(x) )
    for p in subparams:
        out += f( x, *p )
    return out

def residuals( params, xdata, ydata ):
    return multi_f( xdata, params ) - ydata


xl = np.linspace( 0, 8, 150 )
yl = multi_f( xl, ( .21, 5, 0.5, 0.1,2.7, .01 ) )

res = least_squares( residuals, x0=( 1,.9, 1, 1, 1, 1.1 ), args=( xl, yl ) )

print( res.x )
yth = multi_f( xl, res.x )
fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1 )

ax.plot( xl, yl )
ax.plot( xl, yth )

plt.show( )
mikuszefski
  • 3,943
  • 1
  • 25
  • 38
0

I managed to solve it by the following way, maybe not the smart way but it works for me.

def func(x,*args):
Z=0
for i in range(0,round(len(args)/2)):
    Z += (args[i*2] * (1- np.exp (- x / args[2*i+1])))
return Z

Then calling the parameters in a separate function, I can adjust the number of parameters.

def func2(x,a,b,c,d,e,f,g,h):
return func(x,a,b,c,d,e,f,g,h)
popt , pcov = curve_fit(func2,x,y, method = 'trf', maxfev = 100000)

and it works fine for me.

UQ07
  • 39
  • 8