Plotting piecewise functions using Matplotlib - python

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.

Related

how to plot 2d condition function

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):

Plotting a piecewise function in python with numpy

Note: This is for homework so please don't post full code responses, just help on what I'm misusing would be appreciated
I'm trying to plot a piecewise defined function where when 0 < x <= 10 it will be a constant (KQ/10) and for x > 10 it will be KQ/x for 10 < x < 50. Currently my result comes back as a single value instead of my expected result of an array with a constant value up until x > 10 and then varying values until x = 50
My current code
import matplotlib.pyplot as plt
import scipy
x = np.linspace(0, 50, 1)
R = 10
r = np.linspace(10, 50, 1)
k = 1/(4*np.pi*constants.epsilon_0)
Q = 1
def inside(x):
return k*Q/R
def outer(x):
return k*Q/x
result = np.piecewise(
x,
[x <= R, x > R ],
[lambda x: inside(x), lambda x: outer(x)]
)
result
#plt.plot(x,result)
#plt.axis([0, 50,0, 500000])
#plt.xlabel('x')
#plt.ylabel('y')```
x = np.linspace(0, 50, 1)
isnt how linspace works... this only creates one data point ...
x = np.linspace(0, 50, 10000) ... would create 10k datapoints
perhaps you wanted np.arange(0,50,1) ?

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.

Categories