I have a wrapper, plot2d, for the matplotlib function imshow which also calls colorbar (see code below). When I use it on subplots in non-sequential order (e.g. subplot 2 followed by subplot 1) the wrong color bar gets plotted on at least one of the subplot axes. An example of this, is the function bad_cb_on_top, in the below code. However, when I use works_just_fine I get the intended result. The only difference between these two function is the order in which they plot to subplot axes.
My two questions are :
Why does the order in which I plot to subplot axes matter?
How can I get bad_cb_on_top to produce the same results that works_just_fine currently give, without modifying bad_cp_on_top?
Version info:
python 2.7.3
matplotlib 1.1.1
Example code:
from pylab import *
x = linspace(-1, 1, 100)
x, y = meshgrid(x, x)
data = 1./(x**2 + y**2)
def plot2d(data, ax=None, vmax=None):
'''A simple wrapper for implot.'''
# if ax was given, set ax as the current axis for plotting
if ax :
sca(ax)
# Plot the data
im = imshow(data, vmax=vmax, interpolation='lanczos')
# This line assures that ax is the axis which was just plotted on
# even if ax was not specified
ax = im.axes
# Add the colorbar
cb = colorbar(ax=ax, orientation='vertical')
return None
figsize=[4, 7]
def bad_cb_on_top():
# This function copies the color bar from the bottom panel
# to the top panel for some unknown reason.
fig, axs = subplots(2, 1, figsize=figsize)
plot2d(data, vmax=31, ax=axs[1])
plot2d(data, vmax=314, ax=axs[0])
fig.show()
def works_just_fine():
# This function works as intended despite little change
fig, axs = subplots(2, 1, figsize=figsize)
plot2d(data, vmax=314, ax=axs[0])
plot2d(data, vmax=31, ax=axs[1])
fig.show()
bad_cb_on_top()
works_just_fine()
Output from bad_cp_on_top():
Output from works_just_fine()
I could be very wrong, but you may be able to enforce the right one by passing im into colorbar() as the mappable. In plot2d:
cb = colorbar(im, ax=ax, orientation='vertical')
I think that way it specifies not only the axes but the mappable input. FYI, it makes no difference to me (using 1.3.1) so nothing breaks, but it also means I can't test it.
Related
I am using Python/matplotlib to create a figure whereby it has three subplots, each returned from a different 'source' or class method.
For example, I have a script called 'plot_spectra.py' that contains the Spectra() class with method Plot().
So, calling Spectra('filename.ext').Plot() will return a tuple, as per the code below:
# create the plot
fig, ax = plt.subplots()
ax.contour(xx, yy, plane, levels=cl, cmap=cmap)
ax.set_xlim(ppm_1h_0, ppm_1h_1)
ax.set_ylim(ppm_13c_0, ppm_13c_1)
# return the contour plot
return fig, ax
It is my understanding that the 'figure' is the 'window' in matplotlib, and the 'ax' is an individual plot. I would then want to say, plot three of these 'ax' objects in the same figure, but I am struggling to do so because I keep getting an empty window and I think I have misunderstood what each object actually is.
Calling:
hnca, hnca_ax = Spectra('data/HNCA.ucsf', type='sparky').Plot(plane_ppm=resi.N(), vline=resi.H())
plt.subplot(2,2,1)
plt.subplot(hnca_ax)
eucplot, barplot = PlotEucXYIntensity(scores, x='H', y='N')
plt.subplot(2,2,3)
plt.subplot(eucplot)
plt.subplot(2,2,4)
plt.subplot(barplot)
plt.show()
Ultimately, what I am trying to obtain is a single window that looks like this:
Where each plot has been returned from a different function or class method.
What 'object' do I need to return from my functions? And how do I incorporate these three objects into a single figure?
I would suggest this kind of approach, where you specify the ax on which you want to plot in the function:
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
def Spectra(data, ax):
ax.plot(data)
def PlotIntensity(data, ax):
ax.hist(data)
def SeabornScatter(data, ax):
sns.scatterplot(data, data, ax = ax)
spectrum = np.random.random((1000,))
plt.figure()
ax1 = plt.subplot(1,3,1)
Spectra(spectrum, ax1)
ax2 = plt.subplot(1,3,2)
SeabornScatter(spectrum, ax2)
ax3 = plt.subplot(1,3,3)
PlotIntensity(spectrum, ax3)
plt.tight_layout()
plt.show()
You can specify the grid for the subplots in very different ways, and you probably also want to have a look on the gridspec module.
One way to do this is:
f = plt.figure()
gs = f.add_gridspec(2,2)
ax = f.add_subplot(gs[0,:])
Think of the '2,2' as adding 2 row x 2 columns.
On the third line 'gs[0,:]' is telling to add a chart on row 0, all columns. This will create the chart on the top of your top. Note that indices begin with 0 and not with 1.
To add the 'eucplot' you will have to call a different ax on row 1 and column 0:
ax2 = f.add_subplot(gs[1,0])
Lastly, the 'barplot' will go in yet a different ax on row 1, column 1:
ax3 = f.add_subplot(gs[1,1])
See this site here for further reference: Customizing Figure Layouts Using GridSpec and Other Functions
I have searched on google but didn't get an answer. I created a subplot consisting of 2 axes and called plt.gca() but every time it only referred to the last axis in the axes of my subplots. I then started to wonder if it is possible to get a particular axis by passing in some kwargs but didn't find such parameter. I would really like to know how plt.gca() works and why you can't specify which axis to get.
gca means "get current axes".
"Current" here means that it provides a handle to the last active axes. If there is no axes yet, an axes will be created. If you create two subplots, the subplot that is created last is the current one.
There is no such thing as gca(something), because that would translate into "get current axes which is not the current one" - sound unlogical already, doesn't it?
The easiest way to make sure you have a handle to any axes in the plot is to create that handle yourself. E.g.
ax = plt.subplot(121)
ax2 = plt.subplot(122)
You may then use ax or ax2 at any point after that to manipulate the axes of choice.
Also consider using the subplots (note the s) command,
fig, (ax, ax2) = plt.subplots(ncols=2)
If you don't have a handle or forgot to create one, you may get one e.g. via
all_axes = plt.gcf().get_axes()
ax = all_axes[0]
to get the first axes. Since there is no natural order of axes in a plot, this should only be used if no other option is available.
As a supplement to Importance's very fine answer, I thought I would point out the pyplot command sca, which stands for "set current axes".
It takes an axes as an argument and sets it as the current axes, so you still need references to your axes. But the thing about sca that some may find useul is that you can have multiple axes and work on all of them while still using the pyplot interface rather than the object-oriented approach.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.subplot(121)
ax2 = plt.subplot(122)
# Check if ax2 is current axes
print(ax2 is plt.gca())
# >>> True
# Plot on ax2
plt.plot([0,1],[0,1])
plt.xlabel('X')
plt.ylabel('Y')
# Now set ax as current axes
plt.sca(ax)
print(ax2 is plt.gca())
# >>> False
print(ax is plt.gca())
# >>> True
# We can call the exact same commands as we did for ax2, but draw on ax
plt.plot([0,1],[0,1])
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
So you'll notice that we were able to reuse the same code to plot and add labels to both axes.
Actually, I am not clear that
fig_1 = plt.figure()
plt.subplot(2,2,1)
...
Is the ploting like plt.subplot(2,2,1) and other plt. plot on the fig_1 or system will automatically create a new empty figure?
Then how to plot something in a specific figure, for example:
fig_1 = plt.figure()
fig_2 = plt.figure()
plt.subplot(2,2,1)
I want to subplot on fig_2.
You can access a certain figure by e.g.
ax_1_1 = fig_1.add_subplot(2,2,1)
but this has a slightly different syntax (compare plt.subplot() against fig.add_subplot())
So I would recommend to create figures with subplots already prepared vis plt.subplots which returns handles for figure and axes on the fly:
fig_1, axs_1 = plt.subplots(2, 2)
fig_2, axs_2 = plt.subplots(3, 4)
axs_1[0, 0].plot(range(10))
axs_2[2, 3].plot(range(100))
fig_1.suptitle('Figure 1')
fig_2.suptitle('Figure 2')
etc. ...
You can use figure.add_subplot which will return an ax linked to your figure on which you can plot your data. Look at this page to get a global view of the different objects used by matplotlib.
I'm learning to use matplotlib by studying examples, and a lot of examples seem to include a line like the following before creating a single plot...
fig, ax = plt.subplots()
Here are some examples...
Modify tick label text
http://matplotlib.org/examples/pylab_examples/boxplot_demo2.html
I see this function used a lot, even though the example is only attempting to create a single chart. Is there some other advantage? The official demo for subplots() also uses f, ax = subplots when creating a single chart, and it only ever references ax after that. This is the code they use.
# Just a figure and one subplot
f, ax = plt.subplots()
ax.plot(x, y)
ax.set_title('Simple plot')
plt.subplots() is a function that returns a tuple containing a figure and axes object(s). Thus when using fig, ax = plt.subplots() you unpack this tuple into the variables fig and ax. Having fig is useful if you want to change figure-level attributes or save the figure as an image file later (e.g. with fig.savefig('yourfilename.png')). You certainly don't have to use the returned figure object but many people do use it later so it's common to see. Also, all axes objects (the objects that have plotting methods), have a parent figure object anyway, thus:
fig, ax = plt.subplots()
is more concise than this:
fig = plt.figure()
ax = fig.add_subplot(111)
Just a supplement here.
The following question is that what if I want more subplots in the figure?
As mentioned in the Doc, we can use fig = plt.subplots(nrows=2, ncols=2) to set a group of subplots with grid(2,2) in one figure object.
Then as we know, the fig, ax = plt.subplots() returns a tuple, let's try fig, ax1, ax2, ax3, ax4 = plt.subplots(nrows=2, ncols=2) firstly.
ValueError: not enough values to unpack (expected 4, got 2)
It raises a error, but no worry, because we now see that plt.subplots() actually returns a tuple with two elements. The 1st one must be a figure object, and the other one should be a group of subplots objects.
So let's try this again:
fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(nrows=2, ncols=2)
and check the type:
type(fig) #<class 'matplotlib.figure.Figure'>
type(ax1) #<class 'matplotlib.axes._subplots.AxesSubplot'>
Of course, if you use parameters as (nrows=1, ncols=4), then the format should be:
fig, [ax1, ax2, ax3, ax4] = plt.subplots(nrows=1, ncols=4)
So just remember to keep the construction of the list as the same as the subplots grid we set in the figure.
Hope this would be helpful for you.
As a supplement to the question and above answers there is also an important difference between plt.subplots() and plt.subplot(), notice the missing 's' at the end.
One can use plt.subplots() to make all their subplots at once and it returns the figure and axes (plural of axis) of the subplots as a tuple. A figure can be understood as a canvas where you paint your sketch.
# create a subplot with 2 rows and 1 columns
fig, ax = plt.subplots(2,1)
Whereas, you can use plt.subplot() if you want to add the subplots separately. It returns only the axis of one subplot.
fig = plt.figure() # create the canvas for plotting
ax1 = plt.subplot(2,1,1)
# (2,1,1) indicates total number of rows, columns, and figure number respectively
ax2 = plt.subplot(2,1,2)
However, plt.subplots() is preferred because it gives you easier options to directly customize your whole figure
# for example, sharing x-axis, y-axis for all subplots can be specified at once
fig, ax = plt.subplots(2,2, sharex=True, sharey=True)
whereas, with plt.subplot(), one will have to specify individually for each axis which can become cumbersome.
In addition to the answers above, you can check the type of object using type(plt.subplots()) which returns a tuple, on the other hand, type(plt.subplot()) returns matplotlib.axes._subplots.AxesSubplot which you can't unpack.
Using plt.subplots() is popular because it gives you an Axes object and allows you to use the Axes interface to define plots.
The alternative would be to use the global state interface, the plt.plot etc functionality:
import matplotlib.pyplot as plt
# global state version - modifies "current" figure
plt.plot(...)
plt.xlabel(...)
# axes version - modifies explicit axes
ax.plot(...)
ax.set_xlabel(...)
So why do we prefer Axes?
It is refactorable - you can put away some of the code into a function that takes an Axes object, and does not rely on global state
It is easier to transition to a situation with multiple subplots
One consistent/familiar interface instead of switching between two
The only way to access the depth of all features of matplotlib
The global state version was created that way to be easy to use interactively, and to be a familiar interface for Matlab users, but in larger programs and scripts the points outlined here favour using the Axes interface.
There is a matplotlib blog post exploring this topic in more depth: Pyplot vs Object Oriented Interface
It is relatively easy to deal with both worlds. We can for example always ask for the current axes: ax = plt.gca() ("get current axes").
fig.tight_layout()
such a feature is very convenient, if xticks_labels goes out of plot-window, such a line helps to fit xticks_labels & the whole chart to the window, if automatic positioning of chart in plt-window works not correctly. And this code-line works only if you use fig-object in the plt-window
fig, ax = plt.subplots(figsize=(10,8))
myData.plot(ax=ax)
plt.xticks(fontsize=10, rotation=45)
fig.tight_layout()
plt.show()
I'm trying to have an imshow plot inset into another in matplotlib.
I have a figure that has an plot on top and an imshow on the bottom:
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
plt.plot()
ax2 = fig.add_subplot(2,1,2)
plt.imshow()
and then I have another figure, which also is comprised of a plot on top and an imshow below. I want this second figure to be inset into the top plot on the first figure.
I tried follow this example. The code ran without an error but I had an empty axis in the position I wanted it.
My problem is just that I'm not sure where to put the plt.setp() command If that's what I am supposed to use.
First, I don't think you can put a figure into a figure in matplotlib. You will have to arrange your Axes objects (subplots) to achieve the look you want.
The example you provided uses absolute positioning to do that. setp there is not related to positioning, though — it just removes axis ticks from insets. An example of code that does what you want:
import numpy
import matplotlib.pyplot as plt
x = numpy.linspace(0, 1)
xx, yy = numpy.meshgrid(x, x)
im = numpy.sin(xx) + numpy.cos(yy)**2
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(x, x**2)
ax2 = fig.add_subplot(2,1,2)
ax2.imshow(im)
inset1 = fig.add_axes([.15, .72, .15, .15])
inset1.plot(x, x**2)
plt.setp(inset1, xticks=[], yticks=[])
inset2 = fig.add_axes([0.15, 0.55, .15, .15])
inset2.imshow(im)
plt.setp(inset2, xticks=[], yticks=[])
fig.savefig('tt.png')
Here the insets use explicit positioning with coordinates given in "figure units" (the whole figure has size 1 by 1).
Now, of course, there's plenty of room for improvement. You probably want the widths of your plots to be equal, so you'll have to:
specify the positioning of all subplots explicitly; or
play with aspect ratios; or
use two GridSpec objects (this way you'll have the least amount of magic numbers and manual adjustment)