Matplotlib: reorder subplots - python

Say that I have a figure fig which contains two subplots as in the example from the documentation:
I can obtain the two axes (the left one being ax1 and the right one ax2) by just doing:
ax1, ax2 = fig.axes
Now, is it possible to rearrange the subplots? In this example, to swap them?

Sure, as long as you're not going to use subplots_adjust (and therefore tight_layout) after you reposition them (you can use it safely before).
Basically, just do something like:
import matplotlib.pyplot as plt
# Create something similar to your pickled figure......
fig, (ax1, ax2) = plt.subplots(ncols=2)
ax1.plot(range(10), 'r^-')
ax1.set(title='Originally on the left')
ax2.plot(range(10), 'gs-')
ax2.set(title='Originally on the right')
# Now we'll swap their positions after they've been created.
pos1 = ax1.get_position()
ax1.set_position(ax2.get_position())
ax2.set_position(pos1)
plt.show()

Related

How to include the outside legend into the generated file?

I am plotting many lines on several axes, so I have a several fairly busy plots, thus I need to place the legend outside of the figure:
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
fig.tight_layout()
axes[0].legend(loc="upper left", bbox_to_anchor=(1,0,1,1))
which produces something like
However, when I save the figure using fig.savefig("test.png"), I get this:
note the missing legend.
How do I save the figure so that the legend is included?
One option is to tell tight_layout() not to use the full width. That leaves enough room for your legend. I'm not sure if there's a way measure the width of your legend in code, but I experimentally found this fits your legend:
import matplotlib.pyplot as plt
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
fig.tight_layout(rect=(0, 0, 0.84, 1))
axes[0].legend(loc="upper left", bbox_to_anchor=(1,0,1,1))
fig.savefig("test.png")
After some experimentation, though, it seems like simplifying the call to legend() tells tight_layout() about the legend and to leave room for it. Now, making the names longer automatically makes the plots smaller so that everything fits.
There was a problem with tight_layout() leaving gaps between subplots, because the legend was taller than the subplot. We put a single entry in the legend, call tight_layout(), then put all the entries in the legend. The legend extends below the bottom of the first subplot, but that's what we want.
If the names are different lengths, you'd have to do some more trickery to use the longest name instead of the first name or split all the entries across all the subplots before calling tight_layout().
import matplotlib.pyplot as plt
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
# Create a legend with only one entry, so tight_layout doesn't stretch down.
handles, labels = axes[0].get_legend_handles_labels()
axes[0].legend(handles[:1], labels[:1], bbox_to_anchor=(1, 1))
fig.tight_layout()
# Use all the entries without worrying about expanding below the subplot.
axes[0].legend(handles, labels, bbox_to_anchor=(1, 1))
fig.savefig("test.png")
Use plt.subplots_adjust and you can customize the space around the figure. Here I just used plt.subplots_adjust(right=0.8) but you can adjust all the settings (including top, bottom, left, hspace, wspace)
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
fig.tight_layout()
axes[0].legend(loc="upper left", bbox_to_anchor=(1,0,1,1))
fig.subplots_adjust(right=0.80)
fig.savefig("test.png")
Saved image:

Move ticks and labels to the top of a pyplot figure

As per this question, moving the xticks and labels of an AxesSubplot object can be done with ax.xaxis.tick_top(). However, I cannot get this to work with multiple axes inside a figure.
Essentially, I want to move the xticks to the very top of the figure (only displayed at the top for the subplots in the first row).
Here's a silly example of what I'm trying to do:
fig, axs = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
fig.set_figheight(5)
fig.set_figwidth(10)
for ax in axs.flatten():
ax.xaxis.tick_top()
plt.show()
Which shows
My desired result is this same figure but with the xticks and xticklabels at the top of the two plots in the first row.
Credits to #BigBen for the sharex comment. It is indeed what's preventing tick_top to work.
To get your results, you can combine using tick_top for the two top plots and use tick_params for the bottom two:
fig, axs = plt.subplots(2, 2, sharex=False) # Do not share xaxis
for ax in axs.flatten()[0:2]:
ax.xaxis.tick_top()
for ax in axs.flatten()[2:]:
ax.tick_params(axis='x',which='both',labelbottom=False)
See a live implementation here.

