Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Curve fit with parameter bounds

I have experimental data :

xdata = [85,86,87,88,89,90,91,91.75,93,96,100,101,102,103,104,105,106,107.25,108.25,109,109.75,111,112,112.75,114,115.25,116,116.75,118,119.25,120,121,122,122.5,123.5,125.25,126,126.75,127.75,129.25,130.25,131,131.75,133,134.25,135,136,137,138,139,140,141,142,143,144,144.75,146,146.75,148,149.25,150,150.5,152,153.25,154,155,156.75,158,159,159.75,161,162,162.5,164,165,166]

ydata = [0.2,0.21,0.18,0.21,0.19,0.2,0.21,0.2,0.18,0.204,0.208,0.2,0.21,0.25,0.2,0.19,0.216,0.22,0.224,0.26,0.229,0.237,0.22,0.246,0.25,0.264,0.29,0.274,0.29,0.3,0.27,0.32,0.38,0.348,0.372,0.398,0.35,0.42,0.444,0.48,0.496,0.55,0.51,0.54,0.57,0.51,0.605,0.57,0.65,0.642,0.6,0.66,0.7,0.688,0.69,0.705,0.67,0.717,0.69,0.728,0.75,0.736,0.73,0.744,0.72,0.76,0.752,0.74,0.76,0.7546,0.77,0.74,0.758,0.74,0.78,0.76]

And the formula f(x) = m1 + m2 / (1 + e ^ (-m3*(x - m4))). I need to find m1, m2, m3, m4 with least square method, where 0.05 < m1 < 0.3 0.3 < m2 < 0.8 0.05 < m3 < 0.5 100 < m4 < 200.

I use curve_fit and my function is :

def f(xdata, m1, m2, m3, m4):
    if m1 > 0.05 and m1 < 0.3 and \
       m2 > 0.3 and m2 < 0.8 and \
       m3 > 0.05 and m3 < 0.5 and \
       m4 > 100 and m4 < 200:
          return m1 + (m2 * 1. / (1 + e ** (-m3 * (x - m4))))
    return (abs(m1) + abs(m2) + abs(m3) + abs(m4)) * 1e14 # some large number

But the program return the error: RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 1000.

What to do?

import numpy as np
from scipy.optimize import curve_fit
from math import e

xdata = np.array([85,86,87,88,89,90,91,91.75,93,96,100,101,102,103,104,105,106,107.25,108.25,109,109.75,111,112,112.75,114,115.25,116,116.75,118,119.25,120,121,122,122.5,123.5,125.25,126,126.75,127.75,129.25,130.25,131,131.75,133,134.25,135,136,137,138,139,140,141,142,143,144,144.75,146,146.75,148,149.25,150,150.5,152,153.25,154,155,156.75,158,159,159.75,161,162,162.5,164,165,166])`
ydata = np.array([0.2,0.21,0.18,0.21,0.19,0.2,0.21,0.2,0.18,0.204,0.208,0.2,0.21,0.25,0.2,0.19,0.216,0.22,0.224,0.26,0.229,0.237,0.22,0.246,0.25,0.264,0.29,0.274,0.29,0.3,0.27,0.32,0.38,0.348,0.372,0.398,0.35,0.42,0.444,0.48,0.496,0.55,0.51,0.54,0.57,0.51,0.605,0.57,0.65,0.642,0.6,0.66,0.7,0.688,0.69,0.705,0.67,0.717,0.69,0.728,0.75,0.736,0.73,0.744,0.72,0.76,0.752,0.74,0.76,0.7546,0.77,0.74,0.758,0.74,0.78,0.76])

def f(xdata, m1, m2, m3, m4):
    if m1 > 0.05 and m1 < 0.3 and \
       m2 > 0.3 and m2 < 0.8 and \
       m3 > 0.05 and m3 < 0.5 and \
       m4 > 100 and m4 < 200:
        return m1 + (m2 * 1. / (1 + e ** (-m3 * (x - m4))))
    return (abs(m1) + abs(m2) + abs(m3) + abs(m4)) * 1e14

print curve_fit(f, xdata, ydata)
like image 585
KhanSuleyman Avatar asked Nov 11 '15 12:11

KhanSuleyman


People also ask

What is POPT and PCOV?

What does popt and pcov mean? popt- An array of optimal values for the parameters which minimizes the sum of squares of residuals. pcov-2d array which contains the estimated covariance of popt. The diagonals provide the variance of the parameter estimate.

What is PCOV?

The returned parameter covariance matrix pcov is based on scaling sigma by a constant factor. This constant is set by demanding that the reduced chisq for the optimal parameters popt when using the scaled sigma equals unity. In other words, sigma is scaled to match the sample variance of the residuals after the fit.

What is Curvefit return?

The function curve_fit() returns the optimal values for the mapping function, e.g, the coefficient values. It also returns a covariance matrix for the estimated parameters, but we can ignore that for now.

How do you fit a curve to data in Python?

Use the function curve_fit to fit your data. Extract the fit parameters from the output of curve_fit . Use your function to calculate y values using your fit model to see how well your model fits the data. Graph your original data and the fit equation.


1 Answers

Set the initial parameters to useful values:

curve_fit(f, xdata, ydata, p0=(0.1, 0.5, 0.1, 150)))

Also, use xdata instead of x in your function f:

return m1 + (m2 * 1. / (1 + e ** (-m3 * (xdata - m4))))

This is my modified program:

def f(xdata, m1, m2, m3, m4):
    if (0.05 < m1 < 0.3 and
        0.3 < m2 < 0.8 and
        0.05 < m3 < 0.5 and
        100 < m4 < 200):
        return m1 + (m2 * 1. / (1 + e ** (-m3 * (xdata - m4))))
    return 1e38

print(curve_fit(f, xdata, ydata, p0=(0.1, 0.5, 0.1, 150)))

Result:

(array([   0.19567035,    0.56792559,    0.13434829,  129.98915877]), 
 array([[  2.94622909e-05,  -3.96126279e-05,   1.99236054e-05,
          7.48438125e-04],
       [ -3.96126279e-05,   9.24145662e-05,  -4.62302643e-05,
          5.04671621e-04],
       [  1.99236054e-05,  -4.62302643e-05,   3.77364832e-05,
         -2.43866126e-04],
       [  7.48438125e-04,   5.04671621e-04,  -2.43866126e-04,
          1.34700612e-01]]))
like image 91
Mike Müller Avatar answered Sep 29 '22 12:09

Mike Müller