How to keep nested axes position while using subplots_adjust - python

I use the following code to add a colorbar at the top left corner of each subplot.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
# Create figure
fig = plt.figure(figsize=(5, 2))
# Specify geometry of the grid for subplots
gs0 = gridspec.GridSpec(1, 3, wspace=0.7)
# Data
a = np.arange(3*5).reshape(3,5)
for ax_i in range(3):
# Create axes
ax = plt.subplot(gs0[ax_i])
# Plot data
plot_pcolor = plt.pcolormesh(a)
# ******** Plot a nested colorbar inside the plot ********
# Define position of the desired colorbar in axes coordinate
# [(lower left x, lower left y), (upper right x, upper right y)]
ax_coord = [(0.05, 0.5), (0.2, 0.95)]
# Transform the two points from axes coordinates to display coordinates
tr1 = ax.transAxes.transform(ax_coord)
# Create an inverse transversion from display to figure coordinates
inv = fig.transFigure.inverted()
tr2 = inv.transform(tr1)
# Position in figure coordinates [left, bottom, width, height]
datco = [tr2[0,0], tr2[0,1], tr2[1,0]-tr2[0,0], tr2[1,1]-tr2[0,1]]
# Create colorbar axes
cbar_ax = fig.add_axes(datco)
# Plot colorbar
cbar = plt.colorbar(plot_pcolor, cax=cbar_ax)
# ********************************************************
if False:
plt.subplots_adjust(left=0.15, bottom=0.2, right=0.95, top=0.8)
plt.savefig('test.png', dpi=500)
which gives the following plot:
However, if I use the subplots_adjust() function (by replacing False to True in the code above), the colorbars do not move properly:
Do you know how I can handle it?

Using the inset_axes() function from the mpl_toolkits module solves the problem. It is also possible to simply use ax.inset_axes().
Here is the new code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
# Create figure
fig = plt.figure(figsize=(5, 2))
# Specify geometry of the grid for subplots
gs0 = gridspec.GridSpec(1, 3, wspace=0.7)
# Data
a = np.arange(3*5).reshape(3,5)
for ax_i in range(3):
# Create axes
ax = plt.subplot(gs0[ax_i])
# Plot data
plot_pcolor = plt.pcolormesh(a)
axins = inset_axes(ax, width="5%", height="50%", loc='upper left')
# Plot colorbar
cbar = plt.colorbar(plot_pcolor, cax=axins)
# ********************************************************
if True:
plt.subplots_adjust(left=0.15, bottom=0.2, right=0.95, top=0.8)
plt.savefig('test.png', dpi=500)
Here is the result:

Related

Plot a reference near the colorbar?

I want to plot a square on the right hand side of the colorbar as a reference with the same color coding (see the image below).
But I couldn't find a way to achieve this goal. Is there any kind and intelligent man that could make this happen?
You can create a custom legend object and locate it next to the colorbar. Shown in a random plot:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
import matplotlib.patches as patches
class SquareObject(object):
pass
# Custom legend object
class SquareObjectHandler(object):
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
x0, y0 = handlebox.xdescent, handlebox.ydescent
width, height = handlebox.width, handlebox.height
l1 = patches.Rectangle(
(x0, y0), # (x,y)
width / 2, # width
height, # height
fill=True,
facecolor="green",
)
handlebox.add_artist(l1)
return [l1]
fig, ax1 = plt.subplots(1, 1, figsize=(14, 6))
im = ax1.imshow(np.arange(100).reshape((10, 10)))
# To locate the colorbar
divider = make_axes_locatable(ax1)
cax = divider.append_axes('right', size='5%', pad=0.05)
plt.colorbar(im, cax=cax, label="colorbar")
# Add the legend
ax1.legend([SquareObject()],
['Reference'],
handler_map={SquareObject: SquareObjectHandler()},
loc='right center',
bbox_to_anchor=(1.4, 0.8), #(x, y)
frameon=False,
handletextpad=-0.5)
plt.show()
You can move the legend with the bbox_to_anchor parameter.
Just to post it here if someone could have the same question that I did. To get the color from the colorbar, I calculated the corresponding proportion of the given reference in the colorbar.
cmap = cm.get_cmap("OrRd") # get the corresponding colorbar
reference = 90 # set the reference
rgb = cmap( (reference - vmin) / (vmax - vmin) ) # find the color in the colorbar
finallty, set it to the "facecolor" in the class "SquareObjectHandler".
The location would be the same way. Figure out the coordinates of the colorbar and set the "bbox_to_anchor" in the "legend" accordingly.

