Cannot sample values of a trigonometric function - python

I have a problem with my code.
So i try to represent the sampled values of a function 'sin(t^3)/2^tan(t)' for
t between 0 and 1.5 and frequency fs=50Hz.
I have created a function 'sampleFunction' which takes as parameters the string which represents the trigonometric function,beginning of the interval,end of interval and the frequency.
I create tVector(0,0.02,0.04,..,1.48)
Then I take the elements of tVector and use them to evaluate the string and put the result in another vector y
I return both y and tVector
But I encounter a problem when i run it saying 'y' is not defined
This is the code:
import numpy as np
import matplotlib.pyplot as plt
import math
def sampleFunction(functionString,t0,t1,fs):
tVector=np.arange(start=t0, stop=t1, step=1/fs, dtype='float')
t=t0
for i in range(0,len(tVector)):
t=tVector[i]
y[i]=eval(functionString)
return y,tVector
t0=0
t1 =1.5
fs=50
thold=.1
functionString='math.sin(t**3)/2**math.tan(t)'
y,t=sampleFunction(functionString,t0,t1,fs)
plt.plot(t,y)
plt.xlabel('time')
plt.ylabel('Amplitude')

You can change your code in the following way:
def sampleFunction(functionString,t0,t1,fs):
tVector=np.arange(start=t0, stop=t1, step=1/fs, dtype='float')
t=t0
y = np.zeros( tVector.shape )
for i in range(0,len(tVector)):
t=tVector[i]
y[i]=eval(functionString)
return y,tVector
However, this is not good python. There are a couple of issues:
You should use vectorized operations.
You should avoid eval like the plague. This has security implications.
For vectorized operations, simply do:
def sampleFunction(functionString,t0,t1,fs):
t = np.arange(start=t0, stop=t1, step=1/fs, dtype='float')
y = eval(functionString)
return y, t
and call it as:
sampleFunction('np.sin(t**3)/2**np.tan(t)', 0, 10, 100)
This is much faster (especially for large arrays)
Finally, the vectorized form is only a single line long. You probably don't need the extra function.

You have a problem with the allocation of the 'y' variable as Harold is saying.
However, there are multiple ways of achieving what you are doing and the eval function is, unless you have a very good reason, the absolute worst. Maybe consider one of the possible examples below:
import numpy as np
import matplotlib.pyplot as plt
import math
def sampleFunction(functionString,t0,t1,fs):
tVector=np.arange(start=t0, stop=t1, step=1/fs, dtype='float')
t=t0
y = [float]*len(tVector) # <------------------- Allocate 'y' variable
for i in range(0,len(tVector)):
t = tVector[i]
y[i]=eval(functionString)
return y,tVector
t0=0
t1 =1.5
fs=50
thold=.1
# Your code
functionString = 'math.sin(t**3)/2**math.tan(t)'
y, t = sampleFunction(functionString,t0,t1,fs)
plt.plot(t, y, color='cyan')
# Using the 'map' built-in function
t = np.arange(start=t0, stop=t1, step=1./fs, dtype='float')
y = map(lambda ti: 0.9*math.sin(ti**3)/2**math.tan(ti), t)
plt.plot(t, y, color='magenta')
# Using Numpy's 'sin' and 'tan'
t = np.arange(start=t0, stop=t1, step=1./fs, dtype='float')
y = 0.8*np.sin(t**3)/2**np.tan(t)
plt.plot(t, y, color='darkorange')
# Using 'list comprehensions'
t = np.arange(start=t0, stop=t1, step=1./fs, dtype='float')
y = [ 0.7*math.sin(ti**3)/2**math.tan(ti) for ti in t]
plt.plot(t, y, color='darkgreen')
plt.xlabel('time')
plt.ylabel('Amplitude')
plt.show()
The result is:

