Error trying to plot a simple function in python - python

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I get a different error depending on the integration method. The function works correctly when given a single value.
import matplotlib.pyplot as plt
import scipy as sp
import numpy as np
def mass_enc(R):
def int(r): return r**2 * r
return sp.integrate.quad(int, 0, R)
print(mass_enc(10))
t1 = np.arange(0.1, 5.0, 0.1)
plt.plot(t1, mass_enc(t1))

The problem is that you are calling sp.integrate.quad with an array as argument. While some functions actually allow that, quad does not. So you need to provide each value of R individually. This can be done by map(function, iterable). So here is how you can do it.
import matplotlib.pyplot as plt
import scipy as sp
import numpy as np
def inte(r):
return r**2 * r
def mass_enc(R):
return sp.integrate.quad(inte, 0, R)[0]
print(mass_enc(10))
t1 = np.arange(0.1, 5.0, 0.1)
m = map( mass_enc, t1)
plt.plot(t1, m)
plt.show()
Mind, that you should never call any object in python int, since int is a basic type in python and doing that can cause a lot of trouble.

Related

Define and plot constant function in python

I am trying to plot a constant function in python (this is not what I am actually trying to do but if I solve this it might be a first step).
import matplotlib.pyplot as plt
import numpy as np
def constant_function(x):
return 2
t1 = np.arange(0.0,1.0,0.1)
plt.plot(t1,constant_function(t1))
However I get the error
ValueError: x and y must have same first dimension, but have shapes (10,) and (1,)
One option is to vectorize your function:
import matplotlib.pyplot as plt
import numpy as np
#np.vectorize
def constant_function(x):
return 2
t1 = np.arange(0.0, 1.0, 0.1)
plt.plot(t1, constant_function(t1))
plt.show()
this way constant_function(t1) will return [2 2 2 2 2 2 2 2 2 2] (i.e. [f(x[0]), f(x[1]), f(x[2]), ...]) instead of just 2 and the dimensions will match.
In my opinion vectorize is overkill to plot a simple "function" which just yields a constant value, especially if basic python syntax is the source of the problem. (And imho decorators are quite more advanced than using simple numpy methods.)
Instead I'd recommend just setting the return value of the function to an array of the same shape:
import matplotlib.pyplot as plt
import numpy as np
def constant_function(x):
return 2
t1 = np.arange(0.0,1.0,0.1)
plt.plot(t1, np.full(t1.shape, constant_function(t1)))
Since constant_function currently takes an argument which is not used, another improvement could be to construct the array inside constant_function, using the information from the argument x:
def constant_function(x):
return np.full(x.shape, 2)
t1 = np.arange(0.0,1.0,0.1)
plt.plot(t1, constant_function(t1))

Evaluate sum of step functions

I have a fairly large number (around 1000) of step functions, each with only two intervals. I'd like to sum them up and then find the maximum value. What is the best way to do this? I've tried out sympy, with code as follows:
from sympy import Piecewise, piecewise_fold, evalf
from sympy.abc import x
from sympy.plotting import *
import numpy as np
S = 20
t = np.random.random(20)
sum_piecewise = None
for s in range(S):
p = Piecewise((np.random.random(), x<t[s]), (np.random.random(), x>=t[s]))
if not sum_piecewise:
sum_piecewise = p
else:
sum_piecewise += p
print sum_piecewise.evalf(0.2)
However, this outputs a large symbolic expression and not an actual value, which is what I want.
As it appears that you consider numerical functions, it is better (in terms of performance) to work with Numpy. Here's one approach:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(10)
S = 20 # number of piecewise functions
# generate S function parameters.
# For example, the k-th function is defined as equal to
# p_values[k,0] when t<t_values[k] and equal to
# p_values[k,1] when t>= t_values[k]
t_values = np.random.random(S)
p_values = np.random.random((S,2))
# define a piecewise function given the function's parameters
def p_func(t, t0, p0):
return np.piecewise(t, [t < t0, t >= t0], p0)
# define a function that sums a set of piecewise functions corresponding to
# parameter arrays t_values and p_values
def p_sum(t, t_values, p_values):
return np.sum([p_func(t, t0, p0) for t0, p0 in zip(t_values,p_values)])
Here is the plot of the sum of functions:
t_range = np.linspace(0,1,1000)
plt.plot(t_range, [p_sum(tt,t_values,p_values) for tt in t_range])
Clearly, in order to find the maximum, it suffices to consider only the S time instants contained in t_values. For this example,
np.max([p_sum(tt,t_values,p_values) for tt in t_values])
11.945901591934897
What about using substitution? Try changing sum_piecewise.evalf(0.2) by sum_piecewise.subs(x, 0.2)

