I'm a student learning ML with Python. I have the following code which generates a np.array, uses it as a parameter of the function and creates its plot:
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize as opt
def _y_(x):
return np.sin(x/5)*np.exp(x/10) + 5*np.exp(-x/2)
x = np.arange(1, 30.01, 0.01)
y = _y_(x)
plt.plot(x, y)
plt.show()
min1 = opt.minimize(y, x0=2)
print(res1)
print()
min2 = opt.minimize(y, x0=15)
print(min2)
print()
min3 = opt.minimize(y, x0=30)
print(min3)
print()
But when I try to find the minimum of the function for three different points, I get an error:
'numpy.ndarray' object is not callable
It occurs on this line:
min1 = opt.minimize(y, x0=2)
Please, help me fix it!
The minimize function of scipy.optimize expects a callable argument (a function) as its first argument, as per its documentation.
You are providing the array y as input argument. Instead, you should provide the function that calculates y from x, so you should provide _y_. The code min1 = opt.minimize(_y_, x0=2) works without issues.
Related
I am trying to create an curve fitting program using LMFIT on python (Anaconda) but I keep receiving the same error message: TypeError: only size-1 arrays can be converted to Python scalars. I was able to perform an optimization using just one function, but when I try to optimize a function that calls other user-defined functions, I get this error.
import numpy as np
from matplotlib import pyplot
import scipy.special as sp
from scipy import integrate
import lmfit as lm
#Defining the first function.
def function1(alpha,q,s,l):
Intensity = alpha**2 + q -s*alpha / (5*l)
return Intensity
#Defining a second function that will perform a integration over the first function.
def integrate_function(q,s,l):
func_parameters = {
'q':q,
's':s,
'l':l,
}
to_be_integrated = lambda alpha: function1(alpha, **func_parameters)
result, error = integrate.quad(to_be_integrated, 0, 10)
return result
#Setting up the LMFIT model. Here I also provide the initial guess for the parameters.
integrate_function_model = lm.Model(integrate_function, independent_vars=['q'])
integrate_function_model.set_param_hint('s', value=2, min=-10.0, max=20.0, vary=True)
integrate_function_model.set_param_hint('l', value=3, min=1.0, max=10.0, vary=True)
initial_params = integrate_function_model.make_params()
#Creating data to be fitted (I also add some noise)
#Here I set s=1.5 and l=5.0 and I want the optimization routine to be able to find out these numbers.
x_data = np.linspace(0, 10, 100)
y_data = np.zeros(len(x_data))
for i in range(len(x_data)):
y_data[i] = integrate_function(x_data[i],1.5,5.0) + 5.0*np.random.random()
#Fitting the data.
fitting = integrate_function_model.fit(y_data, initial_params, q=x_data, method='leastsq')
#Printing original data and fitted model.
pyplot.plot(x_data, y_data, color='green', lw=2)
pyplot.plot(x_data, fitting.best_fit, color='blue', lw=2)
pyplot.show()
The error occurs when your function integrate_function is called with a np.array as an argument for q:
>>> integrate_function(1,1,1)
333.33333333333337
>>> integrate_function(np.array([1,2]),1,1)
TypeError: only size-1 arrays can be converted to Python scalars
This happens during output.fit where integrate.quad is called. quad is not able to handle vectorized input, which is happening in your case.
One option to fix this would be to change integrate_function to handle the case where q is an array accordingly, for example by manually including a loop over all values in q:
def integrate_function(q,s,l):
# Make q iterable if it is only a float/int
if not hasattr(q, '__iter__'):
q = np.array([q])
result = []
for q0 in q:
func_parameters = {
'q':q0,
's':s,
'l':l,
}
to_be_integrated = lambda alpha: function1(alpha, **func_parameters)
result.append(integrate.quad(to_be_integrated, 0, 10)[0])
return np.array(result)
Executing your code with the modified integrate_function then yields the following plot:
When I run my code I receive the error "unsupported operand type(s) for ** or pow(): 'numpy.ufunc' and 'float'"
The code is:
import numpy as np
import matplotlib.pyplot as plt
from numpy import sqrt,exp,log
from scipy import linalg
from scipy.optimize import curve_fit
data1 = np.loadtxt('decay1.txt', float,skiprows=1)
t = data1[:,0]
n = data1[:,1]
data2 = np.loadtxt('decay2.txt', float,skiprows=1)
T = data2[:,0]
N = data2[:,1]
def Radio(n,t,tao,b):
return (n*(exp**(-(t/tao)))) + b
guesses = (1,1,1)
guesses2 = (1,1,1)
(p0,p1,p2),cc = curve_fit(Radio,t,n,guesses)
(p02,p12,p22),cc2 = curve_fit(Radio,T,N,guesses2)
yfit = Radio(t,p0,p1,p2)
y2fit = Radio(T,p02,p12,p22)
I have to fit the function to the radioactive decay data, so tell me if I also messed up the code to fit a function to it. Thanks for any help!
numpy.exp is the exponential function and ** is the power operator, so you're trying to raise a function definition to the power (-(t/tao)). I think you wanted
def Radio(n,t,tao,b):
return (n*(exp(-(t/tao)))) + b
With respect to the use of the optimization function, there are a couple of issues. First, you are using n as both the parameter (inside the Radio method) and the dependent variable data (from the problem statement), which is confusing things. I would change that to something like a (as is used in the curve_fit doc. That's not really necessary, but helps readability.
Second, and more importantly, the function to be fit must have the independent variable (in this case, t) as the first argument. I think what is happening to cause a flat fit is that you are actually fitting the curve n->Radio(n) (with all those other variables as parameters) instead of t->Radio(t).
I tried to plot the output of the defined function with respect to z. However the error TypeError: unhashable type: 'numpy.ndarray' is shown. Please help.
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
a=1.48185562
b=0.57081914
c=-0.25098188
H0=70.32724312
z=np.linspace(0.0,1.5,100)
omega_m0=0.3
dlabel= 'w(z) vz z'
def func(z):
sp.var('z+1')
H=((2/H0)*((b*(z+1)+c*(z+1)**0.5+2.0-a-b-c)*(1-0.5*a*(z+1)**(-0.5)) - ((z+1)-a*(z+1)**0.5-1.0+a)*(b+c*0.5*(z+1)**(-0.5)))/(b*(z+1)+c*(z+1)**0.5+2.0-a-b-c)**2)**(-1)
return ((2*(z+1)/3)*(sp.diff(sp.log(H)))-1)/(1-(H/H0)**2*omega_m0*(z+1)**3)
wz=func(z)
plt.plot(z,wz)
plt.xlabel('z')
plt.ylabel('w(z)')
plt.show()
I'm not sure what you want to do with sp.var('z+1')... at least I hope you were not trying to create a variable named z+1. I got the code to run but I let you make sure it does what you want and complain if not :)
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
a=1.48185562
b=0.57081914
c=-0.25098188
H0=70.32724312
x=np.linspace(0.0,1.5,100)
omega_m0=0.3
dlabel= 'w(z) vz z'
sp.var('z')
def func(z):
H=((2/H0)*((b*(z+1)+c*(z+1)**0.5+2.0-a-b-c)*(1-0.5*a*(z+1)**(-0.5)) - ((z+1)-a*(z+1)**0.5-1.0+a)*(b+c*0.5*(z+1)**(-0.5)))/(b*(z+1)+c*(z+1)**0.5+2.0-a-b-c)**2)**(-1)
return ((2*(z+1)/3)*(sp.diff(sp.log(H)))-1)/(1-(H/H0)**2*omega_m0*(z+1)**3)
wz = [func(z).evalf(subs = {z : y}) for y in x]
plt.plot(x,wz)
plt.xlabel('z')
plt.ylabel('w(z)')
plt.show()
EDIT: in order to get wz, the following piece is much faster ( cf Evaluate sympy expression from an array of values ):
from sympy.utilities.lambdify import lambdify
func_np_ready = lambdify(z, func(z),'numpy') # returns a numpy-ready function
wz = func_np_ready(x)
You may be better off flagging your question with sympy - it's probably the behaviour of one of those functions that's causing the issue, and someone else might know all about it.
It's probably a good idea to split those really long formulas up into multi lines (at least while debugging) to help you track down the error. Also put in some prints etc.
I know it's not what you want to achieve but if I cut out the sympy (I don't have it installed!) and adjust the array lengths it plots without error:
...
H=((2/H0)*((b*(z+1)+c*(z+1)**0.5+2.0-a-b-c)*(1-0.5*a*(z+1)**(-0.5)) - ((z+1)-a*(z+1)**0.5-1.0+a)*(b+c*0.5*(z+1)**(-0.5)))/(b*(z+1)+c*(z+1)**0.5+2.0-a-b-c)**2)**(-1)
return ((2*(z[:-1]+1)/3)*(np.diff(np.log(H)))-1)/(1-(H[:-1]/H0)**2*omega_m0*(z[:-1]+1)**3)
wz=func(z)
plt.plot(z[:-1],wz)
I have seen this question or a variant asked elsewhere e.g.
Scipy error using optimization module. Failure converting array to fortran
http://numpy-discussion.10968.n7.nabble.com/minimize-Algorithmen-Problem-with-boundarys-td37709.html
But they are not really put with a simple hackable example code. Nor are there any real answers (probably because of the lack of a simple demo of the problem).
The problem is, when trying to fit a function using scipy.optimise fmin_slsqp method you get this pretty opaque error
"failed in converting 8th argument `g' of _slsqp.slsqp to C/Fortran array"
In the code below I fit a linear function to random correlated data using the leastsq method. From the .docs I can see no reason that the same syntax shouldn't do the same thing using fmin_slsqp, but it doesn't.
Does anybody know why?
import numpy as nm
from scipy.optimize import leastsq, fmin_slsqp
import matplotlib.pyplot as plt
# residuals of linear function
def res(params,x,y_real):
y_fit = params[0] +x*params[1]
res = y_fit-y_real
return res
#generate correlated data
xx = nm.array([-0.51, 51.2])
yy = nm.array([0.33, 51.6])
means = [xx.mean(), yy.mean()]
stds = [xx.std() / 3, yy.std() / 3]
corr = 0.8 # correlation
covs = [[stds[0]**2 , stds[0]*stds[1]*corr],
[stds[0]*stds[1]*corr, stds[1]**2]]
m = nm.random.multivariate_normal(means, covs, 100)
x = m[:,0]
y = m[:,1]
# Initial values of parameters
initvals = [0,0]
fit1,j = leastsq(res, initvals, args=(x,y))
#Plot fit 1
y_fit = fit1[0] + fit1[1]*x
plt.scatter(x,y)
plt.plot(x,y_fit)
plt.show()
fit2 = fmin_slsqp(res, initvals, args=(x,y))
I get the same error when the return from the Objective function is not a scalar. A minimal example which causes this error is
from scipy.optimize import fmin_slsqp
def fn(x):
return [0.,1.]
x = [0, 1., 2.]
minsoln = fmin_slsqp(fn, x)
while the following does not raise the error,
from scipy.optimize import fmin_slsqp
def fn(x):
return 0.
x = [0, 1., 2.]
minsoln = fmin_slsqp(fn, x)
I think this is either a bug or should have a clearer error message. I've raise an issue.
UPDATE:
This has now been resolved by b-carter to give a clear error message,
"Objective function must return a scalar"
with the documentation updated, see this thread for discussion.
Hi I had the same error with the following:
def ptf_returns(weights,returns):
return pd.DataFrame(np.array(returns).T*(weights)).T.mean().mean()
When I add the following it works:
def ptf_returns(weights,returns):
return float(pd.DataFrame(np.array(returns).T*(weights)).T.mean().mean())
The bug seems to be oriented around the type() of the response.
When I run the code
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# Initial conditions
def f_func(eta,y_in):
y_out = np.zeros(3)
y_out[0] = y_in[1]
y_out[1] = y_in[2]
y_out[2] = -y_in[0]*y_in[2]/2
return y_out
eta = np.linspace(0,8,100)
X0 = [0,0,0.33206]
X = odeint(f_func,X0,eta)
I get the error
'float' object has no attribute '__getitem__'
When I run the following MATLAB program, everything works OK. The MATLAB function ode45 is equivalent to Pythons' odeint.
main program:
clear
global beta
beta = 1;
initial_value = [0,0,1.2322];
eta = linspace(0,4,100)
[x_out, y_out] = ode45(#falkner_skan,eta,initial_value);
plot(x_out,y_out(:,2))
falkner_skan function:
function y_out = falkner_skan(x,y_in)
global beta
y_out(1,1) = y_in(2);
y_out(2,1) = y_in(3);
y_out(3,1) = -y_in(1)*y_in(3) - beta*(1-y_in(2)^2);
end
This and this and this thread does not seem to give me any guidance.
It seems as though y_in is not a list but a float value. The error rises because you're trying to get an item with obj[x] of an object which doesn't support it.
Looking at the documentation for odeint it says that the input function should take two arguments, the first being your data object and the second should be a float. Your implementation of f_func is therefore wrong.
NumPy has float 64 object which has item() function, np.float64(10.5).item()
I had the same issue. According to documentation for odeint, in f_func(eta,y_in), change the order of eta and y_in, i.e. write it as f_func(y_in, eta) or set the argument tfirst to be True.