pyplot common axes labels for subplots
Solution 1
You can create a big subplot that covers the two subplots and then set the common labels.
import random
import matplotlib.pyplot as plt
x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]
fig = plt.figure()
ax = fig.add_subplot(111) # The big subplot
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
# Turn off axis lines and ticks of the big subplot
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.tick_params(labelcolor='w', top=False, bottom=False, left=False, right=False)
ax1.loglog(x, y1)
ax2.loglog(x, y2)
# Set common labels
ax.set_xlabel('common xlabel')
ax.set_ylabel('common ylabel')
ax1.set_title('ax1 title')
ax2.set_title('ax2 title')
plt.savefig('common_labels.png', dpi=300)
Another way is using fig.text() to set the locations of the common labels directly.
import random
import matplotlib.pyplot as plt
x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.loglog(x, y1)
ax2.loglog(x, y2)
# Set common labels
fig.text(0.5, 0.04, 'common xlabel', ha='center', va='center')
fig.text(0.06, 0.5, 'common ylabel', ha='center', va='center', rotation='vertical')
ax1.set_title('ax1 title')
ax2.set_title('ax2 title')
plt.savefig('common_labels_text.png', dpi=300)
Solution 2
One simple way using subplots
:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(3, 4, sharex=True, sharey=True)
# add a big axes, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axes
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.grid(False)
plt.xlabel("common X")
plt.ylabel("common Y")
Solution 3
plt.setp()
will do the job:
# plot something
fig, axs = plt.subplots(3,3, figsize=(15, 8), sharex=True, sharey=True)
for i, ax in enumerate(axs.flat):
ax.scatter(*np.random.normal(size=(2,200)))
ax.set_title(f'Title {i}')
# set labels
plt.setp(axs[-1, :], xlabel='x axis label')
plt.setp(axs[:, 0], ylabel='y axis label')
Solution 4
New in matplotlib 3.4.0
There are now built-in methods to set common axis labels:
To reproduce OP's loglog
plots (common labels but separate titles):
x = np.arange(0.01, 10.01, 0.01)
y = 2 ** x
fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True)
ax1.loglog(y, x)
ax2.loglog(x, y)
# separate subplot titles
ax1.set_title('ax1.title')
ax2.set_title('ax2.title')
# common axis labels
fig.supxlabel('fig.supxlabel')
fig.supylabel('fig.supylabel')
Solution 5
Wen-wei Liao's answer is good if you are not trying to export vector graphics or that you have set up your matplotlib backends to ignore colorless axes; otherwise the hidden axes would show up in the exported graphic.
My answer suplabel
here is similar to the fig.suptitle
which uses the fig.text
function. Therefore there is no axes artist being created and made colorless.
However, if you try to call it multiple times you will get text added on top of each other (as fig.suptitle
does too). Wen-wei Liao's answer doesn't, because fig.add_subplot(111)
will return the same Axes object if it is already created.
My function can also be called after the plots have been created.
def suplabel(axis,label,label_prop=None,
labelpad=5,
ha='center',va='center'):
''' Add super ylabel or xlabel to the figure
Similar to matplotlib.suptitle
axis - string: "x" or "y"
label - string
label_prop - keyword dictionary for Text
labelpad - padding from the axis (default: 5)
ha - horizontal alignment (default: "center")
va - vertical alignment (default: "center")
'''
fig = pylab.gcf()
xmin = []
ymin = []
for ax in fig.axes:
xmin.append(ax.get_position().xmin)
ymin.append(ax.get_position().ymin)
xmin,ymin = min(xmin),min(ymin)
dpi = fig.dpi
if axis.lower() == "y":
rotation=90.
x = xmin-float(labelpad)/dpi
y = 0.5
elif axis.lower() == 'x':
rotation = 0.
x = 0.5
y = ymin - float(labelpad)/dpi
else:
raise Exception("Unexpected axis: x or y")
if label_prop is None:
label_prop = dict()
pylab.text(x,y,label,rotation=rotation,
transform=fig.transFigure,
ha=ha,va=va,
**label_prop)
Related videos on Youtube
farqwag25
Updated on January 16, 2022Comments
-
farqwag25 over 2 years
I have the following plot:
import matplotlib.pyplot as plt fig2 = plt.figure() ax3 = fig2.add_subplot(2,1,1) ax4 = fig2.add_subplot(2,1,2) ax4.loglog(x1, y1) ax3.loglog(x2, y2) ax3.set_ylabel('hello')
I want to be able to create axes labels and titles not just for each of the two subplots, but also common labels that span both subplots. For example, since both plots have identical axes, I only need one set of x and y- axes labels. I do want different titles for each subplot though.
I tried a few things but none of them worked right
-
PhML about 11 yearsThe suptitle function use the fig.text() version. So this might be the "official" way to do it ?
-
1'' over 9 yearsIt's worth emphasizing that
ax
has to be created beforeax1
andax2
, otherwise the big plot will cover up the small plots. -
Næreen over 6 yearsax.grid(False) or plt.grid(False) is also needed if the global plotting parameters include a (visible) grid.
-
Næreen over 6 yearsax.grid(False) or plt.grid(False) is also needed if the global plotting parameters include a (visible) grid.
-
M. Toya over 6 yearsIt seems that the first approach does not work anymore with recent versions of matplotplib (I use 2.0.2): labels added to the enclosing axe are not visible.
-
asmeurer over 6 years
labelcolor='none'
doesn't disable the labels for pgf output at least. You can make it work withlabelcolor=(1, 1, 1)
(if the background is white). But I guess they are still there, like if you select them in the output you can copy them. -
ZYX about 6 yearsAdding
which="both"
argument to theplt.tick_params()
line helped me remove the major tick marks still remaining -
Fardin Abdi almost 6 yearsHow to add y_labels to each individual subplot?
-
Arthur Dent almost 6 yearsThis is the best answer imo. It's easy to implement and the labels don't overlap because of the labelpad option.
-
dankal444 over 5 yearsIn case you need both, common label and subplots labels, you may add some spacing between them so that they not overlap.
plt.ylabel("Common Y", labelpad=20)
-
Kareem Jeiroudi about 5 yearsThanks!
fig.text(0.06, 0.5, 'common ylabel', ha='center', va='center', rotation='vertical')
worked for me. -
Evidlo about 5 yearsI'm doing this for a (5, 1) subplot and my ylabel is way off at the left edge of the window instead of near the subplots.
-
Kareem Jeiroudi about 5 yearsYou got an upvote. but please always explain what the code is doing, attach an image or show an example, because it definitely took a bit of time to get it.
-
pcko1 almost 5 years
sharey=True, sharex=True
are not really needed, otherwise a neat answer! -
Ted almost 5 yearsChange
'off'
toFalse
with newer versions of Matplotlib (I have 2.2.2) -
usernumber over 4 yearsAnd then how do you add the plots?
for ax in axes: ax.plot(x, y)
doesn't seem to do any good. -
yngve over 4 yearsThis is a good answer, note also the optional labelpad to in particular the ylabel command. This is needed for example when the subplots are logscale to move the label a bit away from the axis. Similarly you can give a negative number if you feel it is placed too far away from the axes
-
pfabri almost 4 yearsIs there a way to also set the font size/weight with this method?
-
Manuel F over 3 years@pfabri ...
plt.setp(axs[-1, :], xlabel='x axis label')
only modifies the label text (apparently). if you want to customize its text parameters do, for instance,ax.set_xlabel(None, size=12, weight='demibold', color='xkcd:lime green', labelpad=0.33)
inside the for loop. check matplotlib.text.Text for more**kwargs
. -
Surya Narayanan almost 3 yearsI got some overlapping x ticks from the (111) subplot and my other subplots. I used plt.tick_params(labelcolor="none", bottom=False, left=False) after fig.add_subplot(111, frame_on=False) from kite.com/python/answers/…
-
vanPelt2 over 2 yearsVery excited about this new functionality, thank you for revisiting this answer and pointing it out!
-
Mead over 2 yearsThe first solution worked for me, but I needed
labelcolor='none'
rather thanlabelcolor='w'
inax.tick_params(...)
otherwise I saw residuals from the big plot.