Fitting to a piecewise function using Python - python

I'm trying to fit a piecewise function with absolute values using Numpy.
The mathematical function is
x < p[1]: y = 1 + p[0] * abs((size + x - p[1]) / size - size / 2)
x >= p[1]: y = 1 + p[0] * abs((x - p[1]) / size - size / 2)
Here's my Python function:
fitfunc = lambda p, x: \
x < p[1] and\
1 + p[0] * abs((data['n1'].size + x - p[1]) / data['n1'].size - data['n1'].size / 2) or\
1 + p[0] * abs((x - p[1]) / data['n1'].size - data['n1'].size / 2)
Though, I'm getting the error:
The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
However, any and all evaluate an entire list to a single boolean value.
More info:
I've used lambdas to fit data to a sine wave by using the following:
fitfunc = lambda p, x: 1 + p[0] * sin(pi * x / data['n1'].size + p[1])
errfunc = lambda p, x, y: fitfunc(p, x) - y # Distance to the target function
Then in a loop:
data = np.genfromtxt(dataFileName, names=('n1', 'n2'))
xAxisSeries = scipy.linspace(0., data['n1'].max(), data['n1'].size)
p0 = [489., 123.] # Initial guess for the parameters
p1, success = scipy.optimize.leastsq(errfunc, p0[:], args=(xAxisSeries, data['n2']))
#time says which points from the sine wave will be plotted
time = scipy.linspace(0., data['n1'].max(), 100)
pylab.plot(time, fitfunc(p1, time), 'r-')
I'm trying to use a lambda function because optimize.leastsq requires one. I'm using the exact same code with the exception of fitfunc being changed.

The code above does not look idiomatic and is making life harder than it should be. :)
If you're trying to define a function and give it a name at the same time, the conventional approach is to use def, not lambda.
fitfunc = lambda p, x: ... ## you're making a named function, so just do...
def fitfunc(p, x): ...
And once you have that, you don't have to simulate short-circuiting branches with 'and' and 'or': you can just use if. You're getting into trouble trying to simulate if.

Truely speaking, it is not clear what you are trying to do with your lambda function.
But maybe it is the time of the day on my side of the globe...
In anycase, do note, that any(somelist) and np.array().any() are the same, but can be called differently.
In [2]: a=np.ones(4)
In [3]: a
Out[3]: array([ 1., 1., 1., 1.])
In [4]: a.any()
Out[4]: True
In [8]: a[1]=0
In [9]: a.all()
Out[9]: False
In [11]: somelist=["1","1","a","3"]
In [12]: any(somelist)
Out[12]: True
Do note also comment, how do you call this function? Can you please post more code?

Use def and scipy.optimize.curve_fit().
import scipy.optimize as so
def fitfunc(p, x):
'''Define fit function'''
if x < p[1]:
return 1 + p[0] * abs((data['n1'].size + x - p[1]) / data['n1'].size - data['n1'].size / 2)
else:
return 1 + p[0] * abs((x - p[1]) / data['n1'].size - data['n1'].size / 2)
popt, pcov = so.curve_fit(fitfunc, x, y)

Related

Solve integral in an annular domain in Python

I am trying to solve a function in an annular domain that has a change of phase with respect to the angular direction of the annulus.
My attempt to solve it is the following:
import numpy as np
from scipy import integrate
def f(x0, y0):
r = np.sqrt(x0**2 + y0**2)
if r >= rIn and r <= rOut:
theta = np.arctan(y0 / x0)
R = np.sqrt((x - x0)**2 + (y - y0)**2 + z**2)
integrand = (np.exp(-1j * (k*R + theta))) / R
return integrand
else:
return 0
# Test
rIn = 0.5
rOut = 1.5
x = 1
y = 1
z = 1
k = 3.66
I = integrate.dblquad(f, -rOut, rOut, lambda x0: -rOut, lambda x0: rOut)
My problem is that I don't know how to get rid of the division by zero occuring when I evaluate theta.
Any help will be more than appreciated!
Use numpy.arctan2 instead, it will have problems only if both x and y are zero, in which case the angle is undetermined.
Also I see you that your integrand is complex, in this case you will probably have to handle real and imaginary part separately, as done here.

