I have to do three plots (contour, 3d surface, and heatmap) in matplotlib. The corresponding grid dimension for the three plots are ([0, 0], [0, 1], and [1, 0:1])
I have a few problems
The text annotation for heatmap (ax3), seem to fly out of ax3, into
ax1 and ax2. How can I constrain them to be within the ax3 only ?
Is this the fastest way to annotate text assuming that I do not want
to use seaborn ?
Can I get some tips on how to resolve my problems ?
Below is the code snippet to perform the plot operation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gspec
from scipy.interpolate import griddata
import pyautogui
from scipy import stats
x = pyautogui.size()
width = x.width
height = x.height
x = np.arange(0, 10, 0.5)
y = np.arange(0, 10, 0.5)
X, Y = np.meshgrid(x, y)
data = 2 * (np.sin(X) + np.sin(3 * Y))
fig = plt.figure()
fig.set_figheight(height / 100)
fig.set_figwidth(width / 100)
fig.set_dpi(100)
gs = gspec.GridSpec(nrows=2, ncols=2)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, 1], projection='3d')
ax3 = plt.subplot(gs[1, 0:1])
ctr = ax1.contourf(X, Y, data, 10, cmap='viridis')
ax1.clabel(ctr, inline=True, fontsize=8)
cbar = plt.colorbar(ctr, ax=ax1)
cbar.set_label('ColorbarLabel', size=15)
surf = ax2.plot_surface(X, Y, data, cmap='jet')
cbar1 = plt.colorbar(surf, ax=ax2)
cbar1.set_label('Colorbar2', size=15)
hmap = ax3.pcolormesh(X, Y, data, cmap='viridis')
cbar2 = plt.colorbar(hmap, ax=ax3)
for y in range(data.shape[0]):
for x in range(data.shape[1]):
ax3.text(x, y, '%.1f' % data[y, x], size=3)
I assume you want your heatmap to cover both columns. To achieve that you have to use ax3 = plt.subplot(gs[1, 0:2]): this tells matplotlib to use columns 0 and 1 (2 is excluded).
The text annotation for heatmap (ax3), seem to fly out of ax3, into ax1 and ax2. How can I constrain them to be within the ax3 only ?
That's because you are using the wrong coordinates in ax3.text.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gspec
from scipy.interpolate import griddata
import pyautogui
from scipy import stats
x = pyautogui.size()
width = x.width
height = x.height
x = np.arange(0, 10, 0.5)
y = np.arange(0, 10, 0.5)
X, Y = np.meshgrid(x, y)
data = 2 * (np.sin(X) + np.sin(3 * Y))
fig = plt.figure()
fig.set_figheight(height / 100)
fig.set_figwidth(width / 100)
fig.set_dpi(100)
gs = gspec.GridSpec(nrows=2, ncols=2)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, 1], projection='3d')
ax3 = plt.subplot(gs[1, 0:2])
ctr = ax1.contourf(X, Y, data, 10, cmap='viridis')
ax1.clabel(ctr, inline=True, fontsize=8)
cbar = plt.colorbar(ctr, ax=ax1)
cbar.set_label('ColorbarLabel', size=15)
surf = ax2.plot_surface(X, Y, data, cmap='jet')
cbar1 = plt.colorbar(surf, ax=ax2)
cbar1.set_label('Colorbar2', size=15)
hmap = ax3.pcolormesh(X, Y, data, cmap='viridis')
cbar2 = plt.colorbar(hmap, ax=ax3)
for i in range(data.shape[0]):
for j in range(data.shape[1]):
ax3.text(x[j], y[i], '%.1f' % data[i, j], size=5)
Related
I'm trying to colour the faces of the tetrahedra determined by the edges shown here in green:
The plot is generated using this code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
%matplotlib notebook
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x,y,z = nCompoundTetra(bloch_vectors,nTetrahedron).getEdges()
x,y,z = list(x)[0],list(y)[0],list(z)[0]
ax.scatter3D(x, y, z, color = "black", s = 3.5)
ax.plot(x,y,z, color="g", linewidth= 1)
ax.set_xlim([-1.1, 1.1])
ax.set_ylim([-1.1, 1.1])
ax.set_zlim([-1.1, 1.1])
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xs = np.outer(np.cos(u), np.sin(v))
ys = np.outer(np.sin(u), np.sin(v))
zs = np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_wireframe(xs, ys, zs, color="grey", alpha=0.15)
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([5, 5, 7, 7]))
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
ax.set_axis_off()
plt.tight_layout()
plt.show()
nCompoundTetra(bloch_vectors,nTetrahedron).getEdges() is a method I'm implementing to generate more tetrahedron
I get the wrong color scheme and range when I try to plot colorbar with contourf. It works well with pcolormesh. I'm aiming to only use 1 colorbar for all the subplots
fig = plt.figure(figsize=(9,8))
gs = gridspec.GridSpec(nrows=1, ncols=3)
color = plt.get_cmap('PRGn')
ax0 = plt.subplot(gs[0, 0])
ax0.contourf(x, y, z1, cmap=color)
ax1 = plt.subplot(gs[0, 1])
ax1.contourf(x, y, z2, cmap=color)
ax2 = plt.subplot(gs[0, 2])
ax2.contourf(x, y, z3, cmap=color)
# colorbar
im = plt.gca().get_children()[0]
cax = fig.add_axes([.918, 0.175, 0.025, 0.4])
cb = fig.colorbar(im, cax=cax)
Thank you
The easiest is to store the result of contourf in a variable to pass to plt.colorbar.
Note that for the 3 plots to use the same colorbar, the limits need to be the same for each. Default, vmin and vmax are set to the minimum and maximum of the given z-array. To get them equal for the 3 plots, one could set them to the global minimum and maximum.
Here is an example (using the modern way to define the subplots):
import numpy as np
import matplotlib.pyplot as plt
fig, (ax0, ax1, ax2) = plt.subplots(nrows=1, ncols=3, figsize=(9, 8))
# create some dummy data, where z1, z2 and z3 possibly have different ranges
x, y = np.meshgrid(np.linspace(0, 4, 20), np.linspace(300, 850, 20))
z1 = np.cos(x) + np.sin(y / 100)
z2 = np.cos(x) + np.sin(y / 100) + 0.2
z3 = np.cos(x) + np.sin(y / 100) + 0.5
vmin = min(z1.min(), z2.min(), z3.min())
vmax = max(z1.max(), z2.max(), z3.max())
cmap = plt.get_cmap('PRGn')
contour_z1 = ax0.contourf(x, y, z1, cmap=cmap, vmin=vmin, vmax=vmax)
contour_z2 = ax1.contourf(x, y, z2, cmap=cmap, vmin=vmin, vmax=vmax)
contour_z3 = ax2.contourf(x, y, z3, cmap=cmap, vmin=vmin, vmax=vmax)
# colorbar
cax = fig.add_axes([.918, 0.175, 0.025, 0.4])
cb = fig.colorbar(contour_z1, cax=cax)
plt.show()
This is a very similar question to "How to plot pcolor colorbar in a different subplot - matplotlib". I am trying to plot a filled contour plot and a line plot with a shared axis and the colorbar in a separate subplot (i.e. so it doesn't take up space for the contourf axis and thus muck up the x-axis sharing). However, the x-axis in my code does not rescale nicely:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
z = np.random.rand(20, 20)
x, y = np.arange(20), np.arange(20)
y2 = np.random.rand(20)
fig = plt.figure(figsize=(8, 8))
gs = mpl.gridspec.GridSpec(2, 2, height_ratios=[1, 2], width_ratios=[2, 1])
ax1 = fig.add_subplot(gs[1, 0])
ax2 = fig.add_subplot(gs[0, 0], sharex=ax1)
ax3 = fig.add_subplot(gs[1, 1])
cont = ax1.contourf(x, y, z, 20)
plt.tick_params(which='both', top=False, right=False)
ax2.plot(x, y2, color='g')
plt.tick_params(which='both', top=False, right=False)
cbar = plt.colorbar(cont, cax=ax3)
cbar.set_label('Intensity', rotation=270, labelpad=20)
plt.tight_layout()
plt.show()
which produces an x-axis scaled from 0 to 20 (inclusive) rather than 0 to 19, which means there is unsightly whitespace in the filled contour plot. Commenting out the sharex=ax1 in the above code means that the x-axis for the contour plot is scaled nicely, but not for the line plot above it and the plt.tick_params code has no effect on either axis.
Is there a way of solving this?
You could also turn off the autoscaling of x-axis for all subsequent call of plot on this axis so that it keeps the range set by contourf and sharex=True :
ax2.set_autoscalex_on(False)
This comes even before your call to ax2.plot() and I think it is better than calling ax2.set_xlim(0, 19) since you do not need to know what are the actual limit of your x-axis that may be needed.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
z = np.random.rand(20, 20)
x, y = np.arange(20), np.arange(20)
y2 = np.random.rand(20)
fig = plt.figure(figsize=(8, 8))
gs = mpl.gridspec.GridSpec(2, 1, height_ratios=[1, 2], width_ratios=[2])
ax1 = fig.add_subplot(gs[1, 0])
ax2 = fig.add_subplot(gs[0, 0], sharex=ax1)
cont = ax1.contourf(x, y, z, 20)
plt.tick_params(which='both', top=False, right=False)
ax2.set_autoscalex_on(False)
ax2.plot(x, y2, color='g')
axins = inset_axes(ax1,
width="5%", # width = 10% of parent_bbox width
height="100%", # height : 50%
loc=6,
bbox_to_anchor=(1.05, 0., 1, 1),
bbox_transform=ax1.transAxes,
borderpad=0,
)
cbar = plt.colorbar(cont, cax=axins)
plt.show()
You can use inset_axes for this without added another axis.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
z = np.random.rand(20, 20)
x, y = np.arange(20), np.arange(20)
y2 = np.random.rand(20)
fig = plt.figure(figsize=(8, 8))
gs = mpl.gridspec.GridSpec(2, 2, height_ratios=[1, 2], width_ratios=[2, 1])
ax1 = fig.add_subplot(gs[1, 0])
ax2 = fig.add_subplot(gs[0, 0], sharex=ax1)
cont = ax1.contourf(x, y, z, 20)
plt.tick_params(which='both', top=False, right=False)
ax2.plot(x, y2, color='g')
plt.tick_params(which='both', top=False, right=False)
axins = inset_axes(ax1,
width="5%", # width = 10% of parent_bbox width
height="100%", # height : 50%
loc=6,
bbox_to_anchor=(1.05, 0., 1, 1),
bbox_transform=ax1.transAxes,
borderpad=0,
)
cbar = plt.colorbar(cont, cax=axins)
plt.savefig('figure.jpg',bbox_inches='tight',dpi=200)
I would like to add a transparent cylinder to my 3D scatter plot. How can I do it?
This is the code I am using to make the plot:
fig = plt.figure(2, figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X, Y, Z, c=Z,cmap=plt.cm.Paired)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
plt.xticks()
Today I have to do the same thing in my project about adding a transparent cylinder in the result. This is the code I get finally. So I share it with you guys just for learning
import numpy as np
def data_for_cylinder_along_z(center_x,center_y,radius,height_z):
z = np.linspace(0, height_z, 50)
theta = np.linspace(0, 2*np.pi, 50)
theta_grid, z_grid=np.meshgrid(theta, z)
x_grid = radius*np.cos(theta_grid) + center_x
y_grid = radius*np.sin(theta_grid) + center_y
return x_grid,y_grid,z_grid
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xc,Yc,Zc = data_for_cylinder_along_z(0.2,0.2,0.05,0.1)
ax.plot_surface(Xc, Yc, Zc, alpha=0.5)
plt.show()
And you will get this beautiful figure.
One possible method is to use the plot_surface. Adapting the solution given in this blog post then have
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Scatter graph
N = 100
X = np.random.uniform(-1, 1, N)
Y = np.random.uniform(-1, 1, N)
Z = np.random.uniform(-2, 2, N)
ax.scatter(X, Y, Z)
# Cylinder
x=np.linspace(-1, 1, 100)
z=np.linspace(-2, 2, 100)
Xc, Zc=np.meshgrid(x, z)
Yc = np.sqrt(1-Xc**2)
# Draw parameters
rstride = 20
cstride = 10
ax.plot_surface(Xc, Yc, Zc, alpha=0.2, rstride=rstride, cstride=cstride)
ax.plot_surface(Xc, -Yc, Zc, alpha=0.2, rstride=rstride, cstride=cstride)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
plt.show()
I've added some minimal configuration of the surface, better can be achieved by consulting the docs.
I improved on #Greg's answer and made a solid 3D cylinder with a top and bottom surface and rewrote the equation so that you can translate in the x, y,and z
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d.art3d as art3d
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle
def plot_3D_cylinder(radius, height, elevation=0, resolution=100, color='r', x_center = 0, y_center = 0):
fig=plt.figure()
ax = Axes3D(fig, azim=30, elev=30)
x = np.linspace(x_center-radius, x_center+radius, resolution)
z = np.linspace(elevation, elevation+height, resolution)
X, Z = np.meshgrid(x, z)
Y = np.sqrt(radius**2 - (X - x_center)**2) + y_center # Pythagorean theorem
ax.plot_surface(X, Y, Z, linewidth=0, color=color)
ax.plot_surface(X, (2*y_center-Y), Z, linewidth=0, color=color)
floor = Circle((x_center, y_center), radius, color=color)
ax.add_patch(floor)
art3d.pathpatch_2d_to_3d(floor, z=elevation, zdir="z")
ceiling = Circle((x_center, y_center), radius, color=color)
ax.add_patch(ceiling)
art3d.pathpatch_2d_to_3d(ceiling, z=elevation+height, zdir="z")
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')
plt.show()
# params
radius = 3
height = 10
elevation = -5
resolution = 100
color = 'r'
x_center = 3
y_center = -2
plot_3D_cylinder(radius, height, elevation=elevation, resolution=resolution, color=color, x_center=x_center, y_center=y_center)
I'm trying to generate two subplots side by side, sharing the y axis, with a single colorbar for both.
This is a MWE of my code:
import matplotlib.pyplot as plt
import numpy as np
def rand_data(l, h):
return np.random.uniform(low=l, high=h, size=(100,))
# Generate data.
x1, x2, y, z = rand_data(0., 1.), rand_data(100., 175.), \
rand_data(150., 200.), rand_data(15., 33.)
fig = plt.figure()
cm = plt.cm.get_cmap('RdYlBu')
ax0 = plt.subplot(121)
plt.scatter(x1, y, c=z, cmap=cm)
ax1 = plt.subplot(122)
# make these y tick labels invisible
plt.setp(ax1.get_yticklabels(), visible=False)
plt.scatter(x2, y, c=z, cmap=cm)
cbar = plt.colorbar()
plt.show()
what this returns is a left subplot slightly larger horizontally than the right one since this last includes the colorbar, see below:
I've tried using ax.set_aspect('equal') but since the x axis are not in the same range the result looks awful.
I need both these plots to be displayed squared. How can I do this?
To expend my comment that one can make 3 plots, plot the colorbar() in the 3rd one, the data plots in the 1st and 2nd. This way, if necessary, we are free to do anything we want to the 1st and 2nd plots:
def rand_data(l, h):
return np.random.uniform(low=l, high=h, size=(100,))
# Generate data.
x1, x2, y, z = rand_data(0., 1.), rand_data(100., 175.), \
rand_data(150., 200.), rand_data(15., 33.)
fig = plt.figure(figsize=(12,6))
gs=gridspec.GridSpec(1,3, width_ratios=[4,4,0.2])
ax1 = plt.subplot(gs[0])
ax2 = plt.subplot(gs[1])
ax3 = plt.subplot(gs[2])
cm = plt.cm.get_cmap('RdYlBu')
ax1.scatter(x1, y, c=z, cmap=cm)
SC=ax2.scatter(x2, y, c=z, cmap=cm)
plt.setp(ax2.get_yticklabels(), visible=False)
plt.colorbar(SC, cax=ax3)
plt.tight_layout()
plt.savefig('temp.png')
Updated - here is another option without using GridSpec.
import numpy as np
import matplotlib.pyplot as plt
N = 50
x_vals = np.random.rand(N)
y_vals = np.random.rand(N)
z1_vals = np.random.rand(N)
z2_vals = np.random.rand(N)
minimum_z = min(np.min(z1_vals), np.min(z2_vals))
maximum_z = max(np.max(z1_vals), np.max(z2_vals))
fig, axis_array = plt.subplots(1,2, figsize = (20, 10), subplot_kw = {'aspect':1})
ax0 = axis_array[0].scatter(x_vals, y_vals, c = z1_vals, s = 100, cmap = 'rainbow', vmin = minimum_z, vmax = maximum_z)
ax1 = axis_array[1].scatter(x_vals, y_vals, c = z2_vals, s = 100, cmap = 'rainbow', vmin = minimum_z, vmax = maximum_z)
cax = fig.add_axes([0.95, 0.05, 0.02, 0.95]) #this locates the axis that is used for your colorbar. It is scaled 0 - 1.
fig.colorbar(ax0, cax, orientation = 'vertical') #'ax0' tells it which plot to base the colors on
plt.show()