简体   繁体   中英

how to fit a step function in python

I have a question about fitting a step function using scipy routines like curve_fit. I have trouble making it vectorized, for example:

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

xobs=np.linspace(0,10,100)
yl=np.random.rand(50); yr=np.random.rand(50)+100
yobs=np.concatenate((yl,yr),axis=0)

def model(x,rf,T1,T2):
#1:   x=np.vectorize(x)
    if x<rf:
        ret= T1
    else:
        ret= T2
    return ret
#2: model=np.vectorize(model)
popt, pcov = curve_fit(model, xobs, yobs, [40.,0.,100.])

It says

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

If I add #1 or #2 it runs but doesn't really fit the data:

OptimizeWarning: Covariance of the parameters could not be estimated     category=OptimizeWarning)
[ 40.          50.51182064  50.51182064] [[ inf  inf  inf]
[ inf  inf  inf]
[ inf  inf  inf]]

Anybody know how to fix that? THX

Here's what I did. I retained xobs and yobs :

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

xobs=np.linspace(0,10,100)
yl=np.random.rand(50); yr=np.random.rand(50)+100
yobs=np.concatenate((yl,yr),axis=0)

Now, Heaviside function must be generated. To give you an overview of this function, consider the half-maximum convention of Heaviside function:

亥

In Python, this is equivalent to: def f(x): return 0.5 * (np.sign(x) + 1)

A sample plot would be:

xval = sorted(np.concatenate([np.linspace(-5,5,100),[0]])) # includes x = 0
yval = f(xval)
plt.plot(xval,yval,'ko-')
plt.ylim(-0.1,1.1)
plt.xlabel('x',size=18)
plt.ylabel('H(x)',size=20)

Heaviside2

Now, plotting xobs and yobs gives:

plt.plot(xobs,yobs,'ko-')
plt.ylim(-10,110)
plt.xlabel('xobs',size=18)
plt.ylabel('yobs',size=20)

xobs_yobs

Notice that comparing the two figures, the second plot is shifted by 5 units and the maximum increases from 1.0 to 100. I infer that the function for the second plot can be represented as follows:

Hfit

or in Python: (0.5 * (np.sign(x-5) + 1) * 100 = 50 * (np.sign(x-5) + 1)

Combining the plots yields (where Fit represents the above fitting function)

Heaviside_combined

The plot confirms that my guess is correct. Now, assuming that YOU DO NOT KNOW how did this correct fitting function come about, a generalized fitting function is created: def f(x,a,b,c): return a * (np.sign(xb) + c) , where theoretically, a = 50 , b = 5 , and c = 1 .

Proceed to estimation:

popt,pcov=curve_fit(f,xobs,yobs,bounds=([49,4.75,0],[50,5,2])) .

Now, bounds = ([lower bound of each parameter (a,b,c)],[upper bound of each parameter]) . Technically, this means that 49 < a < 50, 4.75 < b < 5, and 0 < c < 2.

Here are MY results for popt and pcov : 结果

pcov represents the estimated covariance of popt. The diagonals provide the variance of the parameter estimate [Source] .

Results show that the parameter estimates pcov are near the theoretical values.

Basically, a generalized Heaviside function can be represented by: a * (np.sign(xb) + c)

Here is the code that will generate parameter estimates and the corresponding covariances:

import numpy as np
from scipy.optimize import curve_fit

xobs = np.linspace(0,10,100)
yl = np.random.rand(50); yr=np.random.rand(50)+100
yobs = np.concatenate((yl,yr),axis=0)

def f(x,a,b,c): return a * (np.sign(x-b) + c) # Heaviside fitting function

popt, pcov = curve_fit(f,xobs,yobs,bounds=([49,4.75,0],[50,5,2]))
print 'popt = %s' % popt
print 'pcov = \n %s' % pcov

Finally, note that the estimates of popt and pcov vary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM