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.
Related
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 have a dataframe in pandas that I'm trying to create two separate plots from in the same function, one is an ordinary boxplot w/ jitter and the other is a violin plot.
I've tried saving them to two separate variables and then saving each of those to their own image files, but in each of those files, the plots seem to contain an overlay of both of them rather than each containing their own separate plot. Here's what the code looks like:
final_boxplot = sns.boxplot(data = df)
final_violin = sns.violinplot(data = df)
final_boxplot.figure.savefig('boxplot.png')
final_violin.figure.savefig('violin.png')
any ideas on what I might be doing wrong, or any alternatives?
You should create different instance of figures and
save:
fig,ax = plt.subplots()
sns.boxplot(data=df, ax=ax)
fig.savefig('boxplot.png')
fig, ax = plt.subplots()
sns.violinplot(data=df, ax=ax)
fig.savefig('violin.png')
I'm very new to Python, and I want to plot 13 different figures all in one plot. To do this nicely, I would like to plot the first 12 figures in a 6x2 grid (this works just fine), and then plot the 13th figure below it; either the same size as the other figures and centered, or larger than the rest so that its width is equal to twice the width of the other figures and all the edges are aligned. What would be the best way to specify axes of this kind using subplots? (So far, I've just used nrows=6, ncols=2, but I think something like that won't work with an odd number of figures to plot.) The code I have so far for plotting the first 12 plots looks like this (with simple test data):
fig, axes = plt.subplots(nrows=6, ncols=2, figsize=(45,10))
for ax in axes.flat:
ax.plot([1,2,3,4])
fig.subplots_adjust(right=0.5)
How can I add a 13th figure below the others?
You can use GridSpec (link to documentation) to generate flexible axes layout.
The following code creates the desired layout and puts all Axes objects in a list for easy access.
gs00 = matplotlib.gridspec.GridSpec(7, 2)
fig = plt.figure()
axs = []
for i in range(6):
for j in range(2):
ax = fig.add_subplot(gs00[i,j])
axs.append(ax)
ax = fig.add_subplot(gs00[6,:])
axs.append(ax)
I would like to plot an orthogonal projection like this one:
using matplotlib, possibly including the 3D subplot. All the subplots should share common axes.
fig = plt.figure()
ax = fig.add_subplot(221, title="XZ")
bx = fig.add_subplot(222, title="YZ", sharey=ax)
cx = fig.add_subplot(223, title="XY", sharex=ax, sharey=[something like bx.Xaxis])
dx = fig.add_subplot(224, title="XYZ", projection="3d", sharex=ax, sharey=bx, sharez=[something like bx.Yaxis]
I can't figure out how to "link" the x-axis of one plot with the y-axis of another. Is there a way to accomplish this?
Late to the party but...
You should be able to accomplish what you want by manually updating one subplot's axis data with the other subplots axis data.
Using the notation from your post, for example, you can match the ylim values of cx with the xlim values of bx using the get and set methods.
cx.set_ylim(bx.get_ylim())
Similarly, you can match tick labels and positions across subplots.
bx_xticks = bx.get_xticks()
bx_xticklabels = [label.get_text() for label in bx.get_xticklabels()]
cx.set_yticks(bx_xticks)
cx.set_yticklabels(bx_xticklabels)
You should be able to define any and all axis attributes and objects dynamically from an already instantiated subplot in this way.
Here is my approach to the problem, which is basically a condensed version of #elebards answer. I just add update limit methods to the axes class, so they get access to the set_xlim / set_ylim methods. Then I connect these functions to the callbacks of the axis I want to synchronize it. When these are called the event argument will be filled with
import types
import matplotlib.pyplot as plt
def sync_y_with_x(self, event):
self.set_xlim(event.get_ylim(), emit=False)
def sync_x_with_y(self, event):
self.set_ylim(event.get_xlim(), emit=False)
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.update_xlim = types.MethodType(sync_y_with_x, ax1)
ax2.update_ylim = types.MethodType(sync_x_with_y, ax2)
ax1.callbacks.connect("ylim_changed", ax2.update_ylim)
ax2.callbacks.connect("xlim_changed", ax1.update_xlim)
I solved1 the problem by exploiting event handlers.
Listening for "*lim_changed" events and then properly get_*lim and set*_lim to synchronise the limits does the trick.
Note you also have to reverse the x-axis in the upper right plot YZ.
Here is a sample function to sync the x-axis with the y-axis:
def sync_x_with_y(self, axis):
# check whether the axes orientation is not coherent
if (axis.get_ylim()[0] > axis.get_ylim()[1]) != (self.get_xlim()[0] > self.get_xlim()[1]):
self.set_xlim(axis.get_ylim()[::-1], emit=False)
else:
self.set_xlim(axis.get_ylim(), emit=False)
I implemented a simple class Orthogonal Projection that make quite easy to make such kind of plots.
1 Starting from a hint that Benjamin Root gave me on matplotlib mailing list almost a year ago...sorry for not having posted the solution before
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.