Is it possible to draw 2D and 3D contour plot like this in python.
Sorry I couldn't provide much detail on the plot in terms of mathematical equations and all.
Use plot_surface along with contour to project the contour. It is not limited to the Z plane; you can do this to the X and Y planes as well.
There is an example in the official documentation of Matplotlib: https://matplotlib.org/stable/gallery/mplot3d/contourf3d_2.html#sphx-glr-gallery-mplot3d-contourf3d-2-py
Note that an offset is needed to move the contour to the bottom of the 3D plot. You can set the offset equal to the lower bound of the y limit.
I created an example:
import matplotlib.pyplot as plt
import numpy as np
x = y = np.arange(-3.0, 3.0, 0.02)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z3 = np.exp(-(X + 1) ** 2 - (Y + 1) ** 2)
Z = (Z1 - Z2 - Z3) * 2
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
# draw surface plot
surf = ax.plot_surface(X, Y, Z, lw=0.1, cmap='coolwarm', edgecolor='k')
# add color bar
fig.colorbar(surf, shrink=0.5, aspect=10)
# projecting the contour with an offset
ax.contour(X, Y, Z, 20, zdir='z', offset=-2, cmap='coolwarm')
# match the lower bound of zlim to the offset
ax.set(zlim=(-2, 1))
plt.tight_layout()
plt.show()
Related
I would like to make surface plot of a function which is discontinuous at certain values in parameter space. It is near these discontinuities that the plot's coloring becomes incorrect, as shown in the picture below. How can I fix this?
My code is given below:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5*np.arctan2(2*gamma, mu_b-mu_a)
epsilon = 2*gamma**2/np.sqrt((mu_a-mu_b)**2+4*gamma**2)
y1 = np.arccos(0.5/t*(-mu_a*np.sin(theta)**2 -mu_b*np.cos(theta)**2 - epsilon))
y2 = np.arccos(0.5/t*(-mu_a*np.cos(theta)**2 -mu_b*np.sin(theta)**2 + epsilon))
return y1+y2
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-2.5, 2.5, 0.01)
Y = np.arange(-2.5, 2.5, 0.01)
X, Y = np.meshgrid(X, Y)
Z = phase(X, Y, 1, 0.6)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
An idea is to make all the arrays 1D, filter out the NaN values and then call ax.plot_trisurf:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5 * np.arctan2(2 * gamma, mu_b - mu_a)
epsilon = 2 * gamma ** 2 / np.sqrt((mu_a - mu_b) ** 2 + 4 * gamma ** 2)
with np.errstate(divide='ignore', invalid='ignore'):
y1 = np.arccos(0.5 / t * (-mu_a * np.sin(theta) ** 2 - mu_b * np.cos(theta) ** 2 - epsilon))
y2 = np.arccos(0.5 / t * (-mu_a * np.cos(theta) ** 2 - mu_b * np.sin(theta) ** 2 + epsilon))
return y1 + y2
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# Make data.
X = np.linspace(-2.5, 2.5, 200)
Y = np.linspace(-2.5, 2.5, 200)
X, Y = np.meshgrid(X, Y)
X = X.ravel() # make the array 1D
Y = Y.ravel()
Z = phase(X, Y, 1, 0.6)
mask = ~np.isnan(Z) # select the indices of the valid values
# Plot the surface.
surf = ax.plot_trisurf(X[mask], Y[mask], Z[mask], cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Some remarks:
plot_trisurf will join the XY-values via triangles; this only works well if the domain is convex
to make things draw quicker, less points could be used (the original used 500x500 points, the code here reduces that to 200x200
calling fig.gca(projection='3d') has been deprecated; instead, you could call fig.add_subplot(projection='3d')
the warnings for dividing by zero or using arccos out of range can be temporarily suppressed; that way the warning will still be visible for situations when such isn't expected behavior
Is there any simple way to fill regions outside the contour boundary? Take the following example from matplotlib website:
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots()
CS = ax.contour(X, Y, Z, levels=(0.1, 1.5))
ax.clabel(CS, inline=True, fontsize=10)
This will plot two simple contour of values 0.1 and 1.5.
Now I would like to show regions outside these contours with colors. I tried
ax.contourf(X, Y, Z, levels=(0.1, 1.5))
but this fills regions between 0.1 and 1.5. What can I do to fill outside region but keep inside transparent/white?
(question edited to remove confusing elements from the previous version)
I have plotted a contourf (and contour to plot lines on top) graph with colorbar and equispaced data levels and it is ok. However, when I add an arbitrary level value(1.1 in the example), it is not representd correctly in the colorbar.
In the reproducible example below, besides the levels with steps of 0.5, a new value with a step of 0.1 is added. In the colorbar, the distances are the same.
How can I correct this?
Thanks in advance
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-2.0, 3.0, 0.025)
y = np.arange(-2.0, 3.0, 0.025)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
levels = np.arange(-2,3,1)
levels=np.append(levels,1.1)
levels=np.sort(levels)
fig, ax = plt.subplots()
c = plt.contourf(X, Y, Z, levels=levels,alpha=0.15)
cs = plt.contour(X, Y, Z, levels=levels)
ax.clabel(cs, inline=1, fontsize=10)
cbar=plt.colorbar(c)
plt.show()
You should pass spacing parameter to plt.colorbar:
spacing: uniform spacing gives each discrete color the same space; proportional makes the space proportional to the data interval.
cbar=plt.colorbar(c, spacing = 'proportional')
I am trying to generate 2D line plots at different angles or slices of a matplotlib contourf plot.
As an example from the matplotlib contourf demo example below
import numpy as np
import matplotlib.pyplot as plt
origin = 'lower'
delta = 0.025
x = y = np.arange(-3.0, 3.01, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
nr, nc = Z.shape
fig1, ax2 = plt.subplots(constrained_layout=True)
CS = ax2.contourf(X, Y, Z, 10, cmap=plt.cm.viridis, origin=origin,extend='both')
ax2.set_title('Random Plot')
ax2.set_xlabel('X Axis')
ax2.set_ylabel('Y Axis')
cbar = fig1.colorbar(CS)
Ideally, I want to generate lines at different angles (30,45,60 degrees) across the map (starting at any arbitrary point till the end of existing array) and then plot the Z variations across that line.
I think a simpler problem in principle would be, lines from (X2,Y2) to (X1,Y1) and plot the Z variation for the given contour (which is already interpolated data).
As an example, original problem would be line from (-3,-3) at angle 45 deg across. Analogous problem would be lets say a line from (-3,-3) to (3,3) and plot the Z variation at different locations on that line.
The source contour plot generated is :
Here is a rather inefficient approach, but it does the job. It recalculates the function on a new grid of which it only needs the diagonal.
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import RectBivariateSpline
delta = 0.025
x = y = np.arange(-3.0, 3.01, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
nr, nc = Z.shape
x1, y1 = -3, -2
x2, y2 = 3, 2
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(15, 5))
CS = ax1.contourf(X, Y, Z, 10, cmap=plt.cm.viridis, origin='lower', extend='both')
ax1.plot([x1, x2], [y1, y2], color='k', ls='--', lw=3, alpha=0.6)
ax1.set_xlabel('X Axis')
ax1.set_ylabel('Y Axis')
cbar = fig.colorbar(CS, ax=ax1)
spline_func = RectBivariateSpline(x, y, Z)
xline = np.linspace(x1, x2, 200)
yline = np.linspace(y1, y2, 200)
zline = spline_func(xline, yline)
ax2.plot(xline, zline.diagonal())
ax2.set_xlabel('X Axis')
ax2.set_ylabel('Z Axis')
plt.show()
How do I plot a cone in 3 dimensions when I know the coordinates of the vertex, the vector along which the cone lies (as a line segment whose end point coordinates are known), and the height of the cone, in matplotlib or any other python plotting module?
I guess what would be most helpful, would be some instruction on plotting implicit functions in matplotlib.
What I have tried, is to create a grid of points (x,y, and z), define an equation of the cone, and check whether each of those grid points satisfies the equation of the cone (since grid resolution may mean that it won't exactly satisfy the equation, I check whether it satisfies the equation upto some accuracy). However, I am not sure now how to plot this implicit data set in matplotlib. I have tried writing out the equation (which involves quadratic and linear terms in x,y and z) as a quadratic in z, solving for z and performing a surface plot with z in terms of x and y, but that gave me a plane instead of a cone.
Here is the code I used:
x = np.linspace(0,100,100)
y = np.linspace(0,100,100)
z = (-4823.7 + np.sqrt(abs((4823.7**2) - (4*(-240.185)*((-5692*y) - (240.185*np.multiply(x,x)) - (240.185*np.multiply(y,y)) - 57607.26 )) ))/np.float(2*(-240.185)))
#print z
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.arange(-50.0, 50, 1)
y = np.arange(-50.0, 50, 1)
X, Y = np.meshgrid(x, y)
ax.plot_surface(X, Y, z)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
try with it:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.arange(-50.0, 50, 1)
y = np.arange(-50.0, 50, 1)
X, Y = np.meshgrid(x, y)
Z = (-4823.7 + np.sqrt(abs((4823.7 ** 2) - (4 * (-240.185) * ((-5692 * Y) - (240.185 * np.multiply(X, X))
- (240.185 * np.multiply(Y, Y)) - 57607.26)))) / np.float(
2 * (-240.185)))
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()