scipy.optimze curve_fit return wrong value - python

I was trying to fit a specific function with scipy and I got weird results. I decided to test something I know the answer to so I created this:
from scipy.optimize import curve_fit as cf
import numpy as np
import random
def func(x,a):
return a+X
X =[]
for i in range (10):
V = random.random()
X.append(i+3 + V/10)
print cf(func, np.array(range(10)),np.array(X))
I expected to get something around 3, nevertheless, here the output:
(array([ -2.18158824e-12]), inf)
As a side note, I tried to see what I send something to func and I got this:
print func(np.array(range(10)),3)
Traceback (most recent call last):
File "/tmp/py1759O-P", line 16, in <module>
print func(np.array(range(10)),3)
File "/tmp/py1759O-P", line 6, in func
return a+X
TypeError: unsupported operand type(s) for +: 'int' and 'list
What am I doing wrong?

Don't use x and X as variable names when they carry such different meanings (or perhaps you didn't know Python is case sensitive?):
def func(x,a):
return a+X
X =[]
x is a numpy array, X is a list, and a is a scalar parameter value.
a+X results in an error since you can not add a scalar to a list.

In func, the argument is x, but X is used in the body of the function.
Here's a modified version of your code. It uses a few more features of numpy (e.g. np.random.random() instead of random.random()).
from scipy.optimize import curve_fit as cf
import numpy as np
def func(x, a):
return a + x
n = 10
xdata = np.arange(n)
ydata = func(xdata, 3) + np.random.random(n) / 10
print cf(func, xdata, ydata)
The output is
(array([ 3.04734293]), array([[ 8.19208558e-05]]))

Related

Use a float number as a step size in function plot

