Matplotlib: can you change the relative height of rows using subplots - python

I have a multi-panel plot that looks like this
Code:
f = plt.figure(figsize=(8, 6))
plt.subplot(211)
plt.subplot(245)
plt.subplot(246)
plt.subplot(247)
plt.subplot(248)
I want to keep this layout, but to make the top row take up a larger proportion of the figure. How would I go about making the top tow take up 2/3 of the figure height and the bottom row take up 1/3 of the figure height?

There are several ways to go about it, most of them are detailed in Customizing Figure Layouts Using GridSpec and Other Functions
Here is a demonstration using GridSpec
gs0 = matplotlib.gridspec.GridSpec(2,4, height_ratios=[2,1])
fig = plt.figure()
ax = fig.add_subplot(gs0[0,:])
ax = fig.add_subplot(gs0[1,0])
ax = fig.add_subplot(gs0[1,1])
ax = fig.add_subplot(gs0[1,2])
ax = fig.add_subplot(gs0[1,3])

Related

How to show multiple already plotted matplotlib figures side-by-side or on-top in Python without re-plotting them?

I have already plotted two figures separately in a single jupyter notebook file, and exported them.
What I want is to show them side by side, but not plot them again by using matplotlib.pyplot.subplots.
For example, in Mathematica, it's easier to do this by just saving the figures into a Variable, and displaying them afterwards.
What I tried was saving the figures, using
fig1, ax1 = plt.subplots(1,1)
... #plotting using ax1.plot()
fig2, ax2 = plt.subplots(1,1)
... #plotting using ax2.plot()
Now, those fig1 or fig2 are of type Matplotlib.figure.figure which stores the figure as an 'image-type' instance. I can even see them separately by calling just fig1 or fig2 in my notebook.
But, I can not show them together as by doing something like
plt.show(fig1, fig2)
It returns nothing since, there wasn't any figures currently being plotted.
You may look at this link or this, which is a Mathematica version of what I was talking about.
assuming u want to merge those subplots in the end.
Here is the code
import numpy as np
import matplotlib.pyplot as plt
#e.x function to plot
x = np.linspace(0, 10)
y = np.exp(x)
#almost your code
figure, axes = plt.subplots(1,1)
res_1, = axes.plot(x,y) #saving the results in a tuple
plt.show()
plt.close(figure)
figure, axes = plt.subplots(1,1)
res_2, = axes.plot(x,-y) #same before
plt.show()
#restructure to merge
figure_2, (axe_1,axe_2) = plt.subplots(1,2) #defining rows and columns
axe_1.plot(res_1.get_data()[0], res_1.get_data()[1]) #using the already generated data
axe_2.plot(res_2.get_data()[0], res_2.get_data()[1])
#if you want show them in one
plt.show()
Not quite sure what you mean with:
but not plot them again by using matplotlib.pyplot.subplots.
But you can display two figures next to each other in a jupyter notebook by using:
fig, ax = plt.subplots(nrows=1, ncols=2)
ax[0] = ... # Code for first figure
ax[1] = ... # Code for second figure
plt.show()
Or above each other:
fig, ax = plt.subplots(nrows=2, ncols=1)
ax[0] = ... # Top figure
ax[1] = ... # Bottom figure
plt.show()

Subplot charts plotted within a loop very squashed

I have a dataframe with ~120 features that I would like to examine by year. I am plotting each feature, x = year, y = feature value within a loop. Whilst these plot successfully, the charts are illegible as they are totally squashed.
I have tried using plt.tight_layout() and adjusting the figure size using plt.rcParams['figure.figsize'] but sadly to no avail
for i in range(len(roll_df.columns)):
plt.subplot(len(roll_df.columns), 1, i+1)
name = roll_df.columns[i]
plt.plot(roll_df[name])
plt.title(name, y=0)
plt.yticks([])
plt.xticks([])
plt.tight_layout()
plt.show()
The loop runs but all plots are so squashed on the y-axis as to become illegible:
Matplotlib will not automatically adjust the size of your figure. So if you add more subplots below each other, it will split the available space instead of extending the figure. That's why your y axes are so narrow.
You could try to define the figure size beforehand, or determine the figure size based on how many subplots you have:
n_plots = roll_df.shape[1]
fig, axes = plt.subplots(n_plots, 1, figsize=(8, 4 * n_plots), tight_layout=True)
# Then your usual part, but plot on the created axes
for i in range(n_plots):
name = roll_df.columns[i]
axes[i].plot(roll_df[name])
axes[i].title(name, y=0)
axes[i].yticks([])
axes[i].xticks([])
plt.show()

Python, Matplotlib custom axes share Y axis

