Python exponential decay curve_fit gives me a linear fit - python

Hi I'm attempting to produce a fit for each of my three exponential decays. I am not successful with producing a satisfactory fit. This is what I get: http://i.imgur.com/Nx44wsS.jpg
Any help is greatly appreciated. My code is below.
import pylab as plb
import matplotlib.pyplot as plt
import matplotlib.axes as ax
import scipy as sp
from scipy.optimize import curve_fit
from matplotlib import rc
rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
## for Palatino and other serif fonts use:
#rc('font',**{'family':'serif','serif':['Palatino']})
rc('text', usetex=True)
data = plb.loadtxt('data.csv',skiprows=2)
yp = data[:,4]
yr = data[:,5]
yl = data[:,6]
x = data[:,0]
def func(x,a,b,c):
return a*np.exp(-b*x) + c
popt, pcov = curve_fit(func, x, yl,maxfev=20000)
a = popt[0]
b = popt[1]
c = popt[2]
print a
print b
print c
print func(x,a,b,c)
xf = np.linspace(0,70,100)
yf = a*np.exp(-b*x) + c
plt.clf()
plt.plot(x,yf,'r-', label="Fitted Curve")
plt.plot(x,func(x,*popt))
plt.plot(x,yp,'bo',label='Polished')
plt.plot(x,yr,'ro',label='Rough')
plt.plot(x,yl,'go',label='Lacquered')
plt.legend()
plt.ylabel("Temperature (K)")
plt.xlabel("Time (min)")
plt.show()

Nonlinear fits are difficult and the trick is that you have to provide a reasonable initial guess.
Here is a version of your code which does two fits, one with an approximate initial guess and one with the default initial guess:
import pylab as plb
import matplotlib.pyplot as plt
import matplotlib.axes as ax
import scipy as sp
from scipy.optimize import curve_fit
from matplotlib import rc
import numpy as np
rc('font', **{'family':'sans-serif', 'sans-serif':['Helvetica']})
rc('text', usetex=True)
# Fake data
x = np.arange(0, 70., 2.)
yl = 300 + 63*np.exp(-x/35.)
def func(x, a, b, c):
return a*np.exp(-b*x) + c
popt, pcov = curve_fit(func, x, yl, p0=(40, 0.012, 250), maxfev=20000)
a, b, c = popt
print 'a=', a, 'b=', b, 'c=', c
print 'func=', func(x, a, b, c)
popt2, pcov2 = curve_fit(func, x, yl, p0=None, maxfev=20000)
a2, b2, c2 = popt2
print 'a2=', a2, 'b2=', b2, 'c2=', c2
print 'func=', func(x, a2, b2, c2)
xf = np.linspace(0, 70, 100)
yf = a*np.exp(-b*x) + c
plt.clf()
plt.plot(x, yf, 'r-', label="Fitted Curve")
plt.plot(x, func(x, *popt))
plt.plot(x, func(x, *popt2), 'b-', label='Fit w/o guess')
plt.plot(x, yl, 'go', label='Lacquered')
plt.legend()
plt.ylabel("Temperature (K)")
plt.xlabel("Time (min)")
plt.show()
And here are the resulting fits:
As you can see, the fit with a reasonable initial guess does very well (red line). If you don't provide an initial guess, scipy assumes 1 for all parameters and that works poorly (blue line).

Related

fit multiple parametric curves with scipy python

I am trying to fit two curve into one equation. y = (a * exp(b * (T^-1)))cexp(d100)(x^0.5)
for y1, T =10,
for y2, T =25.
how do a get a,b,c,d
I have a code that simplified to fit one data. I don't know how to do both.
I find a similar question with solution but I can't follow.. fit multiple parametric curves with scipy
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import leastsq
import numpy as np
import pandas as pd
from math import exp
def func(params,x,y):
a, b, c, d = params[0], params[1], params[2],params[3]
return y-(a*exp(b*(10**-1)))*c*exp(d*100)*(x**0.5)
x = [0,33,65,98,135,261,374]
y = [0.000,0.006,0.010,0.018,0.023,0.033,0.035]
y2 = [0.000,0.013,0.032,0.036,0.042,0.046,0.051]
plt.scatter(x,y, label='y1')
plt.scatter(x,y2, label='y1')
params=[0, 0, 0, 0]
result = leastsq(func, params, (x, y))
a, b, c, d = result[0][0], result[0][1], result[0][2], result[0][3]
yfit1 = (a*exp(b*(25**-1)))*c*exp(d*100)*(x**0.5)
plt.plot(x, yfit1, color="red")
print (b,c,d)
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid()
plt.show()
You need to include T in the independent variable being passed to curve_fit. Also, you should use numpy mathematical functions in func.
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
def func(xT, a, b, c, d,):
x = xT[0,:]
T = xT[1,:]
return a * np.exp(b * np.power(T, -1)) * c * np.exp(d * 100.0) * np.power(x, 0.5)
x0 = np.array([0,33,65,98,135,261,374])
y1 = np.array([0.000,0.006,0.010,0.018,0.023,0.033,0.035])
T1 = 10.0 * np.ones(len(x0))
y2 = np.array([0.000,0.013,0.032,0.036,0.042,0.046,0.051])
T2 = 25.0 * np.ones(len(x0))
x = np.concatenate((x0, x0))
y = np.concatenate((y1, y2))
T = np.concatenate((T1, T2))
popt, _ = curve_fit(func, np.vstack((x, T)), y)
N = 101 # number of points for parametric curves
x_ = np.linspace(np.min(x0), np.max(x0), N)
y1_ = func(np.vstack((x_, 10.0 * np.ones(N))), *popt)
plt.plot(x0, y1, 'k.')
plt.plot(x_, y1_, 'k-')
y2_ = func(np.vstack((x_, 25.0 * np.ones(N))), *popt)
plt.plot(x0, y2, 'b.')
plt.plot(x_, y2_, 'b-')

