Strange subplot effect with matplotlib - python

I try to plot my data as follows:
First subplot
alpha = ['Joy', 'fear', 'sadness', 'thankful','anger','surprise','love']
fig = pl.figure()
ax = fig.add_subplot(511,title='SGD')
cax = ax.matshow(cm)
fig.colorbar(cax)
ax.set_xticklabels(['']+alpha)
ax.set_yticklabels(['']+alpha)
Second subplot later on with new cm:
ax = fig.add_subplot(521,title='LIBLINEAR')
cax = ax.matshow(cm)
fig.colorbar(cax)
ax.set_xticklabels(['']+alpha)
ax.set_yticklabels(['']+alpha)
Third subplot later on with new cm:
ax = fig.add_subplot(512,title='MNB')
cax = ax.matshow(cm)
fig.colorbar(cax)
ax.set_xticklabels(['']+alpha)
ax.set_yticklabels(['']+alpha)
Fourth subplot later on with new cm
ax = fig.add_subplot(522,title='BNB')
cax = ax.matshow(cm)
fig.colorbar(cax)
ax.set_xticklabels(['']+alpha)
ax.set_yticklabels(['']+alpha)
Last subplot with new cm
ax = fig.add_subplot(532,title='NC')
cax = ax.matshow(cm)
fig.colorbar(cax)
ax.set_xticklabels(['']+alpha)
ax.set_yticklabels(['']+alpha)
pl.show()
I get this:
What am I doing wrong ?

You change the layout of the subplots every time. You use fig.add_subplot(511) which is short for fig.add_subplot(n_rows, n_columns, index). n_rows and n_columns determine the layout of the sub plots in the figure, index the position (starting with 1).
So if you want to have five rows and two columns, you make
ax = fig.add_subplot(5,2,1)
(...)
ax = fig.add_subplot(5,2,2)
(...)
ax = fig.add_subplot(5,2,3)
to plot 1st row, 1st column; 1st row, 2nd column; 2nd row, 1st column etc.
Again, note that fig.add_subplot(5,2,1) and fig.add_subplot(521) are equivalent.

Related

Resizing matplotlib figure modifies padding

I'm trying to create a figure with some supblots.
Each of the subplots has also 2 subplots side by side.
For that I've used the snippet described here (https://stackoverflow.com/a/67694491).
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2, 2)
for outerind, subfig in enumerate(subfigs.flat):
subfig.suptitle(f'Subfig {outerind}')
axs = subfig.subplots(1, 2)
for innerind, ax in enumerate(axs.flat):
ax.set_title(f'outer={outerind}, inner={innerind}', fontsize='small')
ax.set_xticks([])
ax.set_yticks([])
ax.set_aspect(1 / ax.get_data_ratio())
plt.show()
The problems is that my subplots have to be squared, and if I resize the whole figure, the gaps between them and the title increases.
fig = plt.figure(constrained_layout=True,figsize=(10,10))
subfigs = fig.subfigures(2, 2)
for outerind, subfig in enumerate(subfigs.flat):
subfig.suptitle(f'Subfig {outerind}')
axs = subfig.subplots(1, 2)
for innerind, ax in enumerate(axs.flat):
ax.set_title(f'outer={outerind}, inner={innerind}', fontsize='small')
ax.set_xticks([])
ax.set_yticks([])
ax.set_aspect(1 / ax.get_data_ratio())
plt.show()
So, how can I keep the aspect I want but with a greater size?
I think the patchworklib module can help you achieve your purpose (I am the developer of the module).
Please refer to the following code. By changing subplotsize value in the code, you can quickly modify the subplot sizes.
import patchworklib as pw
subfigs = []
pw.param["margin"] = 0.2
subplotsize = (1,1) #Please change the value to suit your purpose.
for i in range(4):
ax1 = pw.Brick(figsize=subplotsize)
ax1.set_xticks([])
ax1.set_yticks([])
ax1.set_title("ax{}_1".format(i+1))
ax2 = pw.Brick(figsize=subplotsize)
ax2.set_xticks([])
ax2.set_yticks([])
ax2.set_title("ax{}_2".format(i+1))
ax12 = ax1|ax2
ax12.case.set_title("Subfig-{}".format(i+1), pad=5)
subfigs.append(ax12)
pw.param["margin"] = 0.5
subfig12 = subfigs[0]|subfigs[1]
subfig34 = subfigs[2]|subfigs[3]
fig = (subfig12/subfig34)
fig.savefig("test.pdf")
If subplotsize is (1,1),
If subplotsize is (3,3),