I have simple code to create a figure with 7 axes/ custom subplots (my understanding is that subplots are equal-sized and equal-spaced and in my particular situation I need one to be larger than the rest).
fig = plt.figure(figsize = (16,12))
# row 1
ax1 = plt.axes([0.1,0.7,0.2,0.2])
ax2 = plt.axes([0.4,0.7,0.2,0.2])
ax3 = plt.axes([0.7,0.7,0.2,0.2])
# big row 2
ax4 = plt.axes([0.1, 0.4, 0.5, 0.2])
#row 3
ax5 = plt.axes([0.1,0.1,0.2,0.2])
ax6 = plt.axes([0.4,0.1,0.2,0.2])
ax7 = plt.axes([0.7,0.1,0.2,0.2])
my question is, how do i get all of these axes to share the same y-axis. All i can find on google/stack is for subplots, eg:
ax = plt.subplot(blah, sharey=True)
but calling the same thing for axes creation does not work:
ax = plt.axes([blah], sharey=True) # throws error
is there anyway to accomplish this? What I'm working with is:
This is quite simple using matplotlib.gridspec.GridSpec
gs=GridSpec(3,3) creates a 3x3 grid to place subplots on
For your top and bottom rows, we just need to index one cell on that 3x3 grid (e.g. gs[0,0] is on the top left).
For the middle row, you need to span two columns, so we use gs[1,0:2]
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig=plt.figure(figsize=(16,12))
gs = GridSpec(3,3)
# Top row
ax1=fig.add_subplot(gs[0,0])
ax2=fig.add_subplot(gs[0,1],sharey=ax1)
ax3=fig.add_subplot(gs[0,2],sharey=ax1)
# Middle row
ax4=fig.add_subplot(gs[1,0:2],sharey=ax1)
# Bottom row
ax5=fig.add_subplot(gs[2,0],sharey=ax1)
ax6=fig.add_subplot(gs[2,1],sharey=ax1)
ax7=fig.add_subplot(gs[2,2],sharey=ax1)
ax1.set_ylim(-15,10)
plt.show()

Python: subplots with different total sizes

Original Post
I need to make several subplots with different sizes.
I have simulation areas of the size (x y) 35x6µm to 39x2µm and I want to plot them in one figure. All subplots have the same x-ticklabels (there is a grid line every 5µm on the x-axis).
When I plot the subplots into one figure, then the graphs with the small x-area are streched, so that the x-figuresize is completely used. Therefore, the x-gridlines do not match together anymore.
How can I achieve that the subplots aren't streched anymore and are aligned on the left?
Edit: Here is some code:
size=array([[3983,229],[3933,350],[3854,454],[3750,533],[3500,600]], dtype=np.float)
resolution=array([[1024,256],[1024,320],[1024,448],[1024,512],[1024,640]], dtype=np.float)
aspect_ratios=(resolution[:,0]/resolution[:,1])*(size[:,1]/size[:,0])
number_of_graphs=len(data)
fig, ax=plt.subplots(nrows=number_of_graphs, sharex=xshare)
fig.set_size_inches(12,figheight)
for i in range(number_of_graphs):
temp=np.rot90(np.loadtxt(path+'/'+data[i]))
img=ax[i].imshow(temp,
interpolation="none",
cmap=mapping,
norm=specific_norm,
aspect=aspect_ratios[i]
)
ax[i].set_adjustable('box-forced')
#Here I have to set some ticks and labels....
ax[i].xaxis.set_ticks(np.arange(0,int(size[i,0]),stepwidth_width)*resolution[i,0]/size[i,0])
ax[i].set_xticklabels((np.arange(0, int(size[i,0]), stepwidth_width)))
ax[i].yaxis.set_ticks(np.arange(0,int(size[i,1]),stepwidth_height)*resolution[i,1]/size[i,1])
ax[i].set_yticklabels((np.arange(0, int(size[i,1]), stepwidth_height)))
ax[i].set_title(str(mag[i]))
grid(True)
savefig(path+'/'+name+'all.pdf', bbox_inches='tight', pad_inches=0.05) #saves graph
Here are some examples:
If I plot different matrices in a for loop, the iPhython generates an output which is pretty much what I want. The y-distande between each subplot is constant, and the size of each figure is correct. You can see, that the x-labels match to each other:
When I plot the matrices in one figure using subplots, then this is not the case: The x-ticks do not fit together, and every subplot has the same size on the canvas (which means, that for thin subplots there is more white space reservated on the canvas...).
I simply want the first result from iPython in one output file using subplots.
Using GridSpec
After the community told me to use GridSpec to determine the size of my subplots directly I wrote a code for automatic plotting:
size=array([[3983,229],[3933,350],[3854,454],[3750,533],[3500,600]], dtype=np.float)
#total size of the figure
total_height=int(sum(size[:,1]))
total_width=int(size.max())
#determines steps of ticks
stepwidth_width=500
stepwidth_height=200
fig, ax=plt.subplots(nrows=len(size))
fig.set_size_inches(size.max()/300., total_height/200)
gs = GridSpec(total_height, total_width)
gs.update(left=0, right=0.91, hspace=0.2)
height=0
for i in range (len(size)):
ax[i] = plt.subplot(gs[int(height):int(height+size[i,1]), 0:int(size[i,0])])
temp=np.rot90(np.loadtxt(path+'/'+FFTs[i]))
img=ax[i].imshow(temp,
interpolation="none",
vmin=-100,
vmax=+100,
aspect=aspect_ratios[i],
)
#Some rescaling
ax[i].xaxis.set_ticks(np.arange(0,int(size[i,0]),stepwidth_width)*resolution[i,0]/size[i,0])
ax[i].set_xticklabels((np.arange(0, int(size[i,0]), stepwidth_width)))
ax[i].yaxis.set_ticks(np.arange(0,int(size[i,1]),stepwidth_height)*resolution[i,1]/size[i,1])
ax[i].set_yticklabels((np.arange(0, int(size[i,1]), stepwidth_height)))
ax[i].axvline(antenna[i]) #at the antenna position a vertical line is plotted
grid(True)
#colorbar
cbaxes = fig.add_axes([0.93, 0.2, 0.01, 0.6]) #[left, bottom, width, height]
cbar = plt.colorbar(img, cax = cbaxes, orientation='vertical')
tick_locator = ticker.MaxNLocator(nbins=3)
cbar.locator = tick_locator
cbar.ax.yaxis.set_major_locator(matplotlib.ticker.AutoLocator())
cbar.set_label('Intensity',
#fontsize=12
)
cbar.update_ticks()
height=height+size[i,1]
plt.show()
And here is the result....
Do you have any ideas?
What about using matplotlib.gridspec.GridSpec? Docs.
You could try something like
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
gs = GridSpec(8, 39)
ax1 = plt.subplot(gs[:6, :35])
ax2 = plt.subplot(gs[6:, :])
data1 = np.random.rand(6, 35)
data2 = np.random.rand(2, 39)
ax1.imshow(data1)
ax2.imshow(data2)
plt.show()