Error in newton raphson method finding root

I was trying to use the newton raphson method to compute the derivative of a function and I got the following error:
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
acc = 10**-4
x = sym.Symbol('x')
def p(x): #define the function
return 924*x**6 - 2772*x**5 + 3150*x**4 - 1680*x**3 + 420*x**2 - 42*x + 1
p_prime = sym.diff(p(x))
def newton(p,p_prime, acc, start_val):
x= start_val
delta = p(x)/p_prime(x)
while abs(delta) > acc:
delta = p(x)/p_prime(x)
print(abs(delta))
x =x- delta;
return round(x, acc);
a = newton(p, p_prime,-4, 0)
The error was:
delta = p(x)/p_prime(x)
TypeError: 'Add' object is not callable
There are a few mistakes in your code, correcting them as pointed out in the following modified code snippet, it works fine:
def p_prime(xval): # define as a function and substitute the value of x
return sym.diff(p(x)).subs(x, xval)
def newton(p, p_prime, prec, start_val): # rename the output precision variable
x = start_val # otherwise it's shadowing acc tolerance
delta = p(x) / p_prime(x) # call the function p_prime()
while abs(delta) > acc:
delta = p(x) / p_prime(x)
x = x - delta
return round(x, prec) # return when the while loop terminates
a = newton(p, p_prime,4, 0.1)
a
# 0.0338
The following animation shows how Newton-Raphson converges to a root of the polynomial p(x).
Your main problem is to call something which is not callable.
Functions are callable
Sympy expressions are not callable
import sympy as sym
def f(x):
return x**2
x = sym.Symbol("x")
g = 2*x
print(callable(f)) # True
print(callable(g)) # False
print(f(0)) # print(0)
print(g(0)) # error
So, in your case
def p(x):
return 924*x**6 - 2772*x**5 + 3150*x**4 - 1680*x**3 + 420*x**2 - 42*x + 1
print(p(0)) # p is callable, gives the result 1
print(p(1)) # p is callable, gives the result 1
print(p(2)) # p is callable, gives the result 8989
print(callable(p)) # True
And now, if you use a symbolic variable from sympy you get:
x = sym.Symbol("x")
myp = p(x)
print(callable(myp)) # False
print(myp(0)) # gives an error, because myp is an expression, which is not callable
So, if you use diff to get the derivative of the function, you will get a sympy expression. You must transform this expression to a callable function. To do it, you can use the lambda function:
def p(x):
return 924*x**6 - 2772*x**5 + 3150*x**4 - 1680*x**3 + 420*x**2 - 42*x + 1
x = sym.Symbol("x")
myp = p(x)
mydpdx = sym.diff(myp, x) # get the derivative, once myp is an expression
dpdx = lambda x0: mydpdx.subs(x, x0) # dpdx is callable
print(dpdx(0)) # gives '-42'
Another way to make a sympy expression callable is to use lambdify from sympy:
dpdx = sym.lambdify(x, mydpdx)
In sympy functions usually are represented as expressions. Therefore, sym.diff() returns an expression and not a callable function. It makes sense to also represent p as an expression involving x.
To "call" a function p on x, p.subs(x, value) is used. To get a numeric answer, use p.subs(x, value).evalf().
import sympy as sym
acc = 10 ** -4
x = sym.Symbol('x')
p = 924 * x ** 6 - 2772 * x ** 5 + 3150 * x ** 4 - 1680 * x ** 3 + 420 * x ** 2 - 42 * x + 1
p_prime = sym.diff(p)
def newton(p, p_prime, acc, start_val):
xi = start_val
delta = sym.oo
while abs(delta) > acc:
delta = (p / p_prime).subs(x, xi).evalf()
print(xi, delta)
xi -= delta
return xi
a = newton(p, p_prime, 10**-4, 0)
Intermediate values:
0 -0.0238095238095238
0.0238095238095238 -0.00876459050881560
0.0325741143183394 -0.00117130331903862
0.0337454176373780 -1.98196463560775e-5
0.0337652372837341
PS: To plot a function represented as an expression:
sym.plot(p, (x, -0.03, 1.03), ylim=(-0.5, 1.5))