Subplot several scatter histograms

Can someone share an example to create 4 scatter hist plots as a subplot?
To clarify. I am planning to create a pdf of plots. Each page will have 4 subplots. Each subplot being the scatter histogram.
The example of creating scatter histogram seems to be this
Would there be any alternate functions to do this in fewer lines than using this scatter plot example and sub-plotting each of them ?
Using the linked example, all you need to do is increase the number of subplots.
Then for each subplot, you go through the example code to make each one a scatter histogram.
I've pasted a toy example below:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig, axes = plt.subplots(figsize=(10,10),nrows=2, ncols=2)
print(axes)
colors = ['r','b','g','m']
for row in axes:
for axScatter in row:
print()
x = np.random.randn(1000)
y = np.random.randn(1000)
# the scatter plot:
# gets color from the end ('m' will be first)
color = colors.pop()
axScatter.scatter(x, y,color = color)
axScatter.set_aspect(1.)
# create new axes on the right and on the top of the current axes
# The first argument of the new_vertical(new_horizontal) method is
# the height (width) of the axes to be created in inches.
divider = make_axes_locatable(axScatter)
axHistx = divider.append_axes("top", 1.2, pad=0.1, sharex=axScatter)
axHisty = divider.append_axes("right", 1.2, pad=0.1, sharey=axScatter)
# make some labels invisible
axHistx.xaxis.set_tick_params(labelbottom=False)
axHisty.yaxis.set_tick_params(labelleft=False)
# now determine nice limits by hand:
binwidth = 0.25
xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
lim = (int(xymax/binwidth) + 1)*binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
axHistx.hist(x, bins=bins,color=color)
axHisty.hist(y, bins=bins, orientation='horizontal',color=color)
# the xaxis of axHistx and yaxis of axHisty are shared with axScatter,
# thus there is no need to manually adjust the xlim and ylim of these
# axis.
axHistx.set_yticks([0, 50, 100])
axHisty.set_xticks([0, 50, 100])
plt.show()

Matplotlib subplot: imshow + plot

I want to create a plot that looks like the image below. There are two unique plots in the figure. img1 was generated using plt.imshow(), while img2 was generated using plt.plot(). The code I used to generate each of the plots is provided below
plt.clf()
plt.imshow(my_matrix)
plt.savefig("mymatrix.png")
plt.clf()
plt.plot(x,y,'o-')
plt.savefig("myplot.png")
The matrix used in img1 is 64x64. The same range for img2's x-axis (x=range(64)). Ideally, the x-axes of the two img2's align with the axes of img1.
It is important to note that the final image is reminiscent of seaborn's jointplot(), but the marginal subplots (img2) in the image below do not show distribution plots.
You can use the make_axes_locatable functionality of the mpl_toolkits.axes_grid1 to create shared axes along both directions of the central imshow plot.
Here is an example:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np; np.random.seed(0)
Z = np.random.poisson(lam=6, size=(64,64))
x = np.mean(Z, axis=0)
y = np.mean(Z, axis=1)
fig, ax = plt.subplots()
ax.imshow(Z)
# create new axes on the right and on the top of the current axes.
divider = make_axes_locatable(ax)
axtop = divider.append_axes("top", size=1.2, pad=0.3, sharex=ax)
axright = divider.append_axes("right", size=1.2, pad=0.4, sharey=ax)
#plot to the new axes
axtop.plot(np.arange(len(x)), x, marker="o", ms=1, mfc="k", mec="k")
axright.plot(y, np.arange(len(y)), marker="o", ms=1, mfc="k", mec="k")
#adjust margins
axright.margins(y=0)
axtop.margins(x=0)
plt.tight_layout()
plt.show()

matplotlib axesgrid - additional colorbar?

