Discussion:
[SciPy-User] [Matplotlib-users] Sigmoid Curve Fitting
Gökhan Sever
2010-09-20 22:10:49 UTC
Permalink
Hi,
Does Matplotlib/Numpy/Scipy contain the ability to fit a sigmoid curve
to a set of data points?
Regards,
Chris
I am very curious to know how this is done. I have seen in many Cloud
Condensation Nuclei (CCN) counter calibration experiments [e.g. Rose et. al.
(2008) - p1156, fig2
http://www.atmos-chem-phys.net/8/1153/2008/acp-8-1153-2008.html] people use
sigmoidal fits. Never seen a code is shared so far or how it is obtained.

from scipy.optimize import leastsq
could be a good point to start. To me this fit is very likely a least square
minimization problem. You have your tanh(x) or 1/(1+e^-x) as a sigmoidal
function, and you should find the points that will make the error minimal
for a given discrete points/measurements.

from scipy.optimize import curve_fit might be another way to approach.
--
Gökhan
Gökhan Sever
2010-09-20 22:35:00 UTC
Permalink
Hi,
Does Matplotlib/Numpy/Scipy contain the ability to fit a sigmoid curve
to a set of data points?
You could also experiment at zunzun.com. Choose 2D sigmoidal from the 2D
function scroll list.
--
Gökhan
Chris Spencer
2010-09-21 01:38:32 UTC
Permalink
Hi,

Does Scipy contain the ability to fit a sigmoid curve to a set of data points?

I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.

Regards,
Chris
Warren Weckesser
2010-09-21 03:54:44 UTC
Permalink
Post by Chris Spencer
Hi,
Does Scipy contain the ability to fit a sigmoid curve to a set of data points?
I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.
You can use curve_fit (scipy.optimize.curve_fit).

Which family of sigmoid functions do you want to use?
See http://en.wikipedia.org/wiki/Sigmoid_function for a few possibilities.

If, for example, you want to fit the following family to your data:

f(x) = 1/(1 + exp(-k*(x-x0)))

(which has two parameters, k and x0), you can do something like this:

-----
import numpy as np
import pylab
from scipy.optimize import curve_fit

def sigmoid(x, x0, k):
y = 1 / (1 + np.exp(-k*(x-x0)))
return y

xdata = np.array([0.0, 1.0, 3.0, 4.3, 7.0, 8.0, 8.5, 10.0, 12.0])
ydata = np.array([0.01, 0.02, 0.04, 0.11, 0.43, 0.7, 0.89, 0.95, 0.99])

popt, pcov = curve_fit(sigmoid, xdata, ydata)
print popt

x = np.linspace(-1, 15, 50)
y = sigmoid(x, *popt)


pylab.plot(xdata, ydata, 'o', label='data')
pylab.plot(x,y, label='fit')
pylab.ylim(0, 1.05)
pylab.legend(loc='best')
pylab.show()
-----

This script generates the attached plot.


Warren
Chris Spencer
2010-09-21 13:13:07 UTC
Permalink
Thank you. Since that feature only appears to be in the bleeding-edge
version, it took me a while to track-down and compile all the
libraries on Ubuntu 10.04, but it works perfectly.

Regards,
Chris

On Mon, Sep 20, 2010 at 11:54 PM, Warren Weckesser
Post by Warren Weckesser
Post by Chris Spencer
Hi,
Does Scipy contain the ability to fit a sigmoid curve to a set of data points?
I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.
You can use curve_fit (scipy.optimize.curve_fit).
Which family of sigmoid functions do you want to use?
See http://en.wikipedia.org/wiki/Sigmoid_function for a few possibilities.
f(x) = 1/(1 + exp(-k*(x-x0)))
-----
import numpy as np
import pylab
from scipy.optimize import curve_fit
   y = 1 / (1 + np.exp(-k*(x-x0)))
   return y
xdata = np.array([0.0,   1.0,  3.0, 4.3, 7.0,   8.0,   8.5, 10.0, 12.0])
ydata = np.array([0.01, 0.02, 0.04, 0.11, 0.43,  0.7, 0.89, 0.95, 0.99])
popt, pcov = curve_fit(sigmoid, xdata, ydata)
print popt
x = np.linspace(-1, 15, 50)
y = sigmoid(x, *popt)
pylab.plot(xdata, ydata, 'o', label='data')
pylab.plot(x,y, label='fit')
pylab.ylim(0, 1.05)
pylab.legend(loc='best')
pylab.show()
-----
This script generates the attached plot.
Warren
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
Chris Spencer
2010-09-21 15:24:09 UTC
Permalink
Is it possible to get it to determine the asymptotes as well? It seems
to assume the curve will be bounded between y=0 and 1, whereas my data
can have arbitrary limits. I tried changing sigmoid() to:

def sigmoid(x, x0, k, a):
y = a * 1 / (1 + np.exp(-k*(x-x0)))
return y

but that only results in a curve of f(x)=0.5.

Regards,
Chris

On Mon, Sep 20, 2010 at 11:54 PM, Warren Weckesser
Post by Warren Weckesser
Post by Chris Spencer
Hi,
Does Scipy contain the ability to fit a sigmoid curve to a set of data points?
I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.
You can use curve_fit (scipy.optimize.curve_fit).
Which family of sigmoid functions do you want to use?
See http://en.wikipedia.org/wiki/Sigmoid_function for a few possibilities.
f(x) = 1/(1 + exp(-k*(x-x0)))
-----
import numpy as np
import pylab
from scipy.optimize import curve_fit
   y = 1 / (1 + np.exp(-k*(x-x0)))
   return y
xdata = np.array([0.0,   1.0,  3.0, 4.3, 7.0,   8.0,   8.5, 10.0, 12.0])
ydata = np.array([0.01, 0.02, 0.04, 0.11, 0.43,  0.7, 0.89, 0.95, 0.99])
popt, pcov = curve_fit(sigmoid, xdata, ydata)
print popt
x = np.linspace(-1, 15, 50)
y = sigmoid(x, *popt)
pylab.plot(xdata, ydata, 'o', label='data')
pylab.plot(x,y, label='fit')
pylab.ylim(0, 1.05)
pylab.legend(loc='best')
pylab.show()
-----
This script generates the attached plot.
Warren
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
Warren Weckesser
2010-09-21 16:16:40 UTC
Permalink
Post by Chris Spencer
Is it possible to get it to determine the asymptotes as well? It seems
to assume the curve will be bounded between y=0 and 1, whereas my data
y = a * 1 / (1 + np.exp(-k*(x-x0)))
return y
but that only results in a curve of f(x)=0.5.
The following is a variation that includes more parameters in the family
of sigmoid functions. But bear in mind, I chose this family of
functions just as a demonstration of curve_fit. I don't know if it
makes sense to use this family for your data. The appropriate family to
use depends on the nature of the data.

-----
import numpy as np
import pylab
from scipy.optimize import curve_fit

def sigmoid(x, x0, k, a, c):
y = a / (1 + np.exp(-k*(x-x0))) + c
return y

xdata = np.array([0.0, 1.0, 3.0, 4.3, 7.0, 8.0, 8.5, 10.0,
12.0, 14.0])
ydata = np.array([0.11, 0.12, 0.14, 0.21, 0.83, 1.45, 1.78, 1.9,
1.98, 2.02])

popt, pcov = curve_fit(sigmoid, xdata, ydata)
print "Fit:"
print "x0 =", popt[0]
print "k =", popt[1]
print "a =", popt[2]
print "c =", popt[3]
print "Asymptotes are", popt[3], "and", popt[3] + popt[2]

x = np.linspace(-1, 15, 50)
y = sigmoid(x, *popt)


pylab.plot(xdata, ydata, 'o', label='data')
pylab.plot(x,y, label='fit')
pylab.ylim(0, 2.05)
pylab.legend(loc='upper left')
pylab.grid(True)
pylab.show()
-----


Warren
Post by Chris Spencer
Regards,
Chris
On Mon, Sep 20, 2010 at 11:54 PM, Warren Weckesser
Post by Warren Weckesser
Post by Chris Spencer
Hi,
Does Scipy contain the ability to fit a sigmoid curve to a set of data points?
I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.
You can use curve_fit (scipy.optimize.curve_fit).
Which family of sigmoid functions do you want to use?
See http://en.wikipedia.org/wiki/Sigmoid_function for a few possibilities.
f(x) = 1/(1 + exp(-k*(x-x0)))
-----
import numpy as np
import pylab
from scipy.optimize import curve_fit
y = 1 / (1 + np.exp(-k*(x-x0)))
return y
xdata = np.array([0.0, 1.0, 3.0, 4.3, 7.0, 8.0, 8.5, 10.0, 12.0])
ydata = np.array([0.01, 0.02, 0.04, 0.11, 0.43, 0.7, 0.89, 0.95, 0.99])
popt, pcov = curve_fit(sigmoid, xdata, ydata)
print popt
x = np.linspace(-1, 15, 50)
y = sigmoid(x, *popt)
pylab.plot(xdata, ydata, 'o', label='data')
pylab.plot(x,y, label='fit')
pylab.ylim(0, 1.05)
pylab.legend(loc='best')
pylab.show()
-----
This script generates the attached plot.
Warren
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
Chris Spencer
2010-09-21 17:02:47 UTC
Permalink
On Tue, Sep 21, 2010 at 12:16 PM, Warren Weckesser
Post by Warren Weckesser
The following is a variation that includes more parameters in the family
of sigmoid functions.  But bear in mind, I chose this family of
functions just as a demonstration of curve_fit.  I don't know if it
makes sense to use this family for your data.  The appropriate family to
use depends on the nature of the data.
I see what you mean. That modification only fits a low-to-high
sigmoid, but that's close enough for me to adapt by reversing my data
set. Thank you for the excellent example.

Regards,
Chris
Chris Spencer
2010-09-21 18:04:45 UTC
Permalink
I found this modification allows for the inversion of the estimated
sigmoid curve:

def sigmoid(x, x0, k, a, c, d):
y = 1 / (1 + np.exp(-k*(x-x0)))
y = (1 - y)*(1 - d) + y*d
y = a * y + c
return y

Regards,
Chris
Post by Chris Spencer
On Tue, Sep 21, 2010 at 12:16 PM, Warren Weckesser
Post by Warren Weckesser
The following is a variation that includes more parameters in the family
of sigmoid functions.  But bear in mind, I chose this family of
functions just as a demonstration of curve_fit.  I don't know if it
makes sense to use this family for your data.  The appropriate family to
use depends on the nature of the data.
I see what you mean. That modification only fits a low-to-high
sigmoid, but that's close enough for me to adapt by reversing my data
set. Thank you for the excellent example.
Regards,
Chris
Warren Weckesser
2010-09-21 18:21:17 UTC
Permalink
Post by Chris Spencer
I found this modification allows for the inversion of the estimated
y = 1 / (1 + np.exp(-k*(x-x0)))
y = (1 - y)*(1 - d) + y*d
y = a * y + c
return y
A negative value of k "reverses" the sigmoid shape, so you shouldn't
have to define a new function. If you prefer to have k be positive, you
could use

def sigmoid(x, x0, k, a, c):
y = a / (1 + np.exp(k*(x-x0))) + c
return y

(I changed "-k" to "k".)


Warren
Post by Chris Spencer
Regards,
Chris
Post by Chris Spencer
On Tue, Sep 21, 2010 at 12:16 PM, Warren Weckesser
Post by Warren Weckesser
The following is a variation that includes more parameters in the family
of sigmoid functions. But bear in mind, I chose this family of
functions just as a demonstration of curve_fit. I don't know if it
makes sense to use this family for your data. The appropriate family to
use depends on the nature of the data.
I see what you mean. That modification only fits a low-to-high
sigmoid, but that's close enough for me to adapt by reversing my data
set. Thank you for the excellent example.
Regards,
Chris
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
Chris Spencer
2010-09-21 18:30:52 UTC
Permalink
Right, I noticed that negating k should theoretically have the same
effect. However, when I reversed your sample data (i.e. ydata =
ydata[::-1]), I was surprised to find that curve_fit gives me
f(x)=0.92 instead of a proper sigmoid curve. Adding my parameter seems
to work around this problem.

Regards,
Chris

On Tue, Sep 21, 2010 at 2:21 PM, Warren Weckesser
Post by Warren Weckesser
Post by Chris Spencer
I found this modification allows for the inversion of the estimated
     y = 1 / (1 + np.exp(-k*(x-x0)))
     y = (1 - y)*(1 - d) + y*d
     y = a * y + c
     return y
A negative value of k "reverses" the sigmoid shape, so you shouldn't
have to define a new function.  If you prefer to have k be positive, you
could use
    y = a / (1 + np.exp(k*(x-x0))) + c
    return y
(I changed "-k" to "k".)
Warren
Post by Chris Spencer
Regards,
Chris
Post by Chris Spencer
On Tue, Sep 21, 2010 at 12:16 PM, Warren Weckesser
Post by Warren Weckesser
The following is a variation that includes more parameters in the family
of sigmoid functions.  But bear in mind, I chose this family of
functions just as a demonstration of curve_fit.  I don't know if it
makes sense to use this family for your data.  The appropriate family to
use depends on the nature of the data.
I see what you mean. That modification only fits a low-to-high
sigmoid, but that's close enough for me to adapt by reversing my data
set. Thank you for the excellent example.
Regards,
Chris
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
Warren Weckesser
2010-09-21 18:36:57 UTC
Permalink
Post by Chris Spencer
Right, I noticed that negating k should theoretically have the same
effect. However, when I reversed your sample data (i.e. ydata =
ydata[::-1]), I was surprised to find that curve_fit gives me
f(x)=0.92 instead of a proper sigmoid curve.
Ah, in that case, it can help to give curve_fit a better initial guess
for the parameters. For example, if I use

popt, pcov = curve_fit(sigmoid, xdata, ydata, p0=(1.0, -1.0, 1.0, 0.0))

with the original function, it works as expected.


Warren
Post by Chris Spencer
Adding my parameter seems
to work around this problem.
Regards,
Chris
On Tue, Sep 21, 2010 at 2:21 PM, Warren Weckesser
Post by Warren Weckesser
Post by Chris Spencer
I found this modification allows for the inversion of the estimated
y = 1 / (1 + np.exp(-k*(x-x0)))
y = (1 - y)*(1 - d) + y*d
y = a * y + c
return y
A negative value of k "reverses" the sigmoid shape, so you shouldn't
have to define a new function. If you prefer to have k be positive, you
could use
y = a / (1 + np.exp(k*(x-x0))) + c
return y
(I changed "-k" to "k".)
Warren
Post by Chris Spencer
Regards,
Chris
Post by Chris Spencer
On Tue, Sep 21, 2010 at 12:16 PM, Warren Weckesser
Post by Warren Weckesser
The following is a variation that includes more parameters in the family
of sigmoid functions. But bear in mind, I chose this family of
functions just as a demonstration of curve_fit. I don't know if it
makes sense to use this family for your data. The appropriate family to
use depends on the nature of the data.
I see what you mean. That modification only fits a low-to-high
sigmoid, but that's close enough for me to adapt by reversing my data
set. Thank you for the excellent example.
Regards,
Chris
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
_______________________________________________
SciPy-User mailing list
http://mail.scipy.org/mailman/listinfo/scipy-user
Sturla Molden
2010-09-21 19:29:28 UTC
Permalink
Post by Chris Spencer
Hi,
Does Scipy contain the ability to fit a sigmoid curve to a set of data points?
I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.
scipy.optimize.leastsq can fit any non-linear regression curve using
Levenberg-Marquardt. You will have to supply a function that computes the
residuals, and optionally a function that returns their Jacobian unless
you want it estimated.

Sturla
Gökhan Sever
2010-09-21 20:31:29 UTC
Permalink
Post by Sturla Molden
scipy.optimize.leastsq can fit any non-linear regression curve using
Levenberg-Marquardt. You will have to supply a function that computes the
residuals, and optionally a function that returns their Jacobian unless
you want it estimated.
Bruce Southey
2010-09-22 01:18:00 UTC
Permalink
Post by Sturla Molden
Post by Chris Spencer
Hi,
Does Scipy contain the ability to fit a sigmoid curve to a set of data points?
I found some Numpy code
(http://pingswept.org/2009/01/24/least-squares-polynomial-fitting-in-python/)
for fitting curves using the least squares method, but it only seems
to fit parabolas to my sigmoid data.
scipy.optimize.leastsq can fit any non-linear regression curve using
Levenberg-Marquardt. You will have to supply a function that computes the
residuals, and optionally a function that returns their Jacobian unless
you want it estimated.
Sturla
Ignoring that you data is the opposite, there are a lot of 'growth
curves' such as Richard's (growth) curve or generalized logistic
function.
http://en.wikipedia.org/wiki/Generalised_logistic_function

Also, you can fit a linear model if your model can be linearized. The
downside is that the errors are multiplicative rather than additive.
For nonlinear models, it usually helps to standardize your data.

Bruce

Loading...