Python double integral taking too long to compute

I am trying to compute the fresnel integral over a grid of coordinates using dblquad. But its taking very long and finally it's not giving any result.
Below is my code. In this code I integrated only over a 10 x 10 grid but I need to integrate at least over a 500 x 500 grid.
import time
st = time.time()
import pylab
import scipy.integrate as inte
import numpy as np
print 'imhere 0'
def sinIntegrand(y,x, X , Y):
a = 0.0001
R = 2e-3
z = 10e-3
Lambda = 0.5e-6
alpha = 0.01
k = np.pi * 2 / Lambda
return np.cos(k * (((x-R)**2)*a + (R-(x**2 + y**2)) * np.tan(np.radians(alpha)) + ((x - X)**2 + (y - Y)**2) / (2 * z)))
print 'im here 1'
def cosIntegrand(y,x,X,Y):
a = 0.0001
R = 2e-3
z = 10e-3
Lambda = 0.5e-6
alpha = 0.01
k = np.pi * 2 / Lambda
return np.sin(k * (((x-R)**2)*a + (R-(x**2 + y**2)) * np.tan(np.radians(alpha)) + ((x - X)**2 + (y - Y)**2) / (2 * z)))
def y1(x,R = 2e-3):
return (R**2 - x**2)**0.5
def y2(x, R = 2e-3):
return -1*(R**2 - x**2)**0.5
points = np.linspace(-1e-3,1e-3,10)
points2 = np.linspace(1e-3,-1e-3,10)
yv,xv = np.meshgrid(points , points2)
#def integrate_on_grid(func, lo, hi,y1,y2):
# """Returns a callable that can be evaluated on a grid."""
# return np.vectorize(lambda n,m: dblquad(func, lo, hi,y1,y2,(n,m))[0])
#
#intensity = abs(integrate_on_grid(sinIntegrand,-1e-3 ,1e-3,y1, y2)(yv,xv))**2 + abs(integrate_on_grid(cosIntegrand,-1e-3 ,1e-3,y1, y2)(yv,xv))**2
Intensity = []
print 'im here2'
for i in points:
row = []
for j in points2:
print 'im here'
intensity = abs(inte.dblquad(sinIntegrand,-1e-3 ,1e-3,y1, y2,(i,j))[0])**2 + abs(inte.dblquad(cosIntegrand,-1e-3 ,1e-3,y1, y2,(i,j))[0])**2
row.append(intensity)
Intensity.append(row)
Intensity = np.asarray(Intensity)
pylab.imshow(Intensity,cmap = 'gray')
pylab.show()
print str(time.time() - st)
I would really appreciate if you could tell any better way of doing this.
Using a scipy.integrate.dblquad to calculate every pixel of your image is going to be slow in any case.
You should try rewriting your mathematical problem so you can use some classical function in scipy.special instead. For instance, scipy.special.fresnel might work, although it is 1D and your problem seems to be in 2D. Otherwise, that there is a relationship between the Fresnel integral and the incomplete Gamma function (scipy.special.gammainc), if that helps.
If none of this work, as a last resort you can spend time optimizing your code and adapting it to Cython. This it will probably give a speed up of a factor of 10 to 100 (see this answer). Though this wouldn't be sufficient to go from a grid 10x10 to a grid 500x500.

how do I make a numpy.piecewise function of arbitrary length? (having lambda issues)