I want to add another colorbar to a plot where I use AxesGrid toolkit. For example, I add a colorbar axes using ImageGrid on the left, and then I add another one on the right manually. Here is a simple example:
f = plt.figure(1)
grid = ImageGrid(f, 111, # similar to subplot(111)
nrows_ncols=(2, 2),
axes_pad=0.01,
add_all=True,
cbar_location="left",
label_mode='L',
cbar_mode="edge",
cbar_size="3%",
cbar_pad="2%",
)
for i in range(3):
m = grid[i].matshow(np.arange(100).reshape((10, 10)))
plt.colorbar(m, grid.cbar_axes[0])
m = grid[3].matshow(np.arange(100).reshape((10, 10)), cmap='plasma')
plt.colorbar(m, shrink=0.5, anchor=(0, 0))
plt.show()
How do I make the new colorbar match the position of one of the subplots in the grid exactly? I at least managed to fix the size and y-position using shrink and anchor... But it also gets a bit complicated if I try to account for the padding between subplots, and if they are rectangular rather than square...
One option is to manually place the colorbar axes according to the position of one of the axes. To this end one first needs to draw the canvas, such that the positions are known. One can then create a new axes according to coordinates of image plot. This new axes will serve as the colorbar axes.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import ImageGrid
fig = plt.figure(1)
grid = ImageGrid(fig, 111, # similar to subplot(111)
nrows_ncols=(2, 2),
axes_pad=0.01,
add_all=True,
cbar_location="left",
label_mode='L',
cbar_mode="edge",
cbar_size="3%",
cbar_pad="2%",
)
for i in range(3):
m = grid[i].matshow(np.arange(100).reshape((10, 10)))
plt.colorbar(m, grid.cbar_axes[0])
m = grid[3].matshow(np.arange(100).reshape((10, 10)), cmap='plasma')
# first draw the figure, such that the axes are positionned
fig.canvas.draw()
#create new axes according to coordinates of image plot
trans = fig.transFigure.inverted()
g3 =grid[3].bbox.transformed(trans)
pos = [g3.x1 + g3.width*0.02, g3.y0, g3.width*0.03, g3.height ]
cax = fig.add_axes(pos) #l,b,w,h
# add colorbar to new axes
plt.colorbar(m, cax=cax)
plt.show()
This method depends on the position of the axes in the figure, once that changes, e.g. because the figure is rezised, unforseable things might happen.
A different method, which does not rely on the drawn coordinates, is to (mis)use inset axes and place the inset outside the axes. In this way the coordinates by which the inset is located are axes coordinates, so the colorbar will change its position according to the axes.
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
cax = inset_axes(grid[3], "3%", "100%", loc=3, bbox_to_anchor=(1.02,0,1,1),
bbox_transform=grid[3].transAxes, borderpad=0.0)
plt.colorbar(m, cax=cax)

python matplotlib gridspec, unwanted arbitrary axis labels

I have some code to plot a grid, with the data in each cell being distinct and having a very specific position. The easiest way I found to do this was to create the grid with gridspec and use it to precisely position my subplots, however I'm having a problem where the overall grid is labelled from 0 to 1 along each axis. This happens every time, even when the dimensions of the grid are changed. Obviously these numbers have no relevance to my data, and as what I am aiming to display is qualitative rather than quantitative I would like to remove all labels from this plot entirely.
Here is a link to an image with an example of my problem
And here is the MWE that I used to create that image:
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
# mock-up of data being used
x = 6
y = 7
table = np.zeros((x, y))
# plotting
fig = plt.figure(1)
gs = gridspec.GridSpec(x, y, wspace=0, hspace=0)
plt.title('Example Plot')
for (j, k), img in np.ndenumerate(table):
ax = fig.add_subplot(gs[x - j - 1, k])
ax.set_xticklabels('')
ax.set_yticklabels('')
plt.show()
I have not been able to find note of anything like this problem, so any help would be greatly appreciated.
If you just want to draw a grid over the plot, use this code:
import numpy as np
import matplotlib.pyplot as plt
# mock-up of data being used
x = 6
y = 7
table = np.zeros((x, y))
# plotting
fig = plt.figure(1)
plt.title('Example Plot')
plt.gca().xaxis.grid(True, color='darkgrey', linestyle='-')
plt.gca().yaxis.grid(True, color='darkgrey', linestyle='-')
plt.show()
Another variant is used gridspec:
...
# hide ticks of main axes
ax0 = plt.gca()
ax0.get_xaxis().set_ticks([])
ax0.get_yaxis().set_ticks([])
gs = gridspec.GridSpec(x, y, wspace=0, hspace=0)
plt.title('Example Plot')
for (j, k), img in np.ndenumerate(table):
ax = fig.add_subplot(gs[x - j - 1, k])
# hide ticks of gribspec axes
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])

Categories