how to plot 2d condition function - python

I want to plot a 2d condition function, and the code as below:
from numpy import exp,arange
from pylab import meshgrid,cm,imshow,show
def z_func(x,y):
zParity = x % 2
nParity = y % 2
if zParity == 0 and nParity == 0:
return x+y+1
elif zParity!=0 and nParity!=0:
return x+y-1
else:
return x+y
x = arange(1,100,1)
y = arange(1,100,1)
X,Y = meshgrid(x, y) # grid of point
Z = z_func(X, Y) # evaluation of the function on the grid
im = imshow(Z,cmap=cm.RdBu) # drawing the function
show()
I get an error: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
How can I change the code to work?

Try this:
from numpy import exp,arange
from pylab import meshgrid,cm,imshow,show
import numpy as np
def z_func(x,y):
zParity = x % 2
nParity = y % 2
if np.all((zParity == 0, nParity == 0)):
return x+y+1
elif np.all((zParity!=0, nParity!=0)):
return x+y-1
else:
return x+y
x = arange(1,100,1)
y = arange(1,100,1)
X,Y = meshgrid(x, y) # grid of point
Z = z_func(X, Y) # evaluation of the function on the grid
im = imshow(Z,cmap=cm.RdBu) # drawing the function
show()
This is what I get after running the code (I hope I have understood what you want to get, otherwise I will edit my answer accordingly):

Related

Plotting piecewise functions using Matplotlib

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
The above error is what I got for the below code intended to plot the piecewise function created. Can't figure it out.
import numpy as np
import matplotlib.pyplot as plt
def f(x):
if x>=0 and x<=1:
return x
elif x>1 and x<=2:
return 2-x
else:
return 0
xlist = np.linspace(0,1,num = 1000)
ylist = f(xlist)
plt.plot(ylist,xlist)
plt.show()
As per comments, you need to vectorize your method f (and also fix some mistakes):
import numpy as np
import matplotlib.pyplot as plt
def f(x):
y = np.empty_like(x)
mask1 = (0 <= x) & (x <= 1)
mask2 = (1 < x) & (x <= 2)
mask3 = np.logical_not((0 <= x) & (x <= 2)) #or ~((0 <= x) & (x <= 2))
y[mask1] = x[mask1]
y[mask2] = 2 - x[mask2]
y[mask3] = 0
return y
xlist = np.linspace(-1, 3, num = 1000)
ylist = f(xlist)
plt.plot(xlist, ylist)
plt.show()
You might also find that mask3 is not necessarry and vectorize a method f like this:
def f(x):
y = np.full_like(x, fill_value=0) #or np.zeros_like(x)
mask1 = (0 <= x) & (x <= 1)
mask2 = (1 < x) & (x <= 2)
y[mask1] = x[mask1]
y[mask2] = 2 - x[mask2]
return y
What I understood is , you are basically comparing an array with a number which should not be the case here, the code provided might help you with solution,
import numpy as np
import matplotlib.pyplot as plt
def f(x):
if x>=0 and x<=1:
return x
elif x>1 and x<=2:
return 2-x
else:
return 0
xlist = np.linspace(0,1,num = 1000,endpoint=False)
ylist = np.array([f(num) for num in xlist])
plt.plot(xlist,ylist)
plt.show()
You can use the function you have already defined and vectorize is using np.vectorize.
import numpy as np
import matplotlib.pyplot as plt
def f(x):
if x>=0 and x<=1:
return x
elif x>1 and x<=2:
return 2-x
else:
return 0. # make this change to ensure float values
xlist = np.linspace(0,1,num = 1000)
ylist = np.vectorize(f)(xlist)
plt.plot(ylist,xlist)
plt.show()
The issue you are running into in your code is that you are passing an array to f rather than applying f element-wise, so any comparison gives an array of True or False values; this is what is ambiguous in the error message. Using np.vectorize will change the function defined to apply to each element of the array and give you the desired output.

Test whole range of possible inputs for a given question

