I am trying to add subplots of differing sizes to a particular matplotlib figure, and am unsure of how to do so. In the case of there only being one figure, the "subplot2grid" can be utilized as follows:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = plt.subplot2grid((2, 2), (0, 0), colspan=2)
ax1 = plt.subplot2grid((2, 2), (1, 1))
plt.show()
The above code creates a figure, and adds two subplots to that figure, each with different dimensions. Now, my issue arises in the case of having multiple figures -- I cannot find the appropriate way to add subplots to a particular figure using "subplot2grid." Using the more simple "add_subplot" method, one can add subplots to a particular figure, as seen in the below code:
import matplotlib.pyplot as plt
fig1 = plt.figure()
fig2 = plt.figure()
ax1 = fig1.add_subplot(2, 2, 1)
ax2 = fig1.add_subplot(2, 2, 4)
plt.show()
I am looking for the analogous method for adding subplots of different sizes (preferably using some sort of grid manager, e.g. "subplot2grid") to a particular figure. I have reservations about using the plt."x" style because it operates on the last figure that was created -- my code will have several figures, all of which I will need to have subplots of different sizes.
Thanks in advance,
Curtis M.
In the future (probably the upcoming release?), subplot2grid
will take a fig argument
subplot2grid(shape, loc, rowspan=1, colspan=1, fig=None, **kwargs)
such that the following would be possible:
import matplotlib.pyplot as plt
fig1=plt.figure()
fig2=plt.figure()
ax1 = plt.subplot2grid((2, 2), (0, 0), colspan=2, fig=fig1)
ax2 = plt.subplot2grid((2, 2), (1, 1), fig=fig1)
plt.show()
As of now (version 2.0.2) this is not yet possible. Alternatively, you can manually define the underlying GridSpec
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig1=plt.figure()
fig2=plt.figure()
spec1 = GridSpec(2, 2).new_subplotspec((0,0), colspan=2)
ax1 = fig1.add_subplot(spec1)
spec2 = GridSpec(2, 2).new_subplotspec((1,1))
ax2 = fig1.add_subplot(spec2)
plt.show()
Or you can simply set the current figure, such that plt.subplot2grid will work on that exact figure (as shown in this question)
import matplotlib.pyplot as plt
fig1=plt.figure(1)
fig2=plt.figure(2)
# ... some other stuff
plt.figure(1) # set current figure to fig1
ax1 = plt.subplot2grid((2, 2), (0, 0), colspan=2)
ax2 = plt.subplot2grid((2, 2), (1, 1))
plt.show()
Related
I'm trying to create a figure with a number of non-uniform subplots. I would like to be able to create the plots using an iterable index so that I do not have to create each plot individually.
I can create a series of uniform subplots using fig, ax = plt.subplots(5) where I can plot to the various axes using ax[i].
fig, ax = plt.subplots(5)
Going forward I can plot to each plot using ax[i] using ax[0].plt etc.
However I would like to be able to create a series of plots that looks like:
gridsize = (10,3)
fig = plt.figure(figsize=(5,3))
ax0 = plt.subplot2grid(gridsize, (0, 0), colspan=3, rowspan=1)
for i in range(1,5):
ax1 = plt.subplot2grid(gridsize, (i, 0), colspan=2, rowspan=1)
ax2 = plt.subplot2grid(gridsize, (i, 2), colspan=2, rowspan=1)
where I can call each plot using ax[i] as above.
Does anyone have any ideas? Thanks.
You may append the axes to a list from which to index the respective item or over which to iterate.
import numpy as np
import matplotlib.pyplot as plt
gridsize = (10,3)
fig = plt.figure(figsize=(5,3))
ax0 = plt.subplot2grid(gridsize, (0, 0), colspan=3, rowspan=1)
ax = [ax0]
for i in range(1,5):
ax.append(plt.subplot2grid(gridsize, (i, 0), colspan=2, rowspan=1))
ax.append(plt.subplot2grid(gridsize, (i, 2), colspan=2, rowspan=1))
## Now plot to those axes:
for i in range(2*4+1):
ax[i].plot(np.arange(14),np.random.randn(14))
for axi in ax:
axi.plot(np.arange(14),np.random.randn(14))
plt.show()
This question already has answers here:
Save a subplot in matplotlib
(2 answers)
Closed 5 years ago.
Suppose I have the following code (modified version of matplotlib gridspec tutorial)
import matplotlib.pyplot as plt
def make_ticklabels_invisible(fig):
for i, ax in enumerate(fig.axes):
ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
for tl in ax.get_xticklabels() + ax.get_yticklabels():
tl.set_visible(False)
plt.figure(0)
ax1 = plt.subplot2grid((3,3), (0,0), colspan=3)
ax2 = plt.subplot2grid((3,3), (1,0), colspan=2)
ax3 = plt.subplot2grid((3,3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3,3), (2, 0))
plt.subplot2grid((3,3), (2, 1)) # OOPS! Forgot to store axes object
plt.suptitle("subplot2grid")
make_ticklabels_invisible(plt.gcf())
plt.show()
which results in
How can I 'extract' ax5 and plot it 'full screen' in a separate figure without having to recreate the plot?
I can't find anything in official documentation to back up what I'm saying, but my understanding is that it is impossible to "clone" an existing axes onto a new figure. In fact, no artist (line, text, legend) defined in one axes may be added to another axes. This discussion on Github may explain it to some degree.
For example, attempting to add a line from an axes defined on fig1 to an axes on a different figure fig2 raises an error:
import matplotlib.pyplot as plt
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
line, = ax1.plot([0,1])
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
ax2.add_line(line)
>>>RuntimeError: Can not put single artist in more than one figure`
And attempting to add a line that was drawn in ax1 to a second axes ax2 on the same figure raises an error:
fig1 = plt.figure()
ax1 = fig1.add_subplot(121)
line, = ax1.plot([0,1])
ax12 = fig1.add_subplot(122)
ax12.add_line(line)
>>>ValueError: Can not reset the axes. You are probably trying to re-use an artist in more than one Axes which is not supported
The best recommendation I can make is extract the data from the axes you want to copy, and manually plot that into a new axes object that is sized to your liking. Something like below demonstrates this. Note that this works for Line2D objects plotted via ax.plot. If the data was plotted using ax.scatter, then you need to change things just a little bit and I refer you here for instructions on how to extract data from a scatter.
import matplotlib.pyplot as plt
import numpy as np
def rd(n=5):
# Make random data
return np.sort(np.random.rand(n))
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
# Plot three lines on one axes
ax1.plot(rd(), rd(), rd(), rd(), rd(), rd())
xdata = []
ydata = []
# Iterate thru lines and extract x and y data
for line in ax1.get_lines():
xdata.append( line.get_xdata() )
ydata.append( line.get_ydata() )
# New figure and plot the extracted data
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
for X,Y in zip(xdata,ydata):
ax2.plot(X,Y)
Hope it helps.
I am new to python and having some difficulties with plotting using pyplot. My goal is to plot a grid of plots in-line (%pylab inline) in Juypter Notebook.
I programmed a function plot_CV which plots cross-validation erorr over the degree of polynomial of some x where across plots the degree of penalization (lambda) is supposed to vary. Ultimately there are 10 elements in lambda and they are controlled by the first argument in plot_CV. So
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
ax1 = plot_CV(1,CV_ve=CV_ve)
Gives
Now I think I have to use add_subplot to create a grid of plots as in
fig = plt.figure()
ax1 = fig.add_subplot(2,2,1)
ax1 = plot_CV(1,CV_ve=CV_ve)
ax2 = fig.add_subplot(2,2,2)
ax2 = plot_CV(2,CV_ve=CV_ve)
ax3 = fig.add_subplot(2,2,3)
ax3 = plot_CV(3,CV_ve=CV_ve)
ax4 = fig.add_subplot(2,2,4)
ax4 = plot_CV(4,CV_ve=CV_ve)
plt.show()
If I continue this, however, then the plots get smaller and smaller and start to overlap on the x and y labels. Here a picture with a 3 by 3 plot.
Is there a way to space the plots evenly, so that they do not overlap and make better use of the horizontal and vertical in-line space in Jupyter Notebook? To illustrate this point here a screenshot from jupyter:
Final note: I still need to add a title or annotation with the current level of lambda used in plot_CV.
Edit: Using the tight layout as suggested, gives:
Edit 2: Using the fig.set_figheight and fig.set_figwidth I could finally use the full length and heigth available.
The first suggestion to your problem would be taking a look at the "Tight Layout guide" for matplotlib.
They have an example that looks visually very similar to your situation. As well they have examples and suggestions for taking into consideration axis labels and plot titles.
Furthermore you can control the overall figure size by using Figure from the matplotlib.figure class.
Figure(figsize = (x,y))
figsize: x,y (inches)
EDIT:
Here is an example that I pulled from the matplotlib website and added in the:
fig.set_figheight(15)
fig.set_figwidth(15)
example:
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
def example_plot(ax, fontsize=12):
ax.plot([1, 2])
ax.locator_params(nbins=3)
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
plt.close('all')
fig = plt.figure()
fig.set_figheight(15)
fig.set_figwidth(15)
ax1 = plt.subplot2grid((3, 3), (0, 0))
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
plt.tight_layout()
You can achieve padding of your subplots by using tight_layout this way:
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
That way you can keep your subplots from crowding each other even further.
Have a good one!
Having an example code for a scatter plot along with their histograms
x = np.random.rand(5000,1)
y = np.random.rand(5000,1)
fig = plt.figure(figsize=(7,7))
ax = fig.add_subplot(111)
ax.scatter(x, y, facecolors='none')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
fig1 = plt.figure(figsize=(7,7))
ax1 = fig1.add_subplot(111)
ax1.hist(x, bins=25, fill = None, facecolor='none',
edgecolor='black', linewidth = 1)
fig2 = plt.figure(figsize=(7,7))
ax2 = fig2.add_subplot(111)
ax2.hist(y, bins=25 , fill = None, facecolor='none',
edgecolor='black', linewidth = 1)
What I'm wanting to do is to create this graph with the histograms attached to their respected axis almost like this example
I'm familiar with stacking and merging the x-axis
f, (ax1, ax2, ax3) = plt.subplots(3)
ax1.scatter(x, y)
ax2.hist(x, bins=25, fill = None, facecolor='none',
edgecolor='black', linewidth = 1)
ax3.hist(y, bins=25 , fill = None, facecolor='none',
edgecolor='black', linewidth = 1)
f.subplots_adjust(hspace=0)
plt.setp([a.get_xticklabels() for a in f.axes[:-1]], visible=False)
But I have no idea how to attach the histograms to the y axis and x axis like in the picture I posted above, and on top of that, how to vary the size of the graphs (ie make the scatter plot larger and the histograms smaller in comparison)
Seaborn is the way to go for quick statistical plots. But if you want to avoid another dependency you can use subplot2grid to place the subplots and the keywords sharex and sharey to make sure the axes are synchronized.
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randn(100)
y = np.random.randn(100)
scatter_axes = plt.subplot2grid((3, 3), (1, 0), rowspan=2, colspan=2)
x_hist_axes = plt.subplot2grid((3, 3), (0, 0), colspan=2,
sharex=scatter_axes)
y_hist_axes = plt.subplot2grid((3, 3), (1, 2), rowspan=2,
sharey=scatter_axes)
scatter_axes.plot(x, y, '.')
x_hist_axes.hist(x)
y_hist_axes.hist(y, orientation='horizontal')
You should always look at the matplotlib gallery before asking how to plot something, chances are that it will save you a few keystrokes -- I mean you won't have to ask. There are actually two plots like this in the gallery. Unfortunately the code is old and does not take advantage of subplot2grid, the first one uses rectangles and the second one uses axes_grid, which is a somewhat weird beast. That's why I posted this answer.
I think it's hard to do this solely with matplotlib but you can use seaborn which has jointplot function.
import numpy as np
import pandas as pd
import seaborn as sns
sns.set(color_codes=True)
x = np.random.rand(1000,1)
y = np.random.rand(1000,1)
data = np.column_stack((x,y))
df = pd.DataFrame(data, columns=["x", "y"])
sns.jointplot(x="x", y="y", data=df);
I am currently attempting to use passed axis object created in function, e.g.:
def drawfig_1():
import matplotlib.pyplot as plt
# Create a figure with one axis (ax1)
fig, ax1 = plt.subplots(figsize=(4,2))
# Plot some data
ax1.plot(range(10))
# Return axis object
return ax1
My question is, how can I use the returned axis object, ax1, in another figure? For example, I would like to use it in this manner:
# Setup plots for analysis
fig2 = plt.figure(figsize=(12, 8))
# Set up 2 axes, one for a pixel map, the other for an image
ax_map = plt.subplot2grid((3, 3), (0, 0), rowspan=3)
ax_image = plt.subplot2grid((3, 3), (0, 1), colspan=2, rowspan=3)
# Plot the image
ax_psf.imshow(image, vmin=0.00000001, vmax=0.000001, cmap=cm.gray)
# Plot the map
???? <----- #I don't know how to display my passed axis here...
I've tried statements such as:
ax_map.axes = ax1
and although my script does not crash, my axis comes up empty. Any help would be appreciated!
You are trying to make a plot first and then put that plot as a subplot in another plot (defined by subplot2grid). Unfortunately, that is not possible. Also see this post: How do I include a matplotlib Figure object as subplot?.
You would have to make the subplot first and pass the axis of the subplot to your drawfig_1() function to plot it. Of course, drawfig_1() will need to be modified. e.g:
def drawfig_1(ax1):
ax1.plot(range(10))
return ax1
# Setup plots for analysis
fig2 = plt.figure(figsize=(12, 8))
# Set up 2 axes, one for a pixel map, the other for an image
ax_map = plt.subplot2grid((3, 3), (0, 0), rowspan=3)
ax_image = plt.subplot2grid((3, 3), (0, 1), colspan=2, rowspan=3)
# Plot the image
ax_image.imshow(image, vmin=0.00000001, vmax=0.000001, cmap=cm.gray)
# Plot the map:
drawfig_1(ax_map)