Create mpf from array

I'm trying to use fsolve in combination with the mpmath package.
However, I get the error cannot create mpf from array([mpf('1.0')], dtype=object).
Here is a minimal example reproducing the error. For this example, I technically do not need the mpmath package, but my actual function contains hyperconfluent functions that do.
from scipy.optimize import fsolve
#from mpmath import hyp1f1 as hyp1f1mp
#from mpmath import gamma as gammamp
import mpmath as mp
#import numpy as np
mp.dps = 250; mp.pretty = True
def cosFunc(p):
vn = p
output = mp.sin(vn)
return output
estimate = mp.mpf(1)
value = fsolve(cosFunc,estimate)
print value
I found a similar question suggesting to use np.frompyfunc (How to mpf an array?), but it tells me that the function is not callable (when I apply it on vn).
The trick is to apply np.frompyfunc to a function instead of a value. I think the following modification would make your function work:
def cosFunc(p):
vn = p
np_sin = np.frompyfunc(mp.sin, 1, 1)
output = np_sin(vn)
return float(output)
value = fsolve(cosFunc, 1)
print value
The specific cause of the error you is this:
(Pdb) x0
array([mpf('1.0')], dtype=object)
(Pdb) mp.sin(x0)
*** TypeError: cannot create mpf from array([mpf('1.0')], dtype=object)
What happens is that fsolve tries to convert your estimate to array and numpy does not know how to handle mpmath objects.
>>> np.asarray(mp.mpf(1))
>>> array(mpf('1.0'), dtype=object)
Changing how fsolve works is not very productive, so your best bet seems to be to teach your function to handle arrays of mpmath objects
def cos_func(p):
vn = p
if isinstance(p, np.ndarray):
if p.size == 0:
vn = p[0]
else:
raise ValueError # or whatever you want to do here"
return mp.sin(vn)

Function that computes rk method/no plotting