Is there a quick way to find the maximum value (float) from a function and the corresponding arguments x, y that are both integers between 0 and 100 (inclusive)? Do I need to use the assert function or something like that to get the range of all possible inputs?
def fun_A(x,y):
import math
if x == y:
return 0
first = math.cos((y%75)*(math.pi/180))
second = math.sin((x%30)*(math.pi/180))
return (first + second) / (abs(x - y))
For small problems like this it is probably fast enough to evaluate every possible combination and choose the maximum. The numpy library makes this easy to write and pretty fast as well:
import numpy as np
def fun_A(x, y):
first = np.cos((y%75)*(np.pi/180))
second = np.sin((x%30)*(np.pi/180))
return np.where(x == y, 0, (first + second) / (abs(x - y)))
x, y = np.mgrid[0:101, 0:101]
f = fun_A(x, y)
maxindex = np.argmax(f)
print('Max =', f.flat[maxindex], ' at x =', x.flat[maxindex], 'y =', y.flat[maxindex])
Output:
Max = 1.4591796850315724 at x = 89 y = 88
Things to note:
I've just replaced calls to math with calls to np.
x and y are matrices which allow us to evaluate every possible combination the two values in one function call.
I would do this for the tan function :
from math import tan
y = 0
x = 0
for x_iteration in range(0, 101):
if tan(x_iteration) > y :
x = x_iteration
y = tan(x_iteration)
x = int(x)
y = int(y)
It's fairly straightforward to write a program to solve this:
max_result = None
max_x = 0
max_y = 0
for x in range(0, 101):
for y in range(0, 101):
result = fun_A(x, y)
if max_result is None or result > max_result:
max_result = result
max_x = x
max_y = y
print(f"x={max_x} and y={max_y} produced the maximum result of {max_result}")

Plotting Specific Regions

I am new to python. The problem is that, assume that we have two parameters, x and y, and four functions f_1, f_2, f_3 and f_4. Suppose that we know that:
If (x < 5 < y < 5+x) or (5 <= y < x) or (x= 5 and 5 < y < 10) then function f_1 is the maximum function.
If (5 < x < y < 5 + x) or (x <= y < 5) then function f_2 is the maximum function.
If (y < x < 5) or (y < 5 < x) or ( x = 5 and y < x) then function f_3 is the maximum function.
If y > x+5 then function f_4 is the maximum function.
I need to draw a plot with x-axis = x and y-axis = y which shows the regions under which each function is the maximum function.
I used the following code, however the resulted plot, shown below, is not accurate.
import numpy as np
from matplotlib import pyplot as plt
x = np.arange(0,10,.1)
y = np.arange(0,15,.2)
x,y = np.meshgrid(x,y)
maxf = np.zeros(shape = x)
maxf.fill(-9999.99)
for i in range(len(x)):
for j in range(len(y)):
if j<i<5 or j<5<i:
maxf[i,j] =1
elif i<5<=j<i+5 or 5<=j<i:
maxf[i,j] =2
elif 5<i<=j<i+5 or i<=j<5:
maxf[i,j] =3
elif i == 5 and j<5:
maxf[i,j]=1
elif i == 5 and 5<=j<10:
maxf[i,j]=2
elif j >= 5+i:
maxf[i,j]=4
plt.contourf(x,y,maxf)
plt.colorbar()
plt.show()
The result should have been sth like the following picture:
When you set the initial array to -9999.99 you now have to make sure you only contour the values that you want which is between 1-3. Since that value is so much bigger in magnitude it does not get included in your plot. Set your contour levels for your plot using this:
plt.contourf(x,y,maxf,[0,1,2,3])
Yields:
Update
I didn't notice before but you are using i,j like they are the numbers but they actually represent the indexes of the arrays which is throwing off your calculation. You need to know the index and the value so you can use enumerate. If this is still not correct, then you need to revisit your logic in your conditions.
import numpy as np
from matplotlib import pyplot as plt
y = np.arange(0,15,.01)
x = np.arange(0,10,.01)
Y,X = np.meshgrid(y,x)
maxf = np.zeros(shape = Y.shape)
maxf.fill(-9999.99)
for i,x_ in enumerate(x):
for j, y_ in enumerate(y):
if y_<x_<5 or y_<5<x_:
maxf[i,j] =3
elif x_<5<=y_<(x_+5) or 5<=y_<x_:
maxf[i,j] =1
elif 5<x_<=y_<(x_+5) or x_<=y_<5:
maxf[i,j] =2
elif x_ == 5 and y_<5:
maxf[i,j]=3
elif x_ == 5 and y_>=5:
maxf[i,j]=1
elif y_ >= (5+x_):
maxf[i,j]=4
plt.contourf(X,Y,maxf,[0,1,2,3,4])
plt.colorbar()
plt.show()
Final Note
Just because you add a condition does not mean it will get evaluated if another condition is met first. In this case your 4th function is never true because one of the other conditions is always met. If you want that condition first, then make it your first if statement. How you arrange your logical statements matters especially since you have lots of conditions and some of which overlap each other.

Plot a piecewise function in 3D