How to make a nested for loop by plotting two different data sets side by side?

I have these two different data sets (with the same shape) [data1 and data2], and I want to plot the first index of data1 next to the first index of data2:
So figure of data1[0] next to figure of data2[0], figure of data11 next to figure of data21, etc. so 32 plots rather than just 16
I tried doing a nested for loop, but it is only plotting data2, rather than data1 and data2 together. What might be wrong the for loop?
Example and reproducible code is below:
data1 = np.random.rand(16, 20, 15)
data2 = np.random.rand(16,20,15)
fig, axes = plt.subplots(nrows=8, ncols=4, figsize=(10,20))
for i,ax in zip(range(16), axes.ravel()):
for j,ax in zip(range(16),axes.ravel()):
x = ax.contourf(data1[i],levels=10, extend='both')
y = ax.contourf(data2[i],levels=10,extend='both')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid()
plt.tight_layout()
plt.show()
Try with:
fig, axes = plt.subplots(nrows=8, ncols=4, figsize=(10,20))
axes = axes.ravel()
# plot the data in pairs
for i in range(16):
axes[2*i].contourf(data1[i],levels=10, extend='both')
axes[2*i].set_title(f'data1[{i}]')
axes[2*i+1].contourf(data2[i],levels=10,extend='both')
axes[2*i+1].set_title(f'data2[{i}]')
for ax in axes:
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid()
plt.tight_layout()
plt.show()
Output:

Add multiple suptitles [duplicate]

In matplotlib, Is it possible to set a a separate title for each row of subplots in addition to the title set for the entire figure and the title set for each individual plot? This would correspond to the orange text in the figure below.
If not, how would you get around this problem? Create a separate column of empty subplots to the left and fill them with the orange text?
I am aware that it is possible to manually position each single title using text() or annotate(), but that usually requires a lot of tweaking and I have many subplots. Is there a smoother solution?
New in matplotlib 3.4.0
Row titles can now be implemented as subfigure suptitles:
The new subfigure feature allows creating virtual figures within figures with localized artists (e.g., colorbars and suptitles) that only pertain to each subfigure.
See how to plot subfigures for further details.
How to reproduce OP's reference figure:
Either Figure.subfigures (most straightforward)
Create 3x1 fig.subfigures where each subfig gets its own 1x3 subfig.subplots and subfig.suptitle:
fig = plt.figure(constrained_layout=True)
fig.suptitle('Figure title')
# create 3x1 subfigs
subfigs = fig.subfigures(nrows=3, ncols=1)
for row, subfig in enumerate(subfigs):
subfig.suptitle(f'Subfigure title {row}')
# create 1x3 subplots per subfig
axs = subfig.subplots(nrows=1, ncols=3)
for col, ax in enumerate(axs):
ax.plot()
ax.set_title(f'Plot title {col}')
Or Figure.add_subfigure (onto existing subplots)
If you already have 3x1 plt.subplots, then add_subfigure into the underlying gridspec. Again each subfig will get its own 1x3 subfig.subplots and subfig.suptitle:
# create 3x1 subplots
fig, axs = plt.subplots(nrows=3, ncols=1, constrained_layout=True)
fig.suptitle('Figure title')
# clear subplots
for ax in axs:
ax.remove()
# add subfigure per subplot
gridspec = axs[0].get_subplotspec().get_gridspec()
subfigs = [fig.add_subfigure(gs) for gs in gridspec]
for row, subfig in enumerate(subfigs):
subfig.suptitle(f'Subfigure title {row}')
# create 1x3 subplots per subfig
axs = subfig.subplots(nrows=1, ncols=3)
for col, ax in enumerate(axs):
ax.plot()
ax.set_title(f'Plot title {col}')
Output of either example (after some styling):
An idea is to create three "big subplots", to give each of them a title, and make them invisible. On the top of that you can create your matrix of smaller subplots.
This solution is entirely based on this post, except that more attention has been paid to actually removing the background subplot.
import matplotlib.pyplot as plt
fig, big_axes = plt.subplots( figsize=(15.0, 15.0) , nrows=3, ncols=1, sharey=True)
for row, big_ax in enumerate(big_axes, start=1):
big_ax.set_title("Subplot row %s \n" % row, fontsize=16)
# Turn off axis lines and ticks of the big subplot
# obs alpha is 0 in RGBA string!
big_ax.tick_params(labelcolor=(1.,1.,1., 0.0), top='off', bottom='off', left='off', right='off')
# removes the white frame
big_ax._frameon = False
for i in range(1,10):
ax = fig.add_subplot(3,3,i)
ax.set_title('Plot title ' + str(i))
fig.set_facecolor('w')
plt.tight_layout()
plt.show()
Another easy cheat is to give the title of the middle column as subplot row XX\n\nPlot title No.YY
It is better to firstly plot your real subplots and then plot empty subplots above them, thus you will have a more precise title align. And to do it precisely we need plt.GridSpec() (link).
It is best seen in columns subtitles:
# modified code of #snake_chrmer
fig, big_axes = plt.subplots(figsize=(9, 3) , nrows=1, ncols=3, sharey=True)
for title, big_ax in zip(['First', 'Second', 'Third'], big_axes):
big_ax.set_title(f'{title}\n', fontweight='semibold')
big_ax.set_frame_on(False)
big_ax.axis('off')
for i in range(1, 7):
ax = fig.add_subplot(1,6,i)
ax.set_title('Plot title ' + str(i))
fig.set_facecolor('w')
plt.tight_layout()
plt.show()
# my solition
import matplotlib.pyplot as plt
from matplotlib.gridspec import SubplotSpec
def create_subtitle(fig: plt.Figure, grid: SubplotSpec, title: str):
"Sign sets of subplots with title"
row = fig.add_subplot(grid)
# the '\n' is important
row.set_title(f'{title}\n', fontweight='semibold')
# hide subplot
row.set_frame_on(False)
row.axis('off')
rows = 1
cols = 6
fig, axs = plt.subplots(rows, cols, figsize=(9, 3))
for i, ax in enumerate(axs.flatten()):
ax.set_title(f'Plot title {i}')
grid = plt.GridSpec(rows, cols)
create_subtitle(fig, grid[0, 0:2], 'First')
create_subtitle(fig, grid[0, 2:4], 'Second')
create_subtitle(fig, grid[0, 4:6], 'Third')
fig.tight_layout()
fig.set_facecolor('w')
# original problem
rows = 3
cols = 3
fig, axs = plt.subplots(rows, cols, figsize=(9, 9))
for i, ax in enumerate(axs.flatten()):
ax.set_title(f'Plot title {i}')
grid = plt.GridSpec(rows, cols)
create_subtitle(fig, grid[0, ::], 'First')
create_subtitle(fig, grid[1, ::], 'Second')
create_subtitle(fig, grid[2, ::], 'Third')
fig.tight_layout()
fig.set_facecolor('w')
UPD
It is more logical and comprehensible to create subgrid for a set of subplots just to title them. The subgrig gives a wast space for modifications:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
rows = 1
cols = 3
fig = plt.figure(figsize=(9, 3))
# grid for pairs of subplots
grid = plt.GridSpec(rows, cols)
for i in range(rows * cols):
# create fake subplot just to title pair of subplots
fake = fig.add_subplot(grid[i])
# '\n' is important
fake.set_title(f'Fake #{i}\n', fontweight='semibold', size=14)
fake.set_axis_off()
# create subgrid for two subplots without space between them
# <https://matplotlib.org/2.0.2/users/gridspec.html>
gs = gridspec.GridSpecFromSubplotSpec(1, 2, subplot_spec=grid[i], wspace=0)
# real subplot #1
ax = fig.add_subplot(gs[0])
ax.set_title(f'Real {i}1')
# hide ticks and labels
ax.tick_params(left=False, labelleft=False, labelbottom=False, bottom=False)
# real subplot #2
ax = fig.add_subplot(gs[1], sharey=ax)
ax.set_title(f'Real {i}2')
# hide ticks and labels
ax.tick_params(left=False, labelleft=False, labelbottom=False, bottom=False)
fig.patch.set_facecolor('white')
fig.suptitle('SUPERTITLE', fontweight='bold', size=16)
fig.tight_layout()
Original problem:
rows = 3
cols = 1
fig = plt.figure(figsize=(9, 9))
# grid for pairs of subplots
grid = plt.GridSpec(rows, cols)
for i in range(rows * cols):
# create fake subplot just to title set of subplots
fake = fig.add_subplot(grid[i])
# '\n' is important
fake.set_title(f'Fake #{i}\n', fontweight='semibold', size=14)
fake.set_axis_off()
# create subgrid for two subplots without space between them
# <https://matplotlib.org/2.0.2/users/gridspec.html>
gs = gridspec.GridSpecFromSubplotSpec(1, 3, subplot_spec=grid[i])
# real subplot #1
ax = fig.add_subplot(gs[0])
ax.set_title(f'Real {i}1')
# real subplot #2
ax = fig.add_subplot(gs[1], sharey=ax)
ax.set_title(f'Real {i}2')
# real subplot #3
ax = fig.add_subplot(gs[2], sharey=ax)
ax.set_title(f'Real {i}3')
fig.patch.set_facecolor('white')
fig.suptitle('SUPERTITLE', fontweight='bold', size=16)
fig.tight_layout()

How to adjust the plot size in Matplotlib?

I'm trying to remove the white space from the plot that I created:
As it is possible to see, there a big white spot on the right and also on the bottom, how to fix it? Here is my script:
fig = plt.figure(figsize=(7,7))
ax1 = plt.subplot2grid((4,3), (0,0),)
ax2 = plt.subplot2grid((4,3), (1,0),)
ax3 = plt.subplot2grid((4,3), (0,1),)
ax4 = plt.subplot2grid((4,3), (1,1),)
data = self.dframe[i]
tes = print_data(data, self.issues, self.color, self.type_user)
tes.print_top(data=data, top=10, ax=ax1, typegraph="hbar", problem=self.issues[i], tone=self.color[i])
tes.print_top(data=data, top=10, ax=ax2, typegraph="prod_bar", problem=self.issues[i], tone=self.color[i])
tes.print_top(data=data, top=10, ax=ax3, typegraph="reg_hbar", problem=self.issues[i], tone=self.color[i])
tes.print_top(data=data, top=10, ax=ax4, typegraph=self.type_user, problem=self.issues[i], tone=self.color[i])
problem = self.issues[i]
plt.tight_layout()
name = problem + str('.PNG')
plt.close(fig)
fig.savefig(name)
You are creating too many subplots!
If we look at this line:
ax1 = plt.subplot2grid((4,3), (0,0),)
We can see the first argument given to subplot2grid are the dimensions of the subplot grid to be made, in this case 4 rows, and 3 columns. You are then plotting in the subplots in the top left of your figure (the second argument given) which leaves a lot of space that's not used.
So to solve this, reduce the number of subplots by using:
ax1 = plt.subplot2grid((2,2), (0,0),)
Full example:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(25)
fig = plt.figure(figsize=(7,7))
ax1 = plt.subplot2grid((2,2), (0,0),)
ax2 = plt.subplot2grid((2,2), (1,0),)
ax3 = plt.subplot2grid((2,2), (0,1),)
ax4 = plt.subplot2grid((2,2), (1,1),)
ax1.plot(data)
ax2.plot(data)
ax3.plot(data)
ax4.plot(data)
plt.show()
Giving:
you can use
plt.subplots_adjust(left=0.09, bottom=0.07, right=0.98, top=0.97, wspace=0.2 , hspace=0.17 ) to adjust the window.
But the issue is that a lot of the space in your plot is empty
maybe you should change
plt.subplot2grid((4,3)... to plt.subplot2grid((2,2)

2 subplots sharing y-axis (no space between) with single color bar

Does anyone have a matplotlib example of two plots sharing the y-axis (with no space between the plots) with a single color bar pertaining to both subplots? I have not been able to find examples of this yet.
I created the following code based on your question. Personally I do not like it to have no space between the subplots at all. If you do want to change this at some point all you need to do is to replace plt.subplots_adjust(wspace = -.059) with plt.tight_layout().
Hope this helps
import numpy
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
#Random data
data = numpy.random.random((10, 10))
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1, aspect = "equal")
ax2 = fig.add_subplot(1,2,2, aspect = "equal", sharey = ax1) #Share y-axes with subplot 1
#Set y-ticks of subplot 2 invisible
plt.setp(ax2.get_yticklabels(), visible=False)
#Plot data
im1 = ax1.pcolormesh(data)
im2 = ax2.pcolormesh(data)
#Define locations of colorbars for both subplot 1 and 2
divider1 = make_axes_locatable(ax1)
cax1 = divider1.append_axes("right", size="5%", pad=0.05)
divider2 = make_axes_locatable(ax2)
cax2 = divider2.append_axes("right", size="5%", pad=0.05)
#Create and remove the colorbar for the first subplot
cbar1 = fig.colorbar(im1, cax = cax1)
fig.delaxes(fig.axes[2])
#Create second colorbar
cbar2 = fig.colorbar(im2, cax = cax2)
#Adjust the widths between the subplots
plt.subplots_adjust(wspace = -.059)
plt.show()
The result is the following:

Categories

Resources