import math
import matplotlib
import numpy as np
from numpy import linspace
tmax=10.0
n=2000
G=4
D=-1
m=2
t=np.linspace (0,400,n+1)
phi=10
dphi=delta=phi_dot=np.linspace(0,400,n+1)
def f(delta_dot,t):
return ((G)*(D*delta+m))
def iterate (func,phi,delta,tmax,n):
dt=tmax/(n-1)
t=0.0
for i in range(n):
phi,delta = func (phi,delta,t,dt)
t += dt
return phi
def rk_iter(phi,delta,t,dt):
k1=f(t,phi)
k2=f(t+dt*0.5,phi+k1*0.5*dt)
k3=f(t+dt*0.5,phi*k2*0.5*dt)
k4=f(t*dt,phi*k3*dt)
delta +=dt*(k1+2*k2+2*k3+k4)/6
k1=k2=k3=k4=delta=phi_dot
phi += dt*(k1+2*k2+2*k3+k4)/6
return phi,delta
runge_kutta = lambda delta, phi,tmax,n:iterate(rk_iter,delta,phi,tmax,n)
def plot_result (delta,phi,tmax,n):
dt=tmax/(n-1)
error_rk=[]
r_rk=[]
t=0.0
phi=phi_rk=phi
delta=delta_rk=delta
for i in range(n):
phi_rk,delta_rk=rk_iter(phi_rk,delta_rk,t,dt=tmax/(n-1))
t+=dt
_plot("error.png","Error","time t", "error e",error_rk)
def _plot(title,xlabel,ylabel,rk):
import matplotlib.pyplot as plt
plt.title(title)
plt.ylabel(ylabel)
plt.xlabel(xlabel)
plt.plot(rk,"r--",label="Runge-Kutta")
plt.legend(loc=4)
plt.grid(True)
plt.plot(runge_kutta,t)
print "runge_kutta=", runge_kutta(phi,delta,tmax,n)
print "tmax=",t
I have no idea how to get the function plt.show() to work. What do I have to do to open a plot window?
You haven't defined f; instead, f is being imported from matplotlib by the statement from matplotlib import *:
In [10]: import matplotlib
In [11]: matplotlib.f
Out[11]: Forward: "a"
In [12]: matplotlib.f(1,1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-5843007d7dbe> in <module>()
----> 1 matplotlib.f(1,1)
TypeError: __call__() takes at most 2 arguments (3 given)
You will save yourself many headaches like this in the future if you never use the * form of the import statement in your scripts. For example, instead of from math import *, use one of these two forms:
import math
# Now refer to math.sin, math.pi, etc.
or, explicilty import only the names that you will use:
from math import sin, pi
At no point do you call the plot_result procedure. And even if you called it, you do not fill the rk and error_rk lists. You could simply use the result from the runge_kutta call,...
As commented in the other, duplicate post, you define the system equation as f(y,t) but use it as f(t,y).
There is some confusion in the usage of delta, sometimes it is the integration variable, sometimes the rk4-step update.
In the rk4-step, there are some misplaced multiplications where there should be additions. And the line
k1=k2=k3=k4=delta=phi_dot
is complete nonsense and invalidates the previous computations and makes the rk4-update in the next step meaningless.
Remove the imports of math and linspace, neither is used in the code. Move the aliasing of plt to the top and merge it with the unnecessary import of matplotlib.

Error: [only length-1 arrays can be converted to Python scalars] when changing variable order

Dear Stackoverflow Community,
I am very new to Python and to programming in general, so please don't get mad when I don't get your answers and ask again.
I am trying to fit a curve to experimental data with scipy.optimization.curve_fit. This is my code:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as nm
from __future__ import division
import cantera as ct
from matplotlib.backends.backend_pdf import PdfPages
import math as ma
import scipy.optimize as so
R = 8.314
T = nm.array([700, 900, 1100, 1300, 1400, 1500, 1600, 1700])
k = nm.array([289, 25695, 763059, 6358040, 14623536, 30098925, 56605969, 98832907])
def func(A, E, T):
return A*ma.exp(-E/(R*T))
popt, pcov = so.curve_fit(func, T, k)
Now this code works for me, but if I change the function to:
def func(T, A, E)
and keep the rest I get:
TypeError: only length-1 arrays can be converted to Python scalars
Also I am not really convinced by the Parameter solution of the first one.
Can anyone tell me what happens when you change the variable order?
I got the same problem and found the cause and its solution:
The problem lies on the implementation of Scipy. After the optimal parameter has been found, Scipy calls your function with the input array xdata as first argument. That is, it calls func(xdata, *args), and the function complains with a type error because xdata is not an scalar. For example:
from math import erf
erf([1, 2]) # TypeError
erf(np.array([1, 2])) # TypeError
To avoid the error, you can add custom code for supporting arrays, or better, as suggested in the answer of Joris, use numpy functions because they have support for scalars and arrays.
If the math function is not in numpy , like erf or any custom function you coded, then I recommend you instead of doing from math import erf, to do as follows:
from math import erf as math_erf # only supports scalars
import numpy as np
erf = np.vectorize(math_erf) # adds array support
def fit_func(t,s):
return 0.5*(1.0-erf(t/(np.sqrt(2)*s)))
X = np.linspace(-5,5,1000)
Y = np.array([fit_func(x,1) for x in X])
curve_fit(fit_func, X, Y)
The curve_fit function from scipy does not handle very well embedded functions from the math module. When you change the exponential function to the numpy exponential function you don't get the error:
def func(A, E, T):
return A*np.exp(-E/(R*T))
I wonder whether you data shows an exponential decay of rate. The mathematical model may not be the most suitable one.
See the doc string of curve_fit
f : callable
The model function, f(x, ...). It must take the independent variable as the first argument and the parameters to fit as separate remaining arguments.
since your formula is essentially: k=A*ma.exp(-E/(R*T)), the right order of parameters in func should be (T, A, E) or (T, E, A).
Regarding the order of A and E, they don't really matter. If you flip them, the result will get flipped as well:
>>> def func(T, A, E):
return A*ma.exp(-E/(R*T))
>>> so.curve_fit(func, T, k)
(array([ 8.21449078e+00, -5.86499656e+04]), array([[ 6.07720215e+09, 4.31864058e+12],
[ 4.31864058e+12, 3.07102992e+15]]))
>>> def func(T, E, A):
return A*ma.exp(-E/(R*T))
>>> so.curve_fit(func, T, k)
(array([ -5.86499656e+04, 8.21449078e+00]), array([[ 3.07102992e+15, 4.31864058e+12],
[ 4.31864058e+12, 6.07720215e+09]]))
I didn't get your typeerror at all.

Categories