I'm trying to plot a piecewise fit to my data, but I need to do it with an arbitrary number of line segments. Sometimes there are three segments; sometimes there are two. I'm storing the coefficients of the fit in actable and the bounds on the segments in btable.
Here are example values of my bounds:
btable = [[0.00499999989, 0.0244274978], [0.0244275965, 0.0599999987]]
Here are example values of my coefficients:
actable = [[0.0108687987, -0.673182865, 14.6420775], [0.00410866373, -0.0588355861, 1.07750032]]
Here's what my code looks like:
rfig = plt.figure()
<>various other plot specifications<>
x = np.arange(0.005, 0.06, 0.0001)
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x=x: np.log10(actable[j][0] + actable[j][2] * x + actable[j][2] * x**2) for j in list(range(len(actable)))])
plt.plot(x, y)
The problem is that lambda sets itself to the last instance of the list, so it uses the coefficients for the last segment for all the segments. I don't know how to do a piecewise function without using lambda.
Currently, I'm cheating by doing this:
if len(btable) == 2:
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x: np.log10(actable[0][0] + actable[0][1] * x + actable[0][2] * x**2), lambda x: np.log10(actable[1][0] + actable[1][1] * x + actable[1][2] * x**2)])
else if len(btable) == 3:
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x: np.log10(actable[0][0] + actable[0][1] * x + actable[0][2] * x**2), lambda x: np.log10(actable[1][0] + actable[1][1] * x + actable[1][2] * x**2), lambda x: np.log10(actable[2][0] + actable[2][1] * x + actable[2][2] * x**2)])
else
print('Oh no! You have fewer than 2 or more than 3 segments!')
But this makes me feel icky on the inside. I know there must be a better solution. Can someone help?
This issue is common enough that Python's official documentation has an article Why do lambdas defined in a loop with different values all return the same result? with a suggested solution: create a local variable to be initialized by the loop variable, to capture the changing values of the latter within the function.
That is, in the definition of y it suffices to replace
[lambda x=x: np.log10(actable[j][0] + actable[j][1] * x + actable[j][2] * x**2) for j in range(len(actable))]
by
[lambda x=x, k=j: np.log10(actable[k][0] + actable[k][1] * x + actable[k][2] * x**2) for j in range(len(actable))]
By the way, one can use one-sided inequalities to specify ranges for numpy.piecewise: the last of the conditions that evaluate to True will trigger the corresponding function. (This is a somewhat counterintuitive priority; using the first true condition would be more natural, like SymPy does). If the breakpoints are arranged in increasing order, then one should use "x>=" inequalities:
breaks = np.arange(0, 10) # breakpoints
coeff = np.arange(0, 20, 2) # coefficients to use
x = np.arange(0, 10, 0.1)
y = np.piecewise(x, [x >= b for b in breaks], [lambda x=x, a=c: a*x for c in coeff])
Here each coefficient will be used for the interval that begins with the corresponding breakpoint; e.g., coefficient c=0 is used in the range 0<=x<1, coefficient c=2 in the range 1<=x<2, and so on.

how do I find out how many arguments a lambda function needs

I'm trying to create a function that will do a least squares fit based on a passed in lambda function. I want to create an array of zeroes of length equal to that of the number of arguments taken by the lambda function for the initial guess to the lambda function. so if its linear I want [0,0] and for quadratic I want [0,0,0].
#polynomial functions
linear = lambda p, x: p[0] * x + p[1]
quadratic = lambda p, x: p[0] * x**2 + p[1] * x + p[2]
cubic = lambda p, x: p[0] * x**3 + p[1] * x**2 + p[2] * x + p[3]
#polynomial functions forced through 0
linear_zero = lambda p, x: p[0] * x
quadratic_zero = lambda p, x: p[0] * x**2 + p[1] * x
cubic_zero = lambda p, x: p[0] * x**3 + p[1] * x**2 + p[2] * x
def linFit(x, y,fitfunc):
errfunc = lambda p, x, y: fitfunc(p, x) - y
Here I want to create a array of zeros. But at this point p isn't defined. so len(p) does not work.
init_p = np.array(zeros(len(p))) #bundle initial values in initial parameters
p1, success = optimize.leastsq(errfunc, init_p.copy(), args = (x, y))
return p1
under python >= 2.7:
>>> l = lambda a, b: None
>>> l.func_code.co_argcount
2
or under 2.6:
>>> l.__code__.co_argcount
2
by looking at it's code object __code__:
>>> p=lambda x,y:x+y
>>> len(p.__code__.co_varnames)
2
>>> p.__code__.co_varnames
('x', 'y')

Categories