I have defined a function I(a,b) = integral f(a,b,t) dt and want to plot it to see how it depend on the variables a and b. I first wrote a program that graphed y = I(k,x) and it worked just fine, but i wanted to see how it depends on both variables so i tried to write a program that graphs it in 3D.
The program worked for simpler functions like trigonometric and polynomials, but when i try to graph I(x,y) it just gives me the error "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
This is the code, I originally wrote my own program to approximate the integral but then used scipy
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
import scipy.integrate as integrate
def integral(x,y):
return integrate.quad(lambda t: np.sqrt((x**2 + y**2 - 2*x*y*np.cos(np.pi*t*(np.sqrt(1/x**3) - np.sqrt(1/y**3))))/(x**3*y**3)), 0, np.sqrt(x**3*y**3))
X = np.arange(0.1,5,0.1)
Y = np.arange(0.1,5,0.1)
X,Y = np.meshgrid(X, Y)
Z = integral(X,Y)
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.plot_wireframe(X, Y, Z, color='green')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='winter', edgecolor='none')
ax.set_title('copper');
plt.show()
'''
scipy.integrate.quad returns a tuple. You only want the first value of that. Also you need to vectorize the function.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
def integral(x,y):
return integrate.quad(lambda t: np.sqrt((x**2 + y**2 - 2*x*y*np.cos(np.pi*t*(np.sqrt(1/x**3) - np.sqrt(1/y**3))))/(x**3*y**3)), 0, np.sqrt(x**3*y**3))[0]
X = np.arange(0.1,5,0.1)
Y = np.arange(0.1,5,0.1)
X,Y = np.meshgrid(X, Y)
Z = np.vectorize(integral)(X,Y)
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.plot_wireframe(X, Y, Z, color='green')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='winter', norm=plt.Normalize(np.nanmin(Z), np.nanmax(Z)), edgecolor='none')
plt.show()
Related
I'm using a newer version of matplotlib and the argument that sets the linewidth was removed. They seem to have changed it so I set it in Collections object, but I can't find a way of doing this.
I tried their example with a different linewidth:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=10, antialiased=False)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
But as the figure shows, it doesn't add lines to my surface.
What is the new method for setting linewidths?
Thanks!
The linewidth can of course only take effect if there is actually a line to be shown. So one would need to specify the color of the lines to show in order to see them.
surf = ax.plot_surface(X, Y, Z, cmap="RdYlGn", linewidth=2, edgecolor="limegreen")
I am trying to produce a 3D surface plot where X and Y are values between -50 and 50, and Z is calculated by a function depending on X and Y.
This function takes a vector as a parameter in the form of an np array. The vector's first row is a value from X and the second a value from Y. All combinations of X and Y should produce a Z value, hence the meshgrid.
Here is my implementation, for Z I am currently creating a vector where the first row is the entire dataset of X, and the second the entire dataset of Y. This is of course incorrect.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
import matplotlib.pyplot as plt
def myFunction(v):
return v.dot(np.array([1, 2]))
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.linspace(-50,50, 100)
Y = np.linspace(-50,50, 100)
X, Y = np.meshgrid(X, Y)
Z = myFunction(np.array([X, Y])) # <-- Here is the problem
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.Greens,
linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
I hope I have made sense,
Thanks
You would probably like to supply an array with all x values in the frst column and all y values in the second column to the function. That would ensure to have the dimensions match for the dot product. The result can then be reshaped to the shape of the mesh.
Z = myFunction(np.array([X.flatten(), Y.flatten()]).T).reshape(X.shape)
Complete example:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
import matplotlib.pyplot as plt
def myFunction(v):
return v.dot(np.array([1, 2]))
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.linspace(-50,50, 100)
Y = np.linspace(-50,50, 100)
X, Y = np.meshgrid(X, Y)
Z = myFunction(np.array([X.flatten(), Y.flatten()]).T).reshape(X.shape)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.Greens,
linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
I use matplotlib to simulate Y^2 + Z^2 = (SinX)^2
That is,the sine graph rotate 360 degrees based on x axis.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
t = np.arange(-5, 5, 0.25)
X,Y = np.meshgrid(t,t)
Z = np.sin(t)**2
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Following is the image
However,that seems not fit my expectation.
Is my way reasonable?
Or is there any way can implement in vpython?
Here is a VPython program that plots a function in 3D, which may be related to what you want to do.
http://www.glowscript.org/#/user/GlowScriptDemos/folder/Examples/program/Plot3D
I am trying to add legend to a surface plot but unable to do so. Here is the code.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import random
def fun(x, y):
return 0.063*x**2 + 0.0628*x*y - 0.15015876*x + 96.1659*y**2 - 74.05284306*y + 14.319143466051
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.plot(color='red',label='Lyapunov function on XY plane',linewidth=4) # Adding legend
plt.show()
Kindly help. Thanks in advance.
It is not trivial to make a legend in a 3D axis. You can use the following hack:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib as mpl
import random
def fun(x, y):
return 0.063*x**2 + 0.0628*x*y - 0.15015876*x + 96.1659*y**2 - 74.05284306*y + 14.319143466051
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fake2Dline = mpl.lines.Line2D([0],[0], linestyle="none", c='b', marker = 'o')
ax.legend([fake2Dline], ['Lyapunov function on XY plane'], numpoints = 1)
plt.show()
I would say a title is more appropriate than a legend in this case.
According to this question, the issue is ongoing, and there is a relatively simple workaround. You can manually set the two missing attributes that would allow legend to automatically create the patch for you:
surf = ax.plot_surface(X, Y, Z, label='Lyapunov function on XY plane')
surf._edgecolors2d = surf._edgecolor3d
surf._facecolors2d = surf._facecolor3d
ax.legend()
The attribute names on the right hand side of the assignment are surf._edgecolors3d and surf.facecolors3d for matplotlib < v3.3.3.
I have 2 1D arrays with the values of x and y, and also a 2D array with the values of z for each point where the columns correspond to the x values and the rows to the y values. Is there any way to get a plot_surface with this data? when I try to do it it returns me no plot. Here is the code: (calculate_R is a function I made for the program)
x=np.arange(0,10,1)
y=np.arange(0,1,0.2)
lx= len(x)
ly=len(y)
z=np.zeros((lx,ly))
for i in range(lx):
for j in range(ly):
z[i,j]=calculate_R(y[j],x[i])
fig = plt.figure()
ax = Axes3D(fig)
x, y = np.meshgrid(x, y)
ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap='hot')
You forgot to call plt.show() to display your plot.
Note that you might be able to exploit numpy vectorization to speed up the calculation of z:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
x = np.arange(0,10,1)
y = np.arange(0,1,0.2)
xs, ys = np.meshgrid(x, y)
# z = calculate_R(xs, ys)
zs = xs**2 + ys**2
fig = plt.figure()
ax = Axes3D(fig)
ax.plot_surface(xs, ys, zs, rstride=1, cstride=1, cmap='hot')
plt.show()
Here, I used a simple function, since you didn't supply a fully working example.