I am new to python and trying to 3d plot a piecewise function. I am trying to 3d plot the 'mainformula' function below on the z-axis as it varies with x and y ranging from 0 to 10, and constant = 1. But I can't quite seem to figure out the plotting method here.
from sympy import *
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
def mainformula(x,y,constant):
return Piecewise((subformula1(x,y,constant), y >= 0 and y < 3),(subformula2(x,y,constant),y>=3 and y <= 10))
def subformula1(x,y,constant):
return x + y + constant
def subformula2(x,y,constant):
return x - y - constant
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 10, 0.25)
Y = np.arange(0, 10, 0.25)
constant = 1
X, Y = np.meshgrid(X, Y)
Z = mainformula(X,Y,constant)
surf = ax.plot_surface(X, Y, Z)
plt.show()
The error I get when I run that code is: "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
You are working on arrays so it's never going to work to use array > 3 in a boolean context (for example with and) this will always give you the error you received. But you can always define your conditions as boolean masks and operate your formula on the appropriate elements:
def mainformula(x,y,constant):
z = np.zeros_like(x)
# Condition 1 indexes all elements where subformula 1 is valid
condition1 = np.logical_and(y >= 0, y < 3)
# condition1 = (y >= 0) & (y < 3) # is another way of writing it
z[condition1] = x[condition1] + y[condition1] + constant
# now do it in the range where subformula 2 is valid
condition2 = np.logical_and(y >= 3, y <= 10)
# condition1 = (y >= 3) & (y <= 10) # is another way of writing it
z[condition2] = x[condition2] - y[condition2] - constant
return z
This doesn't use sympy.Piecewise but works alright when only trying to plot. If you want seperate functions instead of doing it all in the main formula you need to change it a bit:
z[condition1] = subformula1(x[condition1], y[condition1], constant)
and similar for condition2.
The problem is you are trying to make logic assertions into arrays that do not have a direct one. It's difficult for Python to assert what this is:
y >= 0 and y < 3
So you'll have to change somewhat your code into something it can understand:
def mainformula(x,y,constant):
y2 = y[y>=0]
y2 = y2[y2<3]
y3 = y[y>=3]
y3 = y2[y2<=10]
return Piecewise((subformula1(x,y,constant), y2),(subformula2(x,y,constant),y3))
The problem is Piecewise function also does not seem to accept some arrays into one of the arguments you have. You'll have to rethink your problem, perhaps by building a loop for the Piecewise function in order to be launched at every element.

Random walk in python

I am trying to implement a random walk in python. This is the error I get. I feel my implementation is wrong or at least not the best. Can someone have a look at it. Keep in mind I am a beginner in python and this is how I think someone would code something, so I can be totally off.
in randomWalk(stepSize, stepNumber)
37 for _ in range(stepNumber):
38 r = randint(1,4)
---> 39 x,y = movement[r]
40 xList.append(x)
41 yList.append(y)
TypeError: 'function' object is not iterable
This is my code
from pylab import *
import numpy as np
import matplotlib.pyplot as plt
import random as rnd
matplotlib.rcParams.update({'font.size': 20})
x = 0.
y = 0.
xList = []
yList = []
def goRight(stepSize, y):
direction = np.cos(0)
x = stepSize*direction
return [x,y]
def goUp(stepSize, x):
direction = np.cos(90)
y = stepSize*direction
return [x,y]
def goLeft(stepSize, y):
direction = np.cos(180)
x = stepSize*direction
return [x,y]
def goDown(stepSize, x):
direction = np.cos(270)
y = stepSize*direction
return [x,y]
def randomWalk(stepSize, stepNumber):
movement = {1: goRight,
2: goUp,
3: goLeft,
4: goDown}
for _ in range(stepNumber):
r = randint(1,4)
x,y = movement[r]
xList.append(x)
yList.append(y)
plt.ioff()
plot(x, y)
plt.show()
randomWalk(1.,4)
You are putting functions in your dict movement. movement[r] is not calling the function, only accessing them. What you line is basically doing, is:
x, y = goDown
If you want to call the function in that line, you have to add parentheses and arguments, something like:
x, y = movement[r](stepSize, x)
Which shows that you have a problem in your design, since some functions expect x and some expect y. You could maybe fix that by having all the functions take both coordinates, x and y, and then your line should go like
x, y = movement[r](stepSize, x, y)
The problem is with the line
x,y = movement[r]
The dictionary movement is a list of ints to functions when you call movement[r] only a single function is returned, but here you are trying to unpack it. Instead I think you want:
x,y = movement[r](stepSize)
This will call your function and so return the coords you want.
Also you need to change all your step methods to only take the one parameter...
You can not call a function in a dictionary of fucntions like this , use this instead :
functionToCall = movement[r] # look up for function in dictionary
x,y = functionToCall(stepsize , x, y) # then call function with desired parameters

Categories