How to fit exponential function with the python?

I am trying to fit an exponential function of the form y = e^(-b/x)in python. The data I'm using looks like this
But when I try to plot I'm getting this error
"RuntimeWarning: overflow encountered in exp
if name == 'main':"
Here is my code
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as mpl
""" Fitting Function"""
def func(x, a, b, c):
return a *np.exp(-1*b/x)
data = np.loadtxt("S005_CP_0011_N20.dat", skiprows=1)
xData, yData = np.hsplit(data,2)
x = xData[:,0]
y = yData[:,0]
popt, pcov = curve_fit(func, x, y)
mpl.plot(x, func(x, *popt), label="Fitted Curve")
mpl.legend(loc='upper left')
mpl.show()
Could someone please help to rectify the code?

How do i get a smooth fit for my data points, using "scipy.optimize.curve_fit"?

I want to fit some data points using scipy.optimize.curve_fit. Unfortunately I get an unsteady fit and I do not know why.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
M = np.array([730,910,1066,1088,1150], dtype=float)
V = np.array([95.71581923, 146.18564513, 164.46723727, 288.49796413, 370.98703941], dtype=float)
def func(x, a, b, c):
return a * np.exp(b * x) + c
popt, pcov = curve_fit(func, M, V, [0,0,1], maxfev=100000000)
print(*popt)
fig, ax = plt.subplots()
fig.dpi = 80
ax.plot(M, V, 'go', label='data')
ax.plot(M, func(M, *popt), '-', label='fit')
plt.xlabel("M")
plt.ylabel("V")
plt.grid()
plt.legend()
plt.show()
I would acutally expect some kind of a smooth curve. Can someone explain what I am doing wrong here?
You are only plotting the same x points as the original data in your call:
ax.plot(M, V, 'go', label='data')
ax.plot(M, func(M, *popt), '-', label='fit')
To fix this, you can use a wider range - here we use all the values from 700 to 1200:
toplot = np.arange(700,1200)
ax.plot(toplot, func(toplot, *popt), '-', label='fit')

scipy.curve_fit() returns multiple lines

I am new to python and was trying to fit dataset distribution using the following code. The actual data is a list that contains two columns- predicted market price and actual market price. And I was trying to use scipy.curve_fit() but it gave me many lines plotted at the same place. Any help is appreciated.
# import the necessary modules and define a func.
from scipy.optimize import curve_fit
from matplotlib import pyplot as plt
def func(x, a, b, c):
return a * x** b + c
# my data
pred_data = [3.0,1.0,1.0,7.0,6.0,1.0,7.0,4.0,9.0,3.0,5.0,5.0,2.0,6.0,8.0]
actu_data =[ 3.84,1.55,1.15,7.56,6.64,1.09,7.12,4.17,9.45,3.12,5.37,5.65,1.92,6.27,7.63]
popt, pcov = curve_fit(func, pred_data, actu_data)
#adjusting y
yaj = func(pred_data, popt[0],popt[1], popt[2])
# plot the data
plt.plot(pred_data,actu_data, 'ro', label = 'Data')
plt.plot(pred_data,yaj,'b--', label = 'Best fit')
plt.legend()
plt.show()
Scipy doesn't produce multiple lines, the strange output is caused by the way you present your unsorted data to matplotlib. Sort your x-values and you get the desired output:
from scipy.optimize import curve_fit
from matplotlib import pyplot as plt
def func(x, a, b, c):
return a * x** b + c
# my data
pred_data = [3.0,1.0,1.0,7.0,6.0,1.0,7.0,4.0,9.0,3.0,5.0,5.0,2.0,6.0,8.0]
actu_data =[ 3.84,1.55,1.15,7.56,6.64,1.09,7.12,4.17,9.45,3.12,5.37,5.65,1.92,6.27,7.63]
popt, pcov = curve_fit(func, pred_data, actu_data)
#adjusting y
yaj = func(sorted(pred_data), *popt)
# plot the data
plt.plot(pred_data,actu_data, 'ro', label = 'Data')
plt.plot(sorted(pred_data),yaj,'b--', label = 'Best fit')
plt.legend()
plt.show()
A better way is of course to define an evenly-spaced high resolution array for your x-values and calculate the fit for this array to have a smoother representation of your fit function:
from scipy.optimize import curve_fit
import numpy as np
from matplotlib import pyplot as plt
def func(x, a, b, c):
return a * x** b + c
# my data
pred_data = [3.0,1.0,1.0,7.0,6.0,1.0,7.0,4.0,9.0,3.0,5.0,5.0,2.0,6.0,8.0]
actu_data =[ 3.84,1.55,1.15,7.56,6.64,1.09,7.12,4.17,9.45,3.12,5.37,5.65,1.92,6.27,7.63]
popt, pcov = curve_fit(func, pred_data, actu_data)
xaj = np.linspace(min(pred_data), max(pred_data), 1000)
yaj = func(xaj, *popt)
# plot the data
plt.plot(pred_data,actu_data, 'ro', label = 'Data')
plt.plot(xaj, yaj,'b--', label = 'Best fit')
plt.legend()
plt.show()

