Re-combine different axes to make new figure in matplotlib - python

I am curious that whether this is possible in matplotlib:
I first create some figures, with subplots.
import matplotlib.pyplot as plt
fig1, axs1 = plt.subplots(2, 2)
fig2, axs2 = plt.subplots(2, 2)
And then, could I recombine them, so fig3 is composed of the first row in fig1 (i.e., axs1[0, 0] and axs1[0, 1]) and second row in fig2 (i.e., axs2[1, 0] and axs2[1, 1])?
Currently, all I could do is to re-plot them. I am curious about whether there is a way that I can just move axes around and re-combine them to make new figures. Thanks!
-Shawn

Related

How would I create multiple subplots in the same subplot in a while loop?: Python [duplicate]

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.

Add multiple axes from different sources into same figure

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

Subplots Frequency Plots

I've been struggling to generate the frequency plot of 2 columns named "Country" and "Company" in my DataFrame and show them as 2 subplots. Here's what I've got.
Figure1 = plt.figure(1)
Subplot1 = Figure1.add_subplot(2,1,1)
and here I'm going to use the bar chart pd.value_counts(DataFrame['Country']).plot('barh')
to shows as first subplot.
The problem is, I cant just go: Subplot1.pd.value_counts(DataFrame['Country']).plot('barh') as Subplot1. has no attribute pd. ~ Could anybody shed some light in to this?
Thanks a million in advance for your tips,
R.
You don't have to create Figure and Axes objects separately, and you should probably avoid initial caps in variable names, to differentiate them from classes.
Here, you can use plt.subplots, which creates a Figure and a number of Axes and binds them together. Then, you can just pass the Axes objects to the plot method of pandas:
from matplotlib import pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
pd.value_counts(df['Country']).plot('barh', ax=ax1)
pd.value_counts(df['Company']).plot('barh', ax=ax2)
Pandas' plot method can take in a Matplotlib axes object and direct the resulting plot into that subplot.
# If you want a two plots, one above the other.
nrows = 2
ncols = 1
# Here axes contains 2 objects representing the two subplots
fig, axes = plt.subplots(nrows, ncols, figsize=(8, 4))
# Below, "my_data_frame" is the name of your Pandas dataframe.
# Change it accordingly for the code to work.
# Plot first subplot
# This counts the number of times each country appears and plot
# that as a bar char in the first subplot represented by axes[0].
my_data_frame['Country'].value_counts().plot('barh', ax=axes[0])
# Plot second subplot
my_data_frame['Company'].value_counts().plot('barh', ax=axes[1])

Pylab - Adjust hspace for some of the subplots

I have a plot in which I want to have one panel separate from other four panels. I want the rest of the four panels to share the x axis. The figure is shown below. I want the bottom four panels to have shared x-axis. I tried
f = plt.figure()
ax6=f.add_subplot(511)
ax4=f.add_subplot(515)
ax1=f.add_subplot(512,sharex=ax4)
ax2=f.add_subplot(513,sharex=ax4)
ax3=f.add_subplot(514,sharex=ax4)
However, that does not work for me. The attached figure is made with
f = plt.figure()
ax6=f.add_subplot(511)
ax4=f.add_subplot(515)
ax1=f.add_subplot(512)
ax2=f.add_subplot(513)
ax3=f.add_subplot(514)
and then setting the xticks to none by
ax1.get_xaxis().set_ticklabels([])
ax2.get_xaxis().set_ticklabels([])
ax3.get_xaxis().set_ticklabels([])
using f.subplots_adjust(hspace=0) joins all the subplots. Is there a way to join only the bottom four panels?
Thanks!
It's easiest to use two separate gridspec objects for this. That way you can have independent margins, padding, etc for different groups of subplots.
As a quick example:
import numpy as np
import matplotlib.pyplot as plt
# We'll use two separate gridspecs to have different margins, hspace, etc
gs_top = plt.GridSpec(5, 1, top=0.95)
gs_base = plt.GridSpec(5, 1, hspace=0)
fig = plt.figure()
# Top (unshared) axes
topax = fig.add_subplot(gs_top[0,:])
topax.plot(np.random.normal(0, 1, 1000).cumsum())
# The four shared axes
ax = fig.add_subplot(gs_base[1,:]) # Need to create the first one to share...
other_axes = [fig.add_subplot(gs_base[i,:], sharex=ax) for i in range(2, 5)]
bottom_axes = [ax] + other_axes
# Hide shared x-tick labels
for ax in bottom_axes[:-1]:
plt.setp(ax.get_xticklabels(), visible=False)
# Plot variable amounts of data to demonstrate shared axes
for ax in bottom_axes:
data = np.random.normal(0, 1, np.random.randint(10, 500)).cumsum()
ax.plot(data)
ax.margins(0.05)
plt.show()

Python: Parallel coordinates subplots in subplot

I saw this example on how to create a parallel coordinate plot: Parallel Coordinates:
This creates a nice Parallel Coordinates figure, but I would like to add this plot to an already existing figure in a subplot (there should be another plot next to it in the same plot).
For the already existing figure, the figure and axes are defined as:
fig = plt.figure(figsize=plt.figaspect(2.))
ax = fig.add_subplot(1,2,1)
For the Parallel Coordinates, they suggest:
fig, axes = plt.subplots(1, dims-1, sharey=False)
How can I reconcile both initializations of the figure and the ax(es)?
One option is to create all the axes using subplots then just shift the location of the one that you don't want to have wspace=0 as is done for the Parallel Coordinate plots:
import matplotlib.pylab as plt
dims = 4
fig, axes = plt.subplots(1, dims-1 + 1, sharey=False)
plt.subplots_adjust(wspace=0)
ax1 = axes[0]
pos = ax1.get_position()
ax1.set_position(pos.translated(tx = -0.1,ty=0))
I have added 1 to the number of columns creates (leaving it explicitly -1+1) and set wspace=0 which draws all the plots adjacent to one another with no space inbetween. Take the left most axes and get the position which is a Bbox. This is nice as it gives you the ability to translate it by tx=-0.1 separating your existing figure.

Categories