tight_layout() reserves ylabel and yticklabels space even when disabled - python

I have a 2*4 subplots figure, with half of the ylabel and yticklabels disabled. Unfortunately, tight_layout() does not remove the extra white space which corresponds to the area where the ylabel and yticklabels would appear if they were not disabled. The ylabel and yticklabels are removed because I would like to have 4 pairs of comparison subplots. The plot looks something like this.
I am looking for an efficient way to remove the extra white space. In fact, I would like each pair of plots to be next to each other with no space at all. Here is an working example.
import matplotlib.pyplot as plt
fig, ((ax0, ax1, ax2, ax3), (ax4, ax5, ax6, ax7)) = plt.subplots(2, 4, figsize=(8, 4))
axs = [ax0, ax1, ax2, ax3, ax4, ax5, ax6, ax7]
for i in range(4):
axs[2*i].set_ylabel('parameter '+str(i))
axs[2*i+1].set_yticklabels([])
plt.tight_layout()
plt.show()
The code should yield the above plot. Any tips would be appreciated. Many thanks!

You can do it by having two subgrids and forcing a null distance between the axis (I followed this tutorial). The wspace parameter is described here.
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=False) # you must disable automatic spacing
gs = fig.add_gridspec(1, 2, figure=fig)
gs0 = gs[0].subgridspec(2, 2, wspace=0.0)
gs1 = gs[1].subgridspec(2, 2, wspace=0.0)
axs = [[],[]]
for i in range(2):
for j in range(2):
axs[0].append(fig.add_subplot(gs0[i, j]))
axs[1].append(fig.add_subplot(gs1[i, j]))
axs = [*axs[0],*axs[1]]
for i in range(4):
axs[2*i].set_ylabel('parameter ' + str(i))
axs[2*i+1].set_yticklabels([])
Since the automatic spacing is disabled, you may have to play with axis and figure properties to adapt the position of the axis, labels, etc.
A partial (distance won't be null, but may be interesting for other users), more elegant solution is to use constrained_layout when creating the figure. More info in matplotlib's documentation.
import matplotlib.pyplot as plt
fig, axs = plt.subplots(
2, 4,
figsize=(8, 4),
constrained_layout=True,
)
axs = axs.reshape([8])
for i in range(4):
axs[2*i].set_ylabel('parameter '+str(i))
axs[2*i+1].set_yticklabels([])
# plt.tight_layout() # not necessary
plt.show()

Related

How to reduce horizontal spacing between subplots in matplotlib python? [duplicate]

The code below produces gaps between the subplots. How do I remove the gaps between the subplots and make the image a tight grid?
import matplotlib.pyplot as plt
for i in range(16):
i = i + 1
ax1 = plt.subplot(4, 4, i)
plt.axis('on')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_aspect('equal')
plt.subplots_adjust(wspace=None, hspace=None)
plt.show()
The problem is the use of aspect='equal', which prevents the subplots from stretching to an arbitrary aspect ratio and filling up all the empty space.
Normally, this would work:
import matplotlib.pyplot as plt
ax = [plt.subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
plt.subplots_adjust(wspace=0, hspace=0)
The result is this:
However, with aspect='equal', as in the following code:
import matplotlib.pyplot as plt
ax = [plt.subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
a.set_aspect('equal')
plt.subplots_adjust(wspace=0, hspace=0)
This is what we get:
The difference in this second case is that you've forced the x- and y-axes to have the same number of units/pixel. Since the axes go from 0 to 1 by default (i.e., before you plot anything), using aspect='equal' forces each axis to be a square. Since the figure is not a square, pyplot adds in extra spacing between the axes horizontally.
To get around this problem, you can set your figure to have the correct aspect ratio. We're going to use the object-oriented pyplot interface here, which I consider to be superior in general:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8)) # Notice the equal aspect ratio
ax = [fig.add_subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
a.set_aspect('equal')
fig.subplots_adjust(wspace=0, hspace=0)
Here's the result:
You can use gridspec to control the spacing between axes. There's more information here.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure(figsize = (4,4))
gs1 = gridspec.GridSpec(4, 4)
gs1.update(wspace=0.025, hspace=0.05) # set the spacing between axes.
for i in range(16):
# i = i + 1 # grid spec indexes from 0
ax1 = plt.subplot(gs1[i])
plt.axis('on')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_aspect('equal')
plt.show()
Without resorting gridspec entirely, the following might also be used to remove the gaps by setting wspace and hspace to zero:
import matplotlib.pyplot as plt
plt.clf()
f, axarr = plt.subplots(4, 4, gridspec_kw = {'wspace':0, 'hspace':0})
for i, ax in enumerate(f.axes):
ax.grid('on', linestyle='--')
ax.set_xticklabels([])
ax.set_yticklabels([])
plt.show()
plt.close()
Resulting in:
With recent matplotlib versions you might want to try Constrained Layout. This does (or at least did) not work with plt.subplot() however, so you need to use plt.subplots() instead:
fig, axs = plt.subplots(4, 4, constrained_layout=True)
Have you tried plt.tight_layout()?
with plt.tight_layout()
without it:
Or: something like this (use add_axes)
left=[0.1,0.3,0.5,0.7]
width=[0.2,0.2, 0.2, 0.2]
rectLS=[]
for x in left:
for y in left:
rectLS.append([x, y, 0.2, 0.2])
axLS=[]
fig=plt.figure()
axLS.append(fig.add_axes(rectLS[0]))
for i in [1,2,3]:
axLS.append(fig.add_axes(rectLS[i],sharey=axLS[-1]))
axLS.append(fig.add_axes(rectLS[4]))
for i in [1,2,3]:
axLS.append(fig.add_axes(rectLS[i+4],sharex=axLS[i],sharey=axLS[-1]))
axLS.append(fig.add_axes(rectLS[8]))
for i in [5,6,7]:
axLS.append(fig.add_axes(rectLS[i+4],sharex=axLS[i],sharey=axLS[-1]))
axLS.append(fig.add_axes(rectLS[12]))
for i in [9,10,11]:
axLS.append(fig.add_axes(rectLS[i+4],sharex=axLS[i],sharey=axLS[-1]))
If you don't need to share axes, then simply axLS=map(fig.add_axes, rectLS)
Another method is to use the pad keyword from plt.subplots_adjust(), which also accepts negative values:
import matplotlib.pyplot as plt
ax = [plt.subplot(2,2,i+1) for i in range(4)]
for a in ax:
a.set_xticklabels([])
a.set_yticklabels([])
plt.subplots_adjust(pad=-5.0)
Additionally, to remove the white at the outer fringe of all subplots (i.e. the canvas), always save with plt.savefig(fname, bbox_inches="tight").

Remove separation between subplots in horizontal direction

I have four 2D arrays I want to plot in four subplots using imshow. I want the separation between these subplots to be removed, making the subplots touch each other, similar to the Matplotlib Documentation (second to last example). My attempt is
fig, axs = plt.subplots(2, 2, sharex='col', sharey='row', gridspec_kw={'hspace': 0, 'wspace': 0})
(ax1, ax2), (ax3, ax4) = axs
ax1.imshow(im1)
ax2.imshow(im2)
ax3.imshow(im3)
ax4.imshow(im4)
for ax in fig.get_axes():
ax.label_outer()
plt.show()
This produces
The separation in the vertical direction seems to be correctly removed, but I still have a separation in the horizontal direction. Does anyone know how I can get rid of that as well here?
You can try something along the lines of this answer of ImportanceOfBeingErnest. I have prepared the following pseudo code based on it for your question. You can try it and see if it works for you.
from matplotlib import gridspec
nrow, ncol = 2, 2
fig = plt.figure(figsize=(6,6))
gs = gridspec.GridSpec(nrow, ncol,
wspace=0.0, hspace=0.0,
top=1.-0.5/(nrow+1), bottom=0.5/(nrow+1),
left=0.5/(ncol+1), right=1-0.5/(ncol+1))
ims = [im1, im2, im3, im4]
c = 0 # Counter for the ims array
for i in range(nrow):
for j in range(ncol):
ax= plt.subplot(gs[i,j])
ax.imshow(ims[c])
ax.set_xticklabels([])
ax.set_yticklabels([])
c += 1
for ax in fig.get_axes():
ax.label_outer()

Subplots margins to fit title and labels in matplotlib

I have read tons of answers on similar problems and none has helped.
I just want to increase the margins between the subplots and top, left and bottom edges in order to fit a fig.suptitle and common xlabel and ylabel.
import matplotlib.pyplot as plt
import random
#from matplotlib import rcParams
#rcParams['axes.titlepad'] = 20 # kind of works but shifts all titles, not only suptitle
def dummy(n):
return random.sample(range(1, 100), n)
data = dict()
for i in range(4):
data["area{}".format(i)] = [dummy(10), dummy(10)]
fig, ax = plt.subplots(2, 2, sharex='all', sharey='all', figsize=(10, 10))
fig.suptitle("Common title", fontsize=22)
ax = ax.reshape(-1)
for i, (area, data) in enumerate(data.items()):
ax[i].set_title(area)
ax[i].scatter(data[0], data[1])
fig.text(0.5, 0.01, 'xlabel', ha='center', fontsize=15)
fig.text(0.01, 0.5, 'ylabel', va='center', fontsize=15, rotation=90)
fig.tight_layout() # Does nothing
fig.subplots_adjust(top=0.85) # Does nothing
fig.show()
Matplotlib version 3.0.0
Python 3.6.6
Example plot from shared code
There are many ways to handle this, but I suggest using the gridspec_kw input to plt.subplots for this. The documentation for gridspec_kw is basically just the gridspec.GridSpec documentation. For example:
fig, ax = plt.subplots(2, 2, sharex='all', sharey='all',
gridspec_kw=dict(left=0.1, right=0.9,
bottom=0.1, top=0.9),
figsize=(10, 10))
The left, right, ... inputs to the gridspec_kw specify the extents of the entire group of subplots.
Does that give you what you're looking for?

Adjusting space in-between subplots

I am plotting 4 subplots in one figure, and I want to adjust the space in-between evenly.
I tried grid.GridSpec.update.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure(figsize=(8,8))
gs2 = gridspec.GridSpec(2, 2)
gs2.update(wspace=0.01, hspace=0.01)
ax1 = plt.subplot(gs2[0,0],aspect='equal')
ax1.imshow(img)
ax1.axis('off')
ax2 = plt.subplot(gs2[0,1],aspect='equal')
ax2.imshow(img)
ax2.axis('off')
ax3 = plt.subplot(gs2[1,0],aspect='equal')
ax3.imshow(img)
ax3.axis('off')
ax4 = plt.subplot(gs2[1,1],aspect='equal')
ax4.imshow(img)
ax4.axis('off')
The vertical space in-between 2 plots is too big, and it does not change no matter how I adjust gs2.update(hspace= ), as shown below:
It's likely your aspect='equal' that's causing the problem.
Try this
import numpy as np
%matplotlib inline # if in a jupyter notebook like environment
img = np.ones((30, 30))
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(8,8),
gridspec_kw={'wspace': 0.01, 'hspace': 0.01})
axes = axes.ravel()
for ax in axes:
# aspect : ['auto' | 'equal' | scalar], optional, default: None
ax.imshow(img, aspect='auto')
ax.axis('off')

Python Numpy add hspace between 3D plot and 2D plot with shared axes

Attached is an image showing the current plot. I am setting fig.subplots_adjust(hspace=0) for the 2D plots to share a common x-axis. I would like to add space between the 3D and 2d plots but am not quite sure how to accomplish this as hspace is set to 0.
fig.subplots_adjust(hspace=0)
for ax in [px_t, py_t, pz_t]:
plt.setp(ax.get_xticklabels(), visible=False)
In this case, it's best to use two separate GridSpec instances. That way you can have two separate hspace parameters. Alternatively, you can manually place the top axes.
As an example of the first option:
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
fig = plt.figure(figsize=(8, 10))
gs1 = plt.GridSpec(2, 1, hspace=0.2)
gs2 = plt.GridSpec(8, 1, hspace=0)
ax1 = fig.add_subplot(gs1[0], projection='3d')
ax1.plot(range(10), range(10), range(10))
ax = fig.add_subplot(gs2[4])
lower_axes = [ax]
for i in range(4, 8):
if i > 4:
ax = fig.add_subplot(gs2[i], sharex=lower_axes[0])
ax.plot(range(10))
ax.locator_params(axis='y', nbins=5, prune='both')
lower_axes.append(ax)
for ax in lower_axes:
ax.label_outer()
plt.show()

Categories

Resources