Put shared axis labels to upper plot

from matplotlib import pyplot as plt
fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
fig.show()
Returns this figure:
But I want the x-axis labels below the first plot, not the second, like shown below. How can I achieve this?
There is an example in the official reference, so I answered it by referring to it: In the tick parameter, set the bottom label to false.
import matplotlib.pyplot as plt
ax0 = plt.subplot(211)
ax1 = plt.subplot(212, sharex=ax0, sharey=ax0)
#plt.plot([],[])
plt.tick_params('x', labelbottom=False)
#print(ax1.get_xticks())
plt.show()
The answer from #r-beginners brought me to a solution that also works when using the plt.subplots shortcut instead of instantiating each axis separately.
from matplotlib import pyplot as plt
fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
plt.tick_params('x', labelbottom=False, labeltop=True)
fig.show()
he essential part is plt.tick_params which take keyword arguments labeltop or labelbottom (as well as labelleft or labelright for shared axis on several columns) to select / deselect each axis individually.

how to make Y-label of subplots to appear only once?

I have a simple problem i need to solve, here is the example
import matplotlib.pyplot as plt
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(6, 6))
for axs in ax.flat:
axs.set(ylabel='AUC')
this is the output
I want Y-label(AUC) to appear only once(be shared) at the first subplot, and other values should remain. This is the desired output
How to solve this? Please I need your help
Since you're setting your labels in a loop, you're labeling all the axes in your subplots accordingly. What you need is to only label the first cell in your subplot row.
So this:
for axs in ax.flat:
axs.set(ylabel='AUC')
changes to:
ax[0].set_ylabel("AUC")
I also recommend you to share the axis between your multiple subplots, since all the yticks are making your plot a little less readable than ideal. You can change it as below:
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(6, 6), sharex=True, sharey=True,)
The resulting image will be:

matplotlib share x axis but don't show x axis tick labels for both, just one

I'm using python + matplotlib and I'm having two plots share an axis. If you try to set graph1.set_xticklabels([]) while sharing an axis, it has no effect because it is shared. Is there a way to share the axis AND be able to hide the x axis of one plot?
This is a common gotcha when using shared axes.
Fortunately, there's a simple fix: use plt.setp(ax.get_xticklabels(), visible=False) to make the labels invisible on just one axis.
This is equivalent to [label.set_visible(False) for label in ax.get_xticklabels()], for whatever it's worth. setp will automatically operate on an iterable of matplotlib objects, as well as individual objects.
As an example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(range(10), 'b-')
ax2 = fig.add_subplot(2,1,2, sharex=ax1)
ax2.plot(range(10), 'r-')
plt.setp(ax1.get_xticklabels(), visible=False)
plt.show()
Per a thread on matplotlib-users, you could use
import matplotlib.pyplot as plt
for ax in plt.gcf().axes:
try:
ax.label_outer()
except:
pass
You could use Axes.tick_params():
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212, sharex=ax1)
ax1.tick_params(labelbottom=False)
You can share the axes during subplot creation with plt.subplots as
fig, axes = plt.subplots(nrows=2, sharex=True)
This will automatically turn the ticklabels for inner axes off.
Complete example:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, sharex=True)
axes[0].plot([1,2,3])
axes[1].plot([3,2,1])
plt.show()
Unfortunately, I am not allowed to comment on esmit's answer (which is the best solution in my opinion, thanks esmit), so I have to write my comment as a new answer: I put his solution into a simple function
def remove_inner_ticklabels(fig):
for ax in fig.axes:
try:
ax.label_outer()
except:
pass
which you can call before plt.show(). Joe Kington's answer did not work for me for some reason.

Categories

Resources