I have a function named make_plot that takes as an input a list of matrices of length 15 and outputs a plot that is a 3x5 collection of heatmaps (one for each of the matrices in the list).
I would then like to display the output of this function applied to two different lists of matrices, showing the output side-by-side.
I thought this was just a simple application of matplotlib subplots, as discussed here: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html
However, I haven't figured out how to get the output of my custom function make_plot to fit into the subplots that I would like.
For example, the provided code on the matplotlib site for how to make horizontal plots is
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.suptitle('Horizontally stacked subplots')
ax1.plot(x, y)
ax2.plot(x, -y)
But if I try a gentle modification like
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.suptitle('Horizontally stacked subplots')
ax1 = make_plot(mats1)
ax2 = mate_plot(mats2)
I get two empty plots, and then the usual outputs of make_plot stacked on top of each other.
I feel like there is something I'm not really understanding about the nature of matplotlib objects, this seems like it should be a simple / reasonable thing to do, but I haven't figured out how to do it yet. Any pointers would be so appreciated!
Related
I have a collection of charts I need to put on the same pdf, but they are grouped into different subplot grids like below.
Ex. I have something like
fig, axes = plt.subplots(num_rows, num_cols)
fig2, axes2 = plt.subplots(num_rows2, num_cols2)
fig3, axes3 = plt.subplots(num_rows3, num_cols3)
# more figures
I'm trying a find a way of arranging the figures in the the pdf in a custom way, similar to what you can do with subplots,
fig, axis = plt.subplots(rows, cols)
axis[0, 0].plot(X, Y)
axis[0, 1].plot(X2, Y2)
but instead of a grid of axises it would be a grid of figures, and I could tell matplotlib which row or column each figure goes in.
Is there a way to do this? One workaround is to just have one figure and calculate the positions of all the plots manually based on which group there are in, but I'm wondering if there's something built in to matplotlib to do this.
I'm new in both python and stackoverflow... I come from the ggplot2 R background and I am still getting stacked with python. I don't understand why I have a null plot before my figure using matplotlib... I just have a basic pandas series and I want to plot some of the rows in a subplot, and some on the others (however my display is terrible and I don't know why/how to fix it). Thank you in advance!
df = organism_df.T
fig, (ax1,ax2) = plt.subplots(nrows=1,ncols=2,figsize=(5,5))
ax1 = df.iloc[[0,2,3,-1]].plot(kind='bar')
ax1.get_legend().remove()
ax1.set_title('Number of phages/bacteria interacting vs actual DB')
ax2 = df.iloc[[1,4,5,6,7]].plot(kind='bar')
ax2.get_legend().remove()
ax2.set_title('Number of different taxonomies with interactions')
plt.tight_layout()
The method plot from pandas would need the axes given as an argument, e.g., df.plot(ax=ax1, kind='bar'). In your example, first the figure (consisting of ax1 and ax2) is created, then another figure is created by the plot function (at the same time overwriting the original ax1 object) etc.
I would like to have three plots in a single figure. The figure should have a subplot layout of two by two, where the first plot should occupy the first two subplot cells (i.e. the whole first row of plot cells) and the other plots should be positioned underneath the first one in cells 3 and 4.
I know that MATLAB allows this by using the subplot command like so:
subplot(2,2,[1,2]) % the plot will span subplots 1 and 2
Is it also possible in pyplot to have a single axes occupy more than one subplot?
The docstring of pyplot.subplot doesn't talk about it.
Anyone got an easy solution?
You can simply do:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 7, 0.01)
plt.subplot(2, 1, 1)
plt.plot(x, np.sin(x))
plt.subplot(2, 2, 3)
plt.plot(x, np.cos(x))
plt.subplot(2, 2, 4)
plt.plot(x, np.sin(x)*np.cos(x))
i.e., the first plot is really a plot in the upper half (the figure is only divided into 2x1 = 2 cells), and the following two smaller plots are done in a 2x2=4 cell grid.
The third argument to subplot() is the position of the plot inside the grid (in the direction of reading in English, with cell 1 being in the top-left corner):
for example in the second subplot (subplot(2, 2, 3)), the axes will go to the third section of the 2x2 matrix i.e, to the bottom-left corner.
The Using Gridspec to make multi-column/row subplot layouts shows a way to do this with GridSpec. A simplified version of the example with 3 subplots would look like
import matplotlib.pyplot as plt
fig = plt.figure()
gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, :])
plt.show()
To have multiple subplots with an axis occupy, you can simply do:
from matplotlib import pyplot as plt
import numpy as np
b=np.linspace(-np.pi, np.pi, 100)
a1=np.sin(b)
a2=np.cos(b)
a3=a1*a2
plt.subplot(221)
plt.plot(b, a1)
plt.title('sin(x)')
plt.subplot(222)
plt.plot(b, a2)
plt.title('cos(x)')
plt.subplot(212)
plt.plot(b, a3)
plt.title('sin(x)*cos(x)')
plt.show()
Another way is
plt.subplot(222)
plt.plot(b, a1)
plt.title('sin(x)')
plt.subplot(224)
plt.plot(b, a2)
plt.title('cos(x)')
plt.subplot(121)
plt.plot(b, a3)
plt.title('sin(x)*cos(x)')
plt.show()
For finer-grained control you might want to use the subplot2grid module of matplotlib.pyplot.
http://matplotlib.org/users/gridspec.html
A more modern answer would be: Simplest is probably to use subplots_mosaic:
https://matplotlib.org/stable/tutorials/provisional/mosaic.html
import matplotlib.pyplot as plt
import numpy as np
# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig, axd = plt.subplot_mosaic([['left', 'right'],['bottom', 'bottom']],
constrained_layout=True)
axd['left'].plot(x, y, 'C0')
axd['right'].plot(x, y, 'C1')
axd['bottom'].plot(x, y, 'C2')
plt.show()
There are three main options in matplotlib to make separate plots within a figure:
subplot: access the axes array and add subplots
gridspec: control the geometric properties of the underlying figure (demo)
subplots: wraps the first two in a convenient api (demo)
The posts so far have addressed the first two options, but they have not mentioned the third, which is the more modern approach and is based on the first two options. See the specific docs Combining two subplots using subplots and GridSpec.
Update
A much nicer improvement may be the provisional subplot_mosaic method mentioned in #Jody Klymak's post. It uses a structural, visual approach to mapping out subplots instead of confusing array indices. However it is still based on the latter options mentioned above.
I can think of 2 more flexible solutions.
The most flexible way: using subplot_mosaic.
f, axes = plt.subplot_mosaic('AAB;CDD;EEE')
# axes = {'A': ..., 'B': ..., ...}
Effect:
Using gridspec_kw of subplots. Although it is also inconvenient when different rows need different width ratios.
f, (a0, a1) = plt.subplots(1, 2, gridspec_kw={'width_ratios': [2, 1]})
Effect:
The subplot method of other answers is kind of rigid, IMO. For example, you cannot create two rows with width ratios being 1:2 and 2:1 easily. However, it can help when you need to overwrite some layout of subplots, for example.
I have a figure comprised of two x/y curves, a vline and a fill_between in Matplotlib.
My ultimate aim is displaying this figure along with 2 other figures as subplots in a 4th new figure. And I really want to avoid creating all three figures from scratch again just for this new figure with subplots.
So, I'm looking to create a 1x3 figure (subplots, 1 row, 3 columns) like this:
[fig1, fig2, fig3]
I'm almost there. I've so far been able to extract the two x/y curves from the original figure's ax object. Moving through a for loop, I've been able to rebuild most of the three figures as subplots in my new figure:
(ax_a, ax_b, ax_c are ax objects belonging to the three original figures I want to add as subplots to my new figure)
fig = plt.figure(figsize = (16,4))
ax1 = fig.add_subplot(1,3,1)
ax2 = fig.add_subplot(1,3,2)
ax3 = fig.add_subplot(1,3,3)
for ax, ref in zip(fig.axes, [ax_a, ax_b, ax_c]):
for line in ref.lines:
x = line.get_xdata()
y = line.get_ydata()
ax.plot(x,y)
ax.set_xlabel(ref.get_xlabel())
ax.set_ylabel(ref.get_ylabel())
This actually creates a 1x3 grid of my original 3 plots. It's almost perfect.
What's missing are the fill_between component and the vlines component. If I could extract those objects from ax_a, ax_b and ax_c, I'd be done. But I can't find a way to do that.
Is there a way? If not, how would you solve a problem like this?
Thanks so much in advance for any advice offered.
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()