Python Subplot function parameters

I am having a hard time with putting in the parameters for the python subplot function.
What I want is to plot 4 graphs on a same image file with the following criteria
left
space
right
space
left
space
right
I have tried different ways of the 3 numbers but the output doesnt show up correctly.
Do you mean something like this?
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(4,2,1)
ax2 = fig.add_subplot(4,2,4)
ax3 = fig.add_subplot(4,2,5)
ax4 = fig.add_subplot(4,2,8)
fig.subplots_adjust(hspace=1)
plt.show()
Well, the not-so-easily-found documentation regarding the sublot function template is as follows:
subplot (number_of_graphs_horizontal, number of graphs_vertical, index)
Let us investigate the code from Joe Kington above:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(4,2,1)
ax2 = fig.add_subplot(4,2,4)
ax3 = fig.add_subplot(4,2,5)
ax4 = fig.add_subplot(4,2,8)
fig.subplots_adjust(hspace=1)
plt.show()
You told matplotlib that you want a grid with 4 rows and 2 columns of graphs. ax1, ax2 and so on are the graphs that you add at the index positions which you can read as the third parameter. You count from left to right in a row-wise manner.
I hope that helped :)
Matplotlib provides several ways deal with the deliberate placement of plots on a single page; i think the best is gridspec, which i believe first appeared in the 1.0 release. The other two, by the way, are (i) directly indexing subplot and (ii) the new ImageGrid toolkit).
GridSpec works like grid-based packers in GUI toolkits used to placed widgets in a parent frame, so for that reason at least, it seems the easiest to use and the most configurable of the three placement techniques.
import numpy as NP
import matplotlib.pyplot as PLT
import matplotlib.gridspec as gridspec
import matplotlib.cm as CM
V = 10 * NP.random.rand(10, 10) # some data to plot
fig = PLT.figure(1, (5., 5.)) # create the top-level container
gs = gridspec.GridSpec(4, 4) # create a GridSpec object
# for the arguments to subplot that are identical across all four subplots,
# to avoid keying them in four times, put them in a dict
# and let subplot unpack them
kx = dict(frameon = False, xticks = [], yticks = [])
ax1 = PLT.subplot(gs[0, 0], **kx)
ax3 = PLT.subplot(gs[2, 0], **kx)
ax2 = PLT.subplot(gs[1, 1], **kx)
ax4 = PLT.subplot(gs[3, 1], **kx)
for itm in [ax1, ax2, ax3, ax4] :
itm.imshow(V, cmap=CM.jet, interpolation='nearest')
PLT.show()
Beyond just arranging the four plots in a 'checkerboard' configuration (per your Question), I have not tried to tune this configuration, but that's easy to do. E.g.,
# to change the space between the cells that hold the plots:
gs1.update(left=.1, right=,1, wspace=.1, hspace=.1)
# to create a grid comprised of varying cell sizes:
gs = gridspec.GridSpec(4, 4, width_ratios=[1, 2], height_ratios=[4, 1])

Categories

Resources