When running the above code, you should have gotten an error message saying, in the end, "name 'y' is not defined". If you look at your function definition, you will see that it really isn't. You cannot passing a value to y[i] without defining y first! The following line before the "for" loop fixes that particular problem:
y = [None] * len(tVector)
The code will run fine after that correction.
But: why do you want to pass a function string when you can pass a function? Functions, in Python, are first-class-objects!

Related

SciPy: Why is the input of 2D interpolation sorted internally?

I hae a problem with scipy.interpolate.interp2d.
For sorted input, the interpolation is OK.
When I ask to get the interpolation values for an unsorted array, I get output as if it is sorted internally by SciPy. Why is that?
The way around is to get interpolation values in a loop.
Here is my demonstration code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
SciPy interp2d test.
Why is the input of 2D interpolation sorted internally?
'''
import matplotlib.pyplot as plt
import scipy.interpolate as itp
import numpy as np
def fMain():
nx=11
ny=21
ax=np.linspace(0,1,nx)
mx=np.empty((nx,ny))
for i in range(ny):
mx[:,i] = ax
pass
ay=np.linspace(0,1,ny)
my=np.empty((nx,ny))
for i in range(nx): # can I do this without loop?
my[i,:] = ay
pass
mz=np.empty((nx,ny))
mz=mx**2 + my**3
f2Di = itp.interp2d( mx, my, mz, kind='linear')
#this provides identical results, ok
#f2Di = itp.interp2d( ax, ay, mz.transpose(), kind='linear')
if True :
# just to check the interpolation
mzi = f2Di(ax,ay)
fig = plt.figure()
axis = fig.add_subplot(projection='3d')
axis.plot_wireframe( mx, my, mz )
axis.scatter(mx, my, mzi.transpose(), marker="o",color="red")
axis.set_xlabel("x")
axis.set_ylabel("y")
axis.set_zlabel("z")
plt.tight_layout()
plt.show()
plt.close()
pass
if True:
y = 0.5
az = f2Di( ax , y )
axf=np.flip(ax)
azf1 = f2Di( axf , y )
azf2 = np.empty(nx)
for i in range(nx):
azf2[i] = f2Di( axf[i] , y )
pass
plt.plot(ax,az,label="Normal",linewidth=3,linestyle="dashed")
plt.plot(axf,azf1,label="Reversed")
plt.plot(axf,azf2,label="Reversed loop")
plt.legend()
plt.xlabel("x")
plt.ylabel("z")
plt.tight_layout()
plt.show()
plt.close()
pass
pass
if __name__ == "__main__":
fMain()
pass
To answer another question (from a comment in the code):
ax=np.linspace(0,1,nx)
mx=np.empty((nx,ny))
for i in range(ny):
mx[:,i] = ax
(and similar for ay).
Can I do this with a loop?
Yes (technically no, since there'll be a C loop under the hood, but practially, yes). Use numpy.tile:
ax = np.linspace(0, 1, nx)
mx = np.tile(ax, (ny, 1)).T
And the np.empty doesn't make sense below: you allocate memory, but immediately (re)assign the variable to another value:
#mz = np.empty((nx, ny)) # This line is redundant
mz = mx**2 + my**3
This is also why np.empty has disappeared form the for-replacement code.
There's a builtin function for make grids like your mx,my:
In [68]: I,J = np.meshgrid(ay,ax)
In [69]: I.shape
Out[69]: (11, 21)
In [71]: np.allclose(I,my)
Out[71]: True
In [72]: np.allclose(J,mx)
Out[72]: True
alternatively you could have assigned the values with broadcasting
In [76]: my = np.empty((nx,ny)); my[:]=ay
In [78]: mx = np.empty((nx,ny)); mx[:]=ax[:,None]
The interp2d docs say that input arrays are flattened, even if input as 2d. And that the x,y can be the coordinates as in ax,ay; they don't have to be constructed from the full grid. So the 2 ways of setting up the f2Di are equivalent.
Full documentation for the use of f2Di(x,y) is
https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp2d.__call__.html#scipy.interpolate.interp2d.__call__
It explicitly states that the inputs, x,y have to sorted, or it will do it for you.
One interpolation:
In [86]: mzi = f2Di(ax,ay)
In [87]: mzi.shape
Out[87]: (21, 11)
Another with the inputs reversed:
In [89]: azf1 = f2Di(ax[::-1], ay[::-1] )
In [90]: azf1.shape
Out[90]: (21, 11)
In [91]: np.allclose(mzi, azf1)
Out[91]: True
As you note, and attempt to show with a lot of plotting code, the results are the same - inputs have been sorted before they are used to interpolate.
If I falsely tell it that the coordinates are sorted:
In [94]: azf1 = f2Di(ax[::-1], ay[::-1] , assume_sorted=True)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
File ~\anaconda3\lib\site-packages\scipy\interpolate\_fitpack_impl.py:1054, in bisplev(x, y, tck, dx, dy)
1052 z, ier = _fitpack._bispev(tx, ty, c, kx, ky, x, y, dx, dy)
1053 if ier == 10:
-> 1054 raise ValueError("Invalid input data")
1055 if ier:
1056 raise TypeError("An error occurred")
ValueError: Invalid input data
Note that the error was raised by a function in _fitpack. The name implies that this using some sort of compiled interpolation code, a library that is probably written in C or Fortran. I'm not a developer, but I can imagine that it's easiest to write such code assuming that the inputs are sorted. Such shared libraries work best when they have clear, and relatively simple, expectations regarding the inputs.

'ImmutableDenseNDimArray' - Can't get equation to plot

I'm trying to plot an equation, but I can't seem to do it because of the error mentioned above.
This is a snippet of the code, should work by itself:
import numpy as np
from numpy import linspace
from sympy import *
import matplotlib.pyplot as plt
x , t = symbols('x t')
test = simplify(-(0.02631027*sin(3*x)+0.1594*cos(3*x))*exp(-1*x))
test_f = lambdify([x], test, modules="sympy")
# Creating vectors X and Y
x_num = np.linspace(0, 8, 10)
# Plot size
fig = plt.figure(figsize = (16, 7))
# Create the plot
plt.plot(x_num, test_f(x_num)) ← Error at this line!
plt.grid(alpha=.4,linestyle='--')
plt.legend()
plt.show()
At the line mentioned above, I get that error.
What should I do differently?
You are mixing numpy with sympy, and that is what is causing the issue.
Let's analyze the code. Here you create a symbolic expression:
test = simplify(-(0.02631027*sin(3*x)+0.1594*cos(3*x))*exp(-1*x))
Here you are creating a lambda function asking it to be evaluated by sympy. This means the function will accept symbolic values and returns symbolic values:
test_f = lambdify([x], test, modules="sympy")
Then you create a Numpy array and pass it to the function, but it will raise the error you mentioned. That's because the function is expecting symbolic values, or values that can be converted to symbolic values. Turns out that a Numpy array cannot be converted to any symbolic value!
x_num = np.linspace(0, 8, 10)
test_f(x_num) # ERROR
What you need to do is to create a lambda function asking it to be evaluated by Numpy, like this:
test_f = lambdify([x], test)
The full correct code:
x , t = symbols('x t')
test = simplify(-(0.02631027*sin(3*x)+0.1594*cos(3*x))*exp(-1*x))
test_f = lambdify([x], test)
# Creating vectors X and Y
x_num = np.linspace(0, 8, 10)
# Plot size
fig = plt.figure(figsize = (16, 7))
# Create the plot
plt.plot(x_num, test_f(x_num))
plt.grid(alpha=.4,linestyle='--')
plt.legend()
plt.show()
Your test_f produces:
In [113]: test_f(1)
Out[113]:
-1
(-0.02631027⋅sin(3) - 0.1594⋅cos(3))⋅ℯ
In [114]: test_f(y)
Out[114]:
-y
(-0.02631027⋅sin(3⋅y) - 0.1594⋅cos(3⋅y))⋅ℯ
In other words, it just a way of generating a sympy expression.

Are optimisation builtin functions of Matlab better than Python?

all. I encountered a case where minimisation results of Matlab are very close to mathematical solution(i.e., when we solve equations by hand) when compared to the results obtained from Python's scipy minimize builtin function. I'm not sure where i'm doing wrong or how to improve the results in python. Any suggestion would be of great help.
Aim of this problem is to find the time period of set nonlinear differential equations without time evolving. For of test case I took the problem from "This place".
Non-Linear Differential equations looks like this
Here i'm implementing pseudo spectral method for periodic systems. Implementation method is similar to what described here , only change is i'm taking uniform points and "D" matrix is formed using Pseudospectral.
import numpy as np
from numpy import linalg as LA
import ast
from ast import literal_eval as make_tuple
import scipy
from scipy.optimize import minimize
from scipy.linalg import toeplitz
import matplotlib.pyplot as plt
%matplotlib tk
# This "Dmatrix" is used to get derivative.
def Dmatrix(N):
h = 2.0*np.pi/N;
col = np.zeros(N);
col[1:] = 0.5*(-1.0)**np.arange(1,N)/np.sin(np.arange(1,N)*h/2.0);
row = np.zeros(N); row[0] = col[0]; row[1:] = col[N-1:0:-1]
D = toeplitz(col,row);
return D
# Actual differential equations.
def dxD(x,y,t):
u=(1-(x**2)/4 - (y**2));
dx=-4*y+x*u;
dy=x+y*u;
return np.array([dx,dy])
# Implementing Pseudo spectral method
def dxFdxD(initial_guess,final_time):
N=len(initial_guess)//2;
x_guess=initial_guess[:N];
xl=np.array(x_guess[:]);
y_guess=initial_guess[N:2*N];
yl=np.array(y_guess[:]);
tf=final_time;
tl=np.arange(1,N+1)*tf/N;
D=Dmatrix(N);
XYTzipped=zip(xl,yl,tl);
dX_D=np.array([dxDynamics(xs,ys,ts) for xs,ys,ts in XYTzipped ]);
xlyl=np.array([xl,yl]).transpose();
dX_F=(np.array(D#xlyl))*(2*np.pi/tf);
err=np.array(dX_D - dX_F).flatten();
normError= LA.norm(err, 2);
return normError
# Initial guess points
N=201;
final_time=1.052*np.pi;
tf=final_time;
tgrid=np.arange(1,N+1)*tf/N;
xguess=np.cos(tgrid)*2.0;
yguess=-np.cos(tgrid)*0.5;
tfl=np.pi*0.85;
tfu=1.5*np.pi;
tfbounds=(tfl,tfu);
xstates= np.array([xguess,yguess]).flatten();
xstatesParameter=np.array([xstates,final_time], dtype=object);
xins=np.hstack(xstatesParameter).tolist();
# Objective function for optimising
def obj(x):
N=(len(x)-1)//2;
tf=x[-1];
xylist=x[:2*N];
return dxFdxD(xylist,tf)
# Optimization using method='trust-constr'
l1=[tfbounds];
str1=str([bounds123 for bounds123 in l1]);
str2=str1.replace("[", "").replace("]", "")
bounds1=make_tuple("("+ "(-5,5),(-5,5),"*N + str2+ ")")
bnds=bounds1;
# constraint
def xyradius(x):
nps=(len(x)-1)//2;
xs=x[:nps];
ys=x[nps:2*nps];
xsysZip=zip(xs,ys)
truelist=[bool((xi**2)+(yi**2)>0.25) for xi,yi in xsysZip]
result=int(all(truelist))
return result
xyradiusConstraintType={'type':'ineq','fun':xyradius};
cons=[xyradiusConstraintType]
# Minimising "obj"
sol=minimize(obj,
xins,
method='trust-constr',
bounds=bnds,
tol=1e-10)
# Results
x_y_tf=sol.x;
x_F=x_y_tf[:N];
y_F=x_y_tf[N:2*N];
tf_system=x_y_tf[-1];
print("time period tf=",tf_system,end="\n \n")
tgrid=np.arange(1,N+1)*tf/N;
# Plots
fig = plt.figure(1)
ax = fig.add_subplot(111)
#specify label for the corresponding curve
# ax.set_xticks(tgrid, minor=False)
ax.set_xticks(tgrid, minor=True)
ax.xaxis.grid(True, which='major')
ax.xaxis.grid(True, which='minor')
ax.set_title('Collocation points')
plt.plot(tgrid,x_F,label='x result')
plt.plot(tgrid,y_F,label='y result')
ax.set_title('Optimized result x,y')
plt.legend()
plt.show()
# Parametric plot
ax = plt.figure(4).add_subplot()
ax.plot(x_F,y_F,label='State Space')
ax.legend()
plt.show()
Optimizing(Minimizing) using method='SLSQP'
# Scipy for minimization using method='SLSQP'
l1=[tfbounds];
str1=str([bounds123 for bounds123 in l1]);
str2=str1.replace("[", "").replace("]", "")
bounds1=make_tuple("("+ "(-5,5),(-5,5),"*N + str2+ ")")
bnds=bounds1;
def xyradius(x):
nps=(len(x)-1)//2;
xs=x[:nps];
ys=x[nps:2*nps];
xsysZip=zip(xs,ys)
truelist=[bool((xi**2)+(yi**2)>0.25) for xi,yi in xsysZip]
result=int(all(truelist))
return result
xyradiusConstraintType={'type':'ineq','fun':xyradius};
cons=[xyradiusConstraintType]
sol=minimize(obj,
xins,
method='SLSQP',
bounds=bnds,
constraints=cons,
tol=1e-10)
When I implemented the same work in MatLab . I got "pi= 3.14" as the solution(time period of system), where as when in python i'm getting "4.70" as time period. Any suggestions are greatly appreciated. Thank you

Number format python

I want to have the legend of the plot shown with the value in a list. But what I get is the element index but not the value itself. I dont know how to fix it. I'm referring to the plt.plot line. Thanks for the help.
import matplotlib.pyplot as plt
import numpy as np
x = np.random.random(1000)
y = np.random.random(1000)
n = len(x)
d_ij = []
for i in range(n):
for j in range(i+1,n):
a = np.sqrt((x[i]-x[j])**2+(y[i]-y[j])**2)
d_ij.append(a)
epsilon = np.linspace(0.01,1,num=10)
sigma = np.linspace(0.01,1,num=10)
def lj_pot(epsi,sig,d):
result = []
for i in range(len(d)):
a = 4*epsi*((sig/d[i])**12-(sig/d[i])**6)
result.append(a)
return result
for i in range(len(epsilon)):
for j in range(len(sigma)):
a = epsilon[i]
b = sigma[j]
plt.cla()
plt.ylim([-1.5, 1.5])
plt.xlim([0, 2])
plt.plot(sorted(d_ij),lj_pot(epsilon[i],sigma[j],sorted(d_ij)),label = 'epsilon = %d, sigma =%d' %(a,b))
plt.legend()
plt.savefig("epsilon_%d_sigma_%d.png" % (i,j))
plt.show()
Your code is a bit unpythonic, so I tried to clean it up to the best of my knowledge. numpy.random.random and numpy.random.uniform(0, 1) are basically the same, however, the latter also allows you to pass the shape of the return array that you would like to have, in this case an array with 1000 rows and two columns (1000, 2). I then use some magic to assign the two colums of the return array to x and y in the same line, respectively.
numpy.hypot does as the name suggests and calculates the hypothenuse of x and y. It can also do that for each entry of arrays with the same size, saving you the for loops, which you should try to aviod in Python since they are pretty slow.
You used plt for all your plotting, which is fine as long as you only have one figure, but I would recommend to be as explicit as possible, according to one of Python's key notions:
explicit is better than implicit.
I recommend you read through this guide, in particular the section called 'Stateful Versus Stateless Approaches'. I changed your commands accordingly.
It is also very unpythonic to loop over items of a list using the index of the item in the list like you did (for i in range(len(list)): item = list[i]). You can just reference the item directly (for item in list:).
Lastly I changed your formatted strings to the more convenient f-strings. Have a read here.
import matplotlib.pyplot as plt
import numpy as np
def pot(epsi, sig, d):
result = 4*epsi*((sig/d)**12 - (sig/d)**6)
return result
# I am not sure why you would create the independent variable this way,
# maybe you are simulating something. In that case, the code below is
# simpler than your version and should achieve the same.
# x, y = zip(*np.random.uniform(0, 1, (1000, 2)))
# d = np.array(sorted(np.hypot(x, y)))
# If you only want to plot your pot function then creating the value range
# like this is just fine.
d = np.linspace(0.001, 1, 1000)
epsilons = sigmas = np.linspace(0.01, 1, num=10)
fig, ax = plt.subplots()
ax.set_xlim([0, 2])
ax.set_ylim([-1.5, 1.5])
line = None
for epsilon in epsilons:
for sigma in sigmas:
if line is None:
line = ax.plot(
d, pot(epsilon, sigma, d),
label=f'epsilon = {epsilon}, sigma = {sigma}'
)[0]
fig.legend()
else:
line.set_data(d, pot(epsilon, sigma, d))
# plt.savefig(f"epsilon_{epsilon}_sigma_{sigma}.png")
fig.show()

create a function for chi_square

I need to input chi_square function and got stuck because it always shows there is a invalid syntax when run it, wonder how should I write the script? And how do I input "v"?
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
data = np.loadtxt("214 ohm.txt", skiprows=1)
xdata = [row[0] for row in data ]#x represents current unit is "V"
ydata = [row[1] for row in data]#y represents voltage unit is "mA"
percision_error_V = np.array(xdata) * 0.0025 #we are using last digit of reading and multiply by measured voltage
accuracy_error_V = 0.01#we are using DC Vlotage, so use the error it provided online
erry = []
for i in range(len(percision_error_V)):
#to compare percision_error and accuracy_error for Voltage and use the larger one
erry.append(max(percision_error_V[i], accuracy_error_V))
def model_function (x, a, b):
return a*x + b
p0 = [0 , 0.]#214ohm is measured by ohmeter
p_opt , p_cov = curve_fit ( model_function ,
xdata , ydata , p0,
erry , True )
print(erry)
a_opt = p_opt[0]
b_opt = p_opt[1]
print(p_cov)
print("diagonal of P-cov is",np.diag(p_cov))
print("a_opt, b_opt is ",a_opt, b_opt)
xhat = np.arange(0, 16, 0.1)
plt.plot(xhat, model_function(xhat, a_opt, b_opt), 'r-', label="model function")
plt.errorbar(xdata, ydata,np.array(erry),linestyle="",marker='s', label="error bar")
plt.legend()
plt.ylabel('Current (mA)')
plt.xlabel('Voltage(V)')
plt.title("Voltage vs. Current with 220ohm Resistor")
plt.show()
p_sigma = np.sqrt(np.diag(p_cov))
print("p_sigma is" ,p_sigma)
for i in range(len(xdata)):
sum=sum((ydata[i]-model_function(xdata[i], a_opt, b_opt))
chi.append(sum)
this is the required function I'm supposed to put on python
Thanks
my code is alright until the equation of chi-square, I wonder how should I fix it?
You have an indentation, 1 missing parenthesis, and variable naming issues so far in this sample of code
FROM
for i in range(len(xdata)):
sum=sum((ydata[i]-model_function(xdata[i], a_opt, b_opt))
1.append(sum)
TO
for i in range(len(xdata)):
sum=sum((ydata[i]-model_function(xdata[i], a_opt, b_opt)) )
a.append(sum)
Variables cannot be named with numbers. E.g. 1,2,3. They must start with string - a1, alfa, betta, or s_t, _s.

Categories