I want to plot a function for 0.975 ≤ x ≤ 1.044 with step size 0.0001 and wonder how I can use a float number as step size?
The function I want to plot is y=−1+7x−21x2 +35x3 −35x4 +21x5 −7x6 +x7 and I have computed the code
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0.975, 1.044, 0.0001)
# calculate y for each element of x
y = x**7 - 7*x**6 + 21*x**5 - 35*x**4 + 35*x**3 - 21*x**2 + 7*x -1
fig, ax = plt.subplots()
ax.plot(x, y)
The code works fine if I replace the step size value to a int instead of a float, but when I use 0.0001 I get the error below. Is there someway I can fix this?
File "/opt/anaconda3/lib/python3.8/site-packages/numpy/core/function_base.py", line 117, in linspace
num = operator.index(num)
TypeError: 'float' object cannot be interpreted as an integer
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/idalundmark/Desktop/Programmeringsteknik för matematiker (Labb)/Avklarade labbar/untitled0.py", line 13, in <module>
x = np.linspace(0.975, 1.044, 0.0001)
File "<__array_function__ internals>", line 5, in linspace
File "/opt/anaconda3/lib/python3.8/site-packages/numpy/core/function_base.py", line 119, in linspace
raise TypeError(
TypeError: object of type <class 'float'> cannot be safely interpreted as an integer.
In numpy.linespace() the third parameter indicates the number of samples to generate. (Default it is 50) This could be a non-negative integer value. For more information, you can refer to the official documentation
As #AcaNg suggested in the comments, you can use numpy.arrange() instead. This is also similar to linspace, but uses a step size (instead of the number of samples).
If you do as shown here and below, the problem will be solved.
import matplotlib.pyplot as plt
import numpy as np
N = (1.044 - 0.975) / 0.0001
x = np.linspace(0.975, 1.044, num=int(N), endpoint= True)
# calculate y for each element of x
y = x**7 - 7*x**6 + 21*x**5 - 35*x**4 + 35*x**3 - 21*x**2 + 7*x -1
fig, ax = plt.subplots()
ax.plot(x, y)

how can i avoid generator in python

I want to plot a function in python but I can't seem to do it. I am running the following code, but I get an error that says I can't multiply a generator and a float together. Where is this coming from?
from math import *
import matplotlib.pyplot as plt
t=0.1
cd=t*exp(-t/2)
tau=10
nt=100
v=0.01
w=0.9
u=0.4
s0=10
p=5
for i in range (1,10):
sigma= u/(w+(s0/(p*cd)))
print(sigma)
C= lambda ksi: cd * (1-exp(((u-w * sigma)/v)*ksi))
plt.plot([-10,-9,-8,-7,-6,-5,-4,-3,-2,-1],[C(i for i in range (-10,-1))])
plt.xlabel(ksi)
plt.ylabel(concentration)
plt.title("tumeur avec regénessence")
plt.legend()
plt.show()
t+=tau/nt
this is the error I get
Traceback (most recent call last):
File "C:\Users\ilyes\Downloads\tumeur_avec_regénesence.py", line 18, in <module>
plt.plot([-10,-9,-8,-7,-6,-5,-4,-3,-2,-1],[C(i for i in range (-10,-1))])
File "C:\Users\ilyes\Downloads\tumeur_avec_regénesence.py", line 17, in <lambda>
C= lambda ksi: cd * (1-exp(((u-w * sigma)/v)*ksi))
TypeError: unsupported operand type(s) for *: 'float' and 'generator'
use
plt.plot([-10,-9,-8,-7,-6,-5,-4,-3,-2,-1],[C(i) for i in range (-10, 0)])
instead of
plt.plot([-10,-9,-8,-7,-6,-5,-4,-3,-2,-1],[C(i for i in range (-10,-1))])
explanation: you want to apply C on each value not on the hole list at once.
range(-10,-1) gives you values -10 <= v < -1 but you want -1 to be included.
Use numpy.
import numpy as np
import matplotlib.pyplot as plt
tau=10
nt=100
v=0.01
w=0.9
u=0.4
s0=10
p=5
ksi = np.arange(-10,0)
for t in np.arange(1,10)*tau/nt:
cd = t*np.exp(-t/2)
sigma = u/(w+(s0/(p*cd)))
C = lambda ksi: cd * (1-np.exp(((u-w * sigma)/v)*ksi))
plt.plot(ksi, C(ksi), label=f"t = {t}")
plt.xlabel("ksi")
plt.ylabel("concentration")
plt.title("tumeur avec regénessence")
plt.legend()
plt.show()

Plotting a symbolic function with multiple variables in Python

First code cell:
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
from ipywidgets.widgets import interact
sym.init_printing(use_latex="mathjax")
x, y, z, t = sym.symbols('x y z t')
I am required to plot the following function which I have defined as:
p_w = (1/sym.sqrt(sym.pi*(1-sym.exp(-2*t))))*sym.exp(-(z-sym.exp(-t))**2/(1-sym.exp(-2*t)))
Obviously t and z are both variables and I am to plot the function for t = 0.1,1, and 10, using z as my x-axis and p_w(z) as the y-axis.
I tried defining 3 functions j,k,l with the values subbed in for t like so:
j=p_w.evalf(subs={t:0.1})
k=p_w.evalf(subs={t:1})
l=p_w.evalf(subs={t:10})
then changing these to numpy arrays with the sym.lambdify() function:
j_np=sym.lambdify(z,j)
k_np=sym.lambdify(z,k)
l_np=sym.lambdify(z,l)
I defined my x-axis using:
myz = np.linspace(0,10,1000)
(The 1000 divisions was fairly arbitrary as I wasn't sure how many I'd need for an accurate graph)
Then I tried plotting just j to begin with as follows:
plt.plot(myz, j_np(myz))
and got the following error message:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-41-d743a8a00bcf> in <module>()
----> 1 plt.plot(myz,j_np(myz))
/anaconda3/lib/python3.6/site-packages/numpy/__init__.py in <lambda>. (_Dummy_164)
AttributeError: 'Mul' object has no attribute 'exp'
I am guessing the problem is that numpy does not understand 'exp' in the same way that sympy does, but I am unsure as to how to rectify it, or even if this is the problem at all. Any help would be much appreciated.
You can get this to work if you skip the evalf step:
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
from ipywidgets.widgets import interact
sym.init_printing(use_latex="mathjax")
# x, y, z, t = sym.symbols('x y z t')
from sympy.abc import x,y,z,t
p_w = (1/sym.sqrt(sym.pi*(1-sym.exp(-2*t))))*sym.exp(-(z - sym.exp(-t))**2/(1-sym.exp(-2*t)))
myz = np.linspace(0,10,1000)
lam=sym.lambdify((z,t), p_w)
myz = np.linspace(0,10,1000)
times = [.01, 1, 10]
for time in times:
plt.plot(myz, lam(myz, time))
Output:
It seems the underlying issue was that setting the time using p_w.evalf(subs={t:0.1}) doesn't really work:
print(p_w.evalf(subs={t:0.1}))
0.564189583547756*(1.0 - exp(-2*t))**(-0.5)*exp(-(z - exp(-t))**2/(1 - exp(-2*t)))
It works right if z is removed from p_w:
p_w = (1/sym.sqrt(sym.pi*(1-sym.exp(-2*t))))*sym.exp(-(- sym.exp(-t))**2/(1-sym.exp(-2*t)))
print(p_w.evalf(subs={t:0.1}))
0.0144778612224441
So apparently having the undetermined variable z in p_w prevents p_w.evalf from expanding the t terms correctly.

scipy curve_fit doesn't like math module

While trying to create an example with scipy.optimize curve_fit I found that scipy seems to be incompatible with Python's math module. While function f1 works fine, f2 throws an error message.
from scipy.optimize import curve_fit
from math import sin, pi, log, exp, floor, fabs, pow
x_axis = np.asarray([pi * i / 6 for i in range(-6, 7)])
y_axis = np.asarray([sin(i) for i in x_axis])
def f1(x, m, n):
return m * x + n
coeff1, mat = curve_fit(f1, x_axis, y_axis)
print(coeff1)
def f2(x, m, n):
return m * sin(x) + n
coeff2, mat = curve_fit(f2, x_axis, y_axis)
print(coeff2)
The full traceback is
Traceback (most recent call last):
File "/Documents/Programming/Eclipse/PythonDevFiles/so_test.py", line 49, in <module>
coeff2, mat = curve_fit(f2, x_axis, y_axis)
File "/usr/local/lib/python3.5/dist-packages/scipy/optimize/minpack.py", line 742, in curve_fit
res = leastsq(func, p0, Dfun=jac, full_output=1, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/scipy/optimize/minpack.py", line 377, in leastsq
shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
File "/usr/local/lib/python3.5/dist-packages/scipy/optimize/minpack.py", line 26, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "/usr/local/lib/python3.5/dist-packages/scipy/optimize/minpack.py", line 454, in func_wrapped
return func(xdata, *params) - ydata
File "/Documents/Programming/Eclipse/PythonDevFiles/so_test.py", line 47, in f2
return m * sin(x) + n
TypeError: only length-1 arrays can be converted to Python scalars
The error message appears with lists and numpy arrays as input alike. It affects all math functions, I tested (see functions in import) and must have something to do with, how the math module manipulates input data. This is most obvious with pow() function - if I don't import this function from math, curve_fit works properly with pow().
The obvious question - why does this happen and how can math functions be used with curve_fit?
P.S.: Please don't discuss, that one shouldn't fit the sample data with a linear fit. This was just chosen to illustrate the problem.
Be careful with numpy-arrays, operations working on arrays and operations working on scalars!
Scipy optimize assumes the input (initial-point) to be a 1d-array and often things go wrong in other cases (a list for example becomes an array and if you assumed to work on lists, things go havoc; those kind of problems are common here on StackOverflow and debugging is not that easy to do by the eye; code-interaction helps!).
import numpy as np
import math
x = np.ones(1)
np.sin(x)
> array([0.84147098])
math.sin(x)
> 0.8414709848078965 # this only works as numpy has dedicated support
# as indicated by the error-msg below!
x = np.ones(2)
np.sin(x)
> array([0.84147098, 0.84147098])
math.sin(x)
> TypeError: only size-1 arrays can be converted to Python scalars
To be honest: this is part of a very basic understanding of numpy and should be understood when using scipy's somewhat sensitive functions.

Float error while attempting to use the bisect optimizer within scipy

I’m having trouble using the bisect optimizer within scipy. Here are the relevant portions of my code:
How I’m importing things
import numpy as np
import scipy.optimize as sp
import matplotlib.pyplot as plt
Break in code, section causing errors below
#All variables are previously defined except for h
def BeamHeight(h):
x = 1000e3*M[i]*h/(fw*h^3-(fw-wt)(h-2*ft)^3) - Max_stress_steel
return x
for i in range(0,50):
h = np.zeros((50))
h[i] = sp.bisect(BeamHeight, hb, 5,xtol = 0.001)
Causing this error:
Traceback (most recent call last):
File "ShearMoment.py", line 63, in <module>
h[i] = sp.bisect(BeamHeight, hb, 5,xtol = 0.001)
File "/usr/lib/python2.7/dist-packages/scipy/optimize/zeros.py", line 248, in bisect
r = _zeros._bisect(f,a,b,xtol,rtol,maxiter,args,full_output,disp)
File "ShearMoment.py", line 58, in BeamHeight
x = 1000e3*M[i]*h/(fw*h^3-(fw-wt)(h-2*ft)^3) - Max_stress_steel
TypeError: 'float' object is not callable
I understand that scipy.optimize expects a function as one of its arguments. Am I doing this incorrectly?
In Python, concatenation is not implicitly multiplication, and ^ is not exponentiation. Multiplication must be made explicit with *, and exponentiation must be written as **. This part of BeamHeight:
fw*h^3-(fw-wt)(h-2*ft)^3
must be written as
fw*h**3-(fw-wt)*(h-2*ft)**3

Categories

Resources