获取轴的纵横比

16

当一个 axes 的长宽比例设置为 'auto' 时,有没有一种简单可靠的方法来确定其当前的长宽比例?

显而易见的事情是检查 ax.get_aspect(),但那只会返回 'auto'。我可以通过 ax.set_aspect(aspect) 将其设置为任意常量值,之后同样的常量将被 ax.get_aspect() 返回。默认情况下(也非常有用),我们有 aspect = 'auto',在这种情况下,长宽比例会自动计算并调整以匹配数据限制和轴的大小。
如何获取自动选择的数字长宽比例?

澄清一下,这既不是由 ax.get_data_ratio() 返回的数据限制的长宽比例,也不是由 fig.get_figheight() / fig.get_figwidth()(对于图形)返回的图形或子图的显示大小的长宽比例。这有点微妙,因为它取决于显示大小和数据限制。 (这可能会导致混淆不同比例的原因,所以我认为很重要可以轻松访问它。)


你指的是哪个方面比例?数据还是显示? - Mad Physicist
根据您对@Frank答案的评论,我猜测是显示或者更复杂的内容。 - Mad Physicist
很不幸,@MadPhysicist,这是“更加复杂的东西”。请看我的编辑后的问题。 - askewchan
很抱歉昨天我太累了,没有意识到我的解决方案只适用于aspect='equal'的情况,而你正在寻求其他情况。由于似乎没有简单的方法来解决这个问题,我已经请求添加此功能:https://github.com/matplotlib/matplotlib/issues/8013 - Frank Breitling
嗨 @tacaswell,我用它来计算 ax.annotaterotation 角度。如果能够设置文本的斜率(以数据单位为准),这将解决我的问题。 - askewchan
显示剩余5条评论
4个回答

18
文档中得知,纵横坐标轴中的数据与显示比例单位之比就是宽高比,即如果在y方向上每个显示单元对应10个数据单元,在x方向上每个显示单元对应1个数据单元,则比率为1/10。此时,圆将比其高度矮10倍而宽10倍。这对应于“num”的宽高比会被拉伸如下的说法:

圆将被拉伸,以使其高度为宽度的num倍。aspect=1相当于aspect='equal'。

基于你查看的同一原始代码(matplotlib.axes._base.adjust_aspect,大约从第1405行开始),我认为只要您仅需要线性笛卡尔坐标轴的比率,我们可以得出一个简化的公式。 对于极坐标和对数坐标轴,情况变得复杂,所以我会忽略它们。

重申公式如下:

(x_data_unit / x_display_unit) / (y_data_unit / y_display_unit)

这恰好与之相同。

(y_display_unit / x_display_unit) / (y_data_unit / x_data_unit)

这个最后的公式只是两个方向上显示大小之比除以x和y限制比率的比值。请注意,ax.get_data_ratio在此处不适用,因为它返回的是实际数据边界的结果,而不是轴限制:

from operator import sub
def get_aspect(ax):
    # Total figure size
    figW, figH = ax.get_figure().get_size_inches()
    # Axis size on figure
    _, _, w, h = ax.get_position().bounds
    # Ratio of display units
    disp_ratio = (figH * h) / (figW * w)
    # Ratio of data units
    # Negative over negative because of the order of subtraction
    data_ratio = sub(*ax.get_ylim()) / sub(*ax.get_xlim())

    return disp_ratio / data_ratio

现在让我们来测试一下:

from matplotlib import pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
print('{} == {}'.format(ax.get_aspect(), get_aspect(ax)))
ax.set_aspect(10)
print('{} == {}'.format(ax.get_aspect(), get_aspect(ax)))

这个可以运行。我很惊讶没有更简单的方法来做到这一点。我想知道为什么 matplotlib 没有提供 get_aspect 函数。 - Pygmalion
@Pygmalion。这是一个非常专业的应用程序。 - Mad Physicist
@MadPhysicist 每次你想画圆的时候都需要那个值。我不知道它是否如此专业。 - Guimoute
@Guimoute。这是获取纵横比。您只需要使用 set_aspect('equal') 来绘制一个圆形。所以我认为我的评论仍然很有用。 - Mad Physicist
是的,但set_aspect("equal")并不是一个通用的解决方案。在许多情况下,您可能希望在具有给定和恒定纵横比的已存在画布上处理圆形。例如,我之所以遇到这个问题,是因为我正在编写一个函数来获取最接近我的光标的数据点,当然我没有预料到sqrt(dx² + dy²)不是距离的可接受定义,因为数据限制和图形比例会发生变化。我正在查看一个以显示单位表示的圆,在数据单位中是一个椭圆。编辑:所以你的解决方案对此非常完美,dx /= get_aspect(ax)就可以了。 - Guimoute

4
我能找到的最好的东西是这个:
def get_aspect(ax=None):
    if ax is None:
        ax = plt.gca()
    fig = ax.figure

    ll, ur = ax.get_position() * fig.get_size_inches()
    width, height = ur - ll
    axes_ratio = height / width
    aspect = axes_ratio / ax.get_data_ratio()

    return aspect

但是这个问题非常复杂,我不确定在转换等情况下是否可靠,因为我对bboxtransform对象一无所知。


1
我觉得你应该把这个添加到你的问题中,并问是否有比这更容易或更可靠的方法。 - depperm
这段代码看起来与matplotlib中的Axes.apply_aspect中的代码大致相似。 - Mad Physicist

2
如何?
import numpy as np
aspect = sum(np.abs(ax.get_xlim())) / sum(np.abs(ax.get_ylim()))

谢谢,@Frank,但我认为那只是给了1 / ax.get_data_ratio() - askewchan
哦,没错,甚至比我的建议更好。我不知道这个存在。我以为那就是你要找的,但显然是其他东西。抱歉。 - Frank Breitling
为什么要使用 np.abs()?如果两个限制都具有相同的符号,这将无法工作。 - Jason S

0

我不确定它是否完全符合你的要求,但可以试一下

    bbox = axis.get_window_extent().transformed(figure.dpi_scale_trans.inverted())
    aspect_ratio = bbox.width / bbox.height

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接