matplotlib 子图的行标题
Posted
技术标签:
【中文标题】matplotlib 子图的行标题【英文标题】:Row titles for matplotlib subplot 【发布时间】:2015-02-10 03:53:35 【问题描述】:在matplotlib中,除了为整个图设置的标题和为每个单独的图设置的标题之外,是否可以为每一行子图设置一个单独的标题?这将对应于下图中的橙色文本。
如果没有,您将如何解决这个问题?在左侧创建一个单独的空子图列并用橙色文本填充它们?
我知道可以使用text()
或annotate()
手动定位每个标题,但这通常需要大量调整,而且我有很多子图。有没有更流畅的解决方案?
【问题讨论】:
【参考方案1】:一个想法是创建三个“大子图”,给每个子图一个标题,并使它们不可见。最重要的是,您可以创建较小的子图矩阵。
这个解决方案完全基于this post,只是更加注重实际去除背景子图。
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()
【讨论】:
是的,我看到了这个答案,但觉得必须一直添加隐藏的大子图会有点烦人。也不确定它是否会影响渲染或文件大小。但是看到你的回答,我想这并不太复杂。我建议进行一些编辑以包含情节标题,并在情节之间为两级标题留出适当的垂直空间。 这很好,但破坏了 sharex 和 sharey 选项。 要关闭所有轴线和刻度,只需执行big_ax.axis('off')
不幸的是大多数 matplotlib 格式化方法看起来像 hack...
伙计们,观看big_ax.set_title()
中的\n
,我花了一个小时试图理解为什么我的所有标题都在一行【参考方案2】:
从 matplotlib 3.4.0 开始,行标题现在可以实现为 subfigure suptitles:
新的
Figure.subfigures
和Figure.add_subfigure
允许在人物中创建虚拟人物 [with] 本地化人物艺术家(例如,颜色条和字幕),这些人物仅适用于每个子人物。
matplotlib 库包含 how to plot subfigures 上的演示。
复制OP的参考图:
使用Figure.subfigures
(更直接)
创建fig.subfigures
(3x1),其中每个subfig
都有自己的subfig.subplots
(1x3) 和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'Subplot row 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')
或使用Figure.add_subfigure
(在现有的pyplot.subplots
上)
如果您已经有一个plt.subplots
图形(3x1),那么将add_subfigures
转换为底层gridspec
。同样,每个subfig
都会有自己的subfig.subplots
(1x3) 和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'Subplot row 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')
任一示例的输出(经过一些样式/格式设置):
【讨论】:
嗨,我收到以下错误 fig = plt.figure(constrained_layout=True) fig.suptitle('Figure title') subfigs = fig.subfigures(nrows=3, ncols=1 ) AttributeError: 'Figure' 对象没有属性 'subfigures'。为什么? @Nachengue 你的matplotlib.__version__
是什么?子图至少需要 3.4.0【参考方案3】:
另一个简单的作弊方法是把中间一栏的标题写成subplot row XX\n\nPlot title No.YY
【讨论】:
它不适用于偶数列【参考方案4】:最好先绘制你的真实子图,然后在它们上面绘制空子图,这样你会有更精确的标题对齐。而要做到这一点,我们需要plt.GridSpec()
(link)。
最好在栏目字幕中看到:
# 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')
更新
为一组子图创建子网格只是为了给它们命名更合乎逻辑和易于理解。 subgrig 为修改提供了浪费的空间:
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 i1')
# 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 i2')
# 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()
原来的问题:
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 i1')
# real subplot #2
ax = fig.add_subplot(gs[1], sharey=ax)
ax.set_title(f'Real i2')
# real subplot #3
ax = fig.add_subplot(gs[2], sharey=ax)
ax.set_title(f'Real i3')
fig.patch.set_facecolor('white')
fig.suptitle('SUPERTITLE', fontweight='bold', size=16)
fig.tight_layout()
【讨论】:
以上是关于matplotlib 子图的行标题的主要内容,如果未能解决你的问题,请参考以下文章
删除 Matplotlib,Python 中某些子图的 y 轴刻度标签