Exponential curve fit will not fit

When attempting to plot an exponential curve to a set of data:
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import style
from matplotlib import pylab
import numpy as np
from scipy.optimize import curve_fit
x = np.array([30,40,50,60])
y = np.array([0.027679854,0.055639098,0.114814815,0.240740741])
def exponenial_func(x, a, b, c):
return a*np.exp(-b*x)+c
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
xx = np.linspace(10,60,1000)
yy = exponenial_func(xx, *popt)
plt.plot(x,y,'o', xx, yy)
pylab.title('Exponential Fit')
ax = plt.gca()
fig = plt.gcf()
plt.xlabel(r'Temperature, C')
plt.ylabel(r'1/Time, $s^-$$^1$')
plt.show()
Graph for the above code:
However when I add the data point 20 (x) and 0.015162344 (y):
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import style
from matplotlib import pylab
import numpy as np
from scipy.optimize import curve_fit
x = np.array([20,30,40,50,60])
y = np.array([0.015162344,0.027679854,0.055639098,0.114814815,0.240740741])
def exponenial_func(x, a, b, c):
return a*np.exp(-b*x)+c
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
xx = np.linspace(20,60,1000)
yy = exponenial_func(xx, *popt)
plt.plot(x,y,'o', xx, yy)
pylab.title('Exponential Fit')
ax = plt.gca()
fig = plt.gcf()
plt.xlabel(r'Temperature, C')
plt.ylabel(r'1/Time, $s^-$$^1$')
plt.show()
The above code generates the error
'RuntimeError: Optimal parameters not found: Number of calls to
function has reached maxfev = 800.'
If maxfev is set to maxfev = 1300
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1),maxfev=1300)
The graph is plotted but does not fit the curve correctly. Graph from above code change, maxfev = 1300:
I think this is because points 20 and 30 a too close to each other? For comparison, excel plots the data like this:
How can I plot this curve correctly?
From your data it is obvious that you need a positive exponent, therefore, b needs to be negative as you use a*np.exp(-b*x) + c as the underlying model. However, you start with a positive initial value for b which most likely causes the issues.
If you change
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
to
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, -1e-6, 1))
it works fine and gives the expected outcome.
Alternatively, you could also change your equation to
return a*np.exp(b*x) + c
and start with the same initial values as you had.
Here is the entire code:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
def exponenial_func(x, a, b, c):
return a*np.exp(b*x)+c
x = np.array([20, 30, 40, 50, 60])
y = np.array([0.015162344, 0.027679854, 0.055639098, 0.114814815, 0.240740741])
popt, pcov = curve_fit(exponenial_func, x, y, p0=(1, 1e-6, 1))
xx = np.linspace(20, 60, 1000)
yy = exponenial_func(xx, *popt)
# please check whether that is correct
r2 = 1. - sum((exponenial_func(x, *popt) - y) ** 2) / sum((y - np.mean(y)) ** 2)
plt.plot(x, y, 'o', xx, yy)
plt.title('Exponential Fit')
plt.xlabel(r'Temperature, C')
plt.ylabel(r'1/Time, $s^-$$^1$')
plt.text(30, 0.15, "equation:\n{:.4f} exp({:.4f} x) + {:.4f}".format(*popt))
plt.text(30, 0.1, "R^2:\n {}".format(r2))
plt.show()

Categories

Resources