自动获取子图的坐标,以便将它们设置为图例的自动定位
Get automatically coordinates of subplots in order to set them for automatic positioning of legend
我第一次尝试手动设置Getdist tool制作的主要情节的主要图例的位置。
下图表示来自具有联合分布的协方差矩阵的 1/2 西格玛置信水平。它由 Getdist tool.
制作
生成此图的主要例程是:
# g.settings
g = plots.get_subplot_plotter()
g.settings.figure_legend_frame = True
g.settings.legend_fontsize = 21
g.triangle_plot([matrix1, matrix2],
names,
filled = True,
contour_colors = ['darkblue','red'],
line_args = [{'lw':2, 'color':'darkblue'},
{'lw':2, 'color':'red'}]
)
g.add_legend(['Opt. Flat. No Gamma. - cross - standard situation - Criterion taking into accound a = 200',\
'Pess. Flat. No Gamma. - cross - standard situation - Criterion taking into account a = 300' ],\
bbox_to_anchor = [1.5, 8.5])
数值1.5
好像对应图例的x-coordinate(宽度)8.5
对应图例的y-coordinate(高度)
现在,我想自动执行此过程,而不是每次都手动设置图例的位置。
我希望图例的右上角位于第一个左上角框的顶部边框(正好在“1sigma ± 0.0012”标题下方的顶部边框水平)。
我还希望将图例推到图的右侧(直到图右下框的右边框:由 sigma8
"1sigma ± 0.001" title
标识;注意:我希望它位于 1.0 和 0.0 xticks 之前,就在右线边框的 x-coordinate 处)。
这里是我尝试获取左上框顶部边框的全局坐标(整个图)的内容:
# First, get y coordinates of top border for first Likelihood
box1 = g.subplots[0,0]
box1_coords = box1._position.bounds
print('box1_coords = ', box1_coords)
我在执行时得到以下值:
box1_coords = (0.125, 0.7860975609756098, 0.09451219512195125, 0.09390243902439022)
如您所见,这些值似乎是标准化的,所以如果我想将这些值插入到其中,我不知道如何处理:
bbox_to_anchor = [box1_coords[0], box1_coords[1]]
正如预期的那样,这行代码产生了错误的图例位置。
那么,我如何设法自动为 bbox_to_anchor 分配好的值以获得我想要的(y-coordinate 在由 [=23= 标识的左上框的顶部边框级别) ]) 并在右侧向上推到右下框的右边界(x-coordinate 由 sigma8 用 "1sigma ± 0.001" title
识别)?
更新 1
我尝试根据我的情况调整它们,但问题仍然存在。这是我所做的:
# g.settings
g = plots.get_subplot_plotter()
# get the max y position of the top left axis
top_left_plot = g.subplots[0,0].axes.get_position().ymax
# get the max x position of the bottom right axis
# it is -1 to reference the last plot
bottom_right_plot = g.subplots[-1,-1].axes.get_position().xmax
不知道为什么top_left_plot
和bottom_right_plot
的值不是很好
我认为 subplots[0,0]
(对于图例的顶部 y-coordinate
)指的是左上角的子图,subplots[-1,-1]
指的是右下角的子图(对于图例的右侧 x-coordinate
传说)但考虑到这一点,它不起作用。
例如:
# g.settings
g = plots.get_subplot_plotter()
# Call triplot
g.triangle_plot([matrix1, matrix2],
names,
filled = True,
legend_labels = [],
contour_colors = ['darkblue','red'],
line_args = [{'lw':2, 'color':'darkblue'},
{'lw':2, 'color':'red'}])
g.add_legend(['Opt. Flat. No Gamma. - cross - standard situation - Criterion taking into accound a = 200',
'Pess. Flat. No Gamma. - cross - standard situation - Criterion taking into account a = 300'],
legend_loc='upper right',
bbox_to_anchor=(bottom_right_plot, top_left_plot)
)
我得到:
legend_coords y_max, x_max 0.88 0.9000000000000001
我不明白为什么 g.add_legend
没有考虑这些值(似乎介于 0.0 和 1.0 之间)。
用@mullinscr
的解,得到下图:
如果我通过强制获取图例位置的坐标:
top_left_plot = 8.3
bottom_right_plot = 1.0
这看起来像这个 post 的第一张图。但是这两个值并没有像它应该的那样包含在 0.0 和 1.0 之间。
更新 2
@mullinscr
,谢谢,我关注了你的更新,但总是遇到问题。如果我直接在我的脚本中应用相同的代码片段,即:
g.add_legend(['An example legend - item 1'],
legend_loc='upper right', # we want to specify the location of this point
bbox_to_anchor=(bottom_right_plot, top_left_plot),
bbox_transform=plt.gcf().transFigure, # this is the x and y co-ords we extracted above
borderaxespad=0, # this means there is no padding around the legend
edgecolor='black')
然后我得到下图:
如您所见,坐标并非真正预期的那样:x-coordinate
和 y-coordinate
存在轻微偏移。
如果我将您的代码片段应用到我的图例文本中,我得到:
我给你我整个脚本的 link,与预期的相比,这可能会让你更容易看到错误:
基本上按照您描述的方式工作。轴的 bboxes (xmin, ymin, width, height)
以图中的分数给出,plt.legend()
使用相同的格式,因此两者兼容。通过将图例的 upper right
角设置为最外侧轴定义的角,您可以获得干净的布局,而不必担心图例的确切大小。
import matplotlib.pyplot as plt
n = 4
# Create the subplot grid
# Alternative: fig, ax = plt.subplots(n, n); ax[i, j].remove() for j > i
fig = plt.figure()
gs = fig.add_gridspec(nrows=n, ncols=n)
ax = np.zeros((n, n), dtype=object)
for i in range(n):
for j in range(n):
if j <= i:
ax[i, j] = fig.add_subplot(gs[i, j])
# add this to make the position of the legend easier to spot
ax[0, -1] = fig.add_subplot(gs[0, -1])
# Plot some dummy data
ax[0, 0].plot(range(10), 'b-o', label='Dummy Label 4x4')
# Set the legend
y_max = ax[0][0].get_position().ymax
x_max = ax[-1][-1].get_position().xmax
fig.legend(loc='upper right', bbox_to_anchor=(x_max, y_max),
borderaxespad=0)
plt.show()
一些陷阱可能是使用 Constrained Layout
或者在保存文件时使用 bbox_inches='tight'
,因为两者都以意想不到的方式搞砸了图例的位置。
有关图例放置的更多示例,我找到了 this collection
很有帮助。
这是我的回答,与@scleronomic 的回答相同,但我会指出一些在解决这个问题时让我感到困惑的事情。
下面是我的代码,用于重现您想要的定位,我尝试为您创建相同的子图布局,但通过 matplotlib 而不是 getdist —— 虽然结果相同。
如您所见,诀窍在于提取第一个和最后一个轴(top-left 和 lower-right)的位置数据,以供参考。您使用的边界方法 returns:轴的 x0、y0、宽度和高度(请参阅文档)。但是我们想要的是 maximum x 和 y,这样我们的图例角就在右上角。这可以通过使用 xmax 和 ymax 方法来实现:
axes.flatten()[-1].get_position().xmax
axes.flatten()[0].get_position().ymax
一旦我们有了这些变量,就可以将它们传递到 add_legend()
函数的 bbox_to_anchor
参数中,就像您所做的那样。但是,如果我们也使用 loc='upper right'
,它会告诉 matplotlib 我们希望将图例的右上角固定到这个右上角。最后,我们需要设置 borderaxespad=0
否则由于默认填充,图例不会准确地位于我们想要的位置。
请查看下面我的示例代码以及生成的图片。请注意,我留下了 top-right 图,因此您可以看到它正确排列。
另外,请注意,正如@scleronomic 所说,调用 plt.tight_layout()
等会弄乱这个定位。
import matplotlib.pyplot as plt
# code to layout subplots as in your example:
# --------------------------------------------
g, axes = plt.subplots(nrows=7, ncols=7,figsize=(10,10))
unwanted = [1,2,3,4,5,9,10,11,12,13,17,
18,19,20,25,26,27,33,34,41]
for ax in axes.flatten():
ax.plot([1,2], [1,2])
ax.set_yticks([])
ax.set_xticks([])
for n, ax in enumerate(axes.flatten()):
if n in unwanted:
ax.remove()
# Code to answer your question:
# ------------------------------
# get the max y position of the top left axis
top_left_plot = axes.flatten()[0].get_position().ymax
# get the max x position of the bottom right axis
# it is -1 to reference the last plot
bottom_right_plot = axes.flatten()[-1].get_position().xmax
# I'm using the matplotlib so it is g.legend() not g.add_legend
# but g.add_legend() should work the same as it is a wrapper of th ematplotlib func
g.legend(['Opt. Flat. No Gamma. - cross - standard situation - Criterion taking into accound a = 200',
'Pess. Flat. No Gamma. - cross - standard situation - Criterion taking into account a = 300'],
loc='upper right', # we want to specify the location of this point
bbox_to_anchor=(bottom_right_plot, top_left_plot), # this is the x and y co-ords we extracted above
borderaxespad=0, # this means there is no padding around the legend
edgecolor='black') # I set it black for this example
plt.show()
更新
在 @youpilat13 的评论之后,我进行了更多调查并安装了 getdist 以尝试使用该工具重新创建。最初我得到了相同的结果,但发现诀窍在于,与在 matplotlib 中进行此操作不同,您必须将图例的坐标转换为图形坐标。这可以通过 g.add_legend()
调用中的以下内容实现:
bbox_transform=plt.gcf().transFigure
这是一个完整的例子:
import getdist
from getdist import plots, MCSamples
from getdist.gaussian_mixtures import GaussianND
covariance = [[0.001**2, 0.0006*0.05, 0], [0.0006*0.05, 0.05**2, 0.2**2], [0, 0.2**2, 2**2]]
mean = [0.02, 1, -2]
gauss=GaussianND(mean, covariance)
g = plots.get_subplot_plotter(subplot_size=3)
g.triangle_plot(gauss,filled=True)
top_left_plot = g.subplots.flatten()[0].get_position().ymax
bottom_right_plot = g.subplots.flatten()[-1].get_position().xmax
g.add_legend(['An example legend - item 1'],
legend_loc='upper right', # we want to specify the location of this point
bbox_to_anchor=(bottom_right_plot, top_left_plot),
bbox_transform=plt.gcf().transFigure, # this is the x and y co-ords we extracted above
borderaxespad=0, # this means there is no padding around the legend
edgecolor='black')
生成的图像:
我第一次尝试手动设置Getdist tool制作的主要情节的主要图例的位置。
下图表示来自具有联合分布的协方差矩阵的 1/2 西格玛置信水平。它由 Getdist tool.
制作生成此图的主要例程是:
# g.settings
g = plots.get_subplot_plotter()
g.settings.figure_legend_frame = True
g.settings.legend_fontsize = 21
g.triangle_plot([matrix1, matrix2],
names,
filled = True,
contour_colors = ['darkblue','red'],
line_args = [{'lw':2, 'color':'darkblue'},
{'lw':2, 'color':'red'}]
)
g.add_legend(['Opt. Flat. No Gamma. - cross - standard situation - Criterion taking into accound a = 200',\
'Pess. Flat. No Gamma. - cross - standard situation - Criterion taking into account a = 300' ],\
bbox_to_anchor = [1.5, 8.5])
数值1.5
好像对应图例的x-coordinate(宽度)8.5
对应图例的y-coordinate(高度)
现在,我想自动执行此过程,而不是每次都手动设置图例的位置。
我希望图例的右上角位于第一个左上角框的顶部边框(正好在“1sigma ± 0.0012”标题下方的顶部边框水平)。
我还希望将图例推到图的右侧(直到图右下框的右边框:由 sigma8
"1sigma ± 0.001" title
标识;注意:我希望它位于 1.0 和 0.0 xticks 之前,就在右线边框的 x-coordinate 处)。
这里是我尝试获取左上框顶部边框的全局坐标(整个图)的内容:
# First, get y coordinates of top border for first Likelihood
box1 = g.subplots[0,0]
box1_coords = box1._position.bounds
print('box1_coords = ', box1_coords)
我在执行时得到以下值:
box1_coords = (0.125, 0.7860975609756098, 0.09451219512195125, 0.09390243902439022)
如您所见,这些值似乎是标准化的,所以如果我想将这些值插入到其中,我不知道如何处理:
bbox_to_anchor = [box1_coords[0], box1_coords[1]]
正如预期的那样,这行代码产生了错误的图例位置。
那么,我如何设法自动为 bbox_to_anchor 分配好的值以获得我想要的(y-coordinate 在由 [=23= 标识的左上框的顶部边框级别) ]) 并在右侧向上推到右下框的右边界(x-coordinate 由 sigma8 用 "1sigma ± 0.001" title
识别)?
更新 1
我尝试根据我的情况调整它们,但问题仍然存在。这是我所做的:
# g.settings
g = plots.get_subplot_plotter()
# get the max y position of the top left axis
top_left_plot = g.subplots[0,0].axes.get_position().ymax
# get the max x position of the bottom right axis
# it is -1 to reference the last plot
bottom_right_plot = g.subplots[-1,-1].axes.get_position().xmax
不知道为什么top_left_plot
和bottom_right_plot
的值不是很好
我认为 subplots[0,0]
(对于图例的顶部 y-coordinate
)指的是左上角的子图,subplots[-1,-1]
指的是右下角的子图(对于图例的右侧 x-coordinate
传说)但考虑到这一点,它不起作用。
例如:
# g.settings
g = plots.get_subplot_plotter()
# Call triplot
g.triangle_plot([matrix1, matrix2],
names,
filled = True,
legend_labels = [],
contour_colors = ['darkblue','red'],
line_args = [{'lw':2, 'color':'darkblue'},
{'lw':2, 'color':'red'}])
g.add_legend(['Opt. Flat. No Gamma. - cross - standard situation - Criterion taking into accound a = 200',
'Pess. Flat. No Gamma. - cross - standard situation - Criterion taking into account a = 300'],
legend_loc='upper right',
bbox_to_anchor=(bottom_right_plot, top_left_plot)
)
我得到:
legend_coords y_max, x_max 0.88 0.9000000000000001
我不明白为什么 g.add_legend
没有考虑这些值(似乎介于 0.0 和 1.0 之间)。
用@mullinscr
的解,得到下图:
如果我通过强制获取图例位置的坐标:
top_left_plot = 8.3
bottom_right_plot = 1.0
这看起来像这个 post 的第一张图。但是这两个值并没有像它应该的那样包含在 0.0 和 1.0 之间。
更新 2
@mullinscr
,谢谢,我关注了你的更新,但总是遇到问题。如果我直接在我的脚本中应用相同的代码片段,即:
g.add_legend(['An example legend - item 1'],
legend_loc='upper right', # we want to specify the location of this point
bbox_to_anchor=(bottom_right_plot, top_left_plot),
bbox_transform=plt.gcf().transFigure, # this is the x and y co-ords we extracted above
borderaxespad=0, # this means there is no padding around the legend
edgecolor='black')
然后我得到下图:
如您所见,坐标并非真正预期的那样:x-coordinate
和 y-coordinate
存在轻微偏移。
如果我将您的代码片段应用到我的图例文本中,我得到:
我给你我整个脚本的 link,与预期的相比,这可能会让你更容易看到错误:
基本上按照您描述的方式工作。轴的 bboxes (xmin, ymin, width, height)
以图中的分数给出,plt.legend()
使用相同的格式,因此两者兼容。通过将图例的 upper right
角设置为最外侧轴定义的角,您可以获得干净的布局,而不必担心图例的确切大小。
import matplotlib.pyplot as plt
n = 4
# Create the subplot grid
# Alternative: fig, ax = plt.subplots(n, n); ax[i, j].remove() for j > i
fig = plt.figure()
gs = fig.add_gridspec(nrows=n, ncols=n)
ax = np.zeros((n, n), dtype=object)
for i in range(n):
for j in range(n):
if j <= i:
ax[i, j] = fig.add_subplot(gs[i, j])
# add this to make the position of the legend easier to spot
ax[0, -1] = fig.add_subplot(gs[0, -1])
# Plot some dummy data
ax[0, 0].plot(range(10), 'b-o', label='Dummy Label 4x4')
# Set the legend
y_max = ax[0][0].get_position().ymax
x_max = ax[-1][-1].get_position().xmax
fig.legend(loc='upper right', bbox_to_anchor=(x_max, y_max),
borderaxespad=0)
plt.show()
一些陷阱可能是使用 Constrained Layout
或者在保存文件时使用 bbox_inches='tight'
,因为两者都以意想不到的方式搞砸了图例的位置。
有关图例放置的更多示例,我找到了 this collection 很有帮助。
这是我的回答,与@scleronomic 的回答相同,但我会指出一些在解决这个问题时让我感到困惑的事情。
下面是我的代码,用于重现您想要的定位,我尝试为您创建相同的子图布局,但通过 matplotlib 而不是 getdist —— 虽然结果相同。
如您所见,诀窍在于提取第一个和最后一个轴(top-left 和 lower-right)的位置数据,以供参考。您使用的边界方法 returns:轴的 x0、y0、宽度和高度(请参阅文档)。但是我们想要的是 maximum x 和 y,这样我们的图例角就在右上角。这可以通过使用 xmax 和 ymax 方法来实现:
axes.flatten()[-1].get_position().xmax
axes.flatten()[0].get_position().ymax
一旦我们有了这些变量,就可以将它们传递到 add_legend()
函数的 bbox_to_anchor
参数中,就像您所做的那样。但是,如果我们也使用 loc='upper right'
,它会告诉 matplotlib 我们希望将图例的右上角固定到这个右上角。最后,我们需要设置 borderaxespad=0
否则由于默认填充,图例不会准确地位于我们想要的位置。
请查看下面我的示例代码以及生成的图片。请注意,我留下了 top-right 图,因此您可以看到它正确排列。
另外,请注意,正如@scleronomic 所说,调用 plt.tight_layout()
等会弄乱这个定位。
import matplotlib.pyplot as plt
# code to layout subplots as in your example:
# --------------------------------------------
g, axes = plt.subplots(nrows=7, ncols=7,figsize=(10,10))
unwanted = [1,2,3,4,5,9,10,11,12,13,17,
18,19,20,25,26,27,33,34,41]
for ax in axes.flatten():
ax.plot([1,2], [1,2])
ax.set_yticks([])
ax.set_xticks([])
for n, ax in enumerate(axes.flatten()):
if n in unwanted:
ax.remove()
# Code to answer your question:
# ------------------------------
# get the max y position of the top left axis
top_left_plot = axes.flatten()[0].get_position().ymax
# get the max x position of the bottom right axis
# it is -1 to reference the last plot
bottom_right_plot = axes.flatten()[-1].get_position().xmax
# I'm using the matplotlib so it is g.legend() not g.add_legend
# but g.add_legend() should work the same as it is a wrapper of th ematplotlib func
g.legend(['Opt. Flat. No Gamma. - cross - standard situation - Criterion taking into accound a = 200',
'Pess. Flat. No Gamma. - cross - standard situation - Criterion taking into account a = 300'],
loc='upper right', # we want to specify the location of this point
bbox_to_anchor=(bottom_right_plot, top_left_plot), # this is the x and y co-ords we extracted above
borderaxespad=0, # this means there is no padding around the legend
edgecolor='black') # I set it black for this example
plt.show()
更新
在 @youpilat13 的评论之后,我进行了更多调查并安装了 getdist 以尝试使用该工具重新创建。最初我得到了相同的结果,但发现诀窍在于,与在 matplotlib 中进行此操作不同,您必须将图例的坐标转换为图形坐标。这可以通过 g.add_legend()
调用中的以下内容实现:
bbox_transform=plt.gcf().transFigure
这是一个完整的例子:
import getdist
from getdist import plots, MCSamples
from getdist.gaussian_mixtures import GaussianND
covariance = [[0.001**2, 0.0006*0.05, 0], [0.0006*0.05, 0.05**2, 0.2**2], [0, 0.2**2, 2**2]]
mean = [0.02, 1, -2]
gauss=GaussianND(mean, covariance)
g = plots.get_subplot_plotter(subplot_size=3)
g.triangle_plot(gauss,filled=True)
top_left_plot = g.subplots.flatten()[0].get_position().ymax
bottom_right_plot = g.subplots.flatten()[-1].get_position().xmax
g.add_legend(['An example legend - item 1'],
legend_loc='upper right', # we want to specify the location of this point
bbox_to_anchor=(bottom_right_plot, top_left_plot),
bbox_transform=plt.gcf().transFigure, # this is the x and y co-ords we extracted above
borderaxespad=0, # this means there is no padding around the legend
edgecolor='black')
生成的图像: