matplotlib set_major_formatter考虑值的范围

yws3nbqq  于 2023-10-24  发布在  其他
关注(0)|答案(1)|浏览(121)

我写了一个包含matplotlib图形的UI。它显示图形,我想在轴上使用工程符号(指数是3的倍数)。请注意,我希望写出幂(例如“123×10 ",而不是“123 M”,因此不使用EngFormatter)。
我可以用我多年前写的格式化函数(format_eng)和matplotlib的set_major_formatterfmt_x_data功能来做到这一点:

def axes_eng_format(ax, x=True, y=True, **kwargs):
    if x:
        if 'use_si' not in kwargs and 'use_latex' not in kwargs:
            ax.xaxis.set_major_formatter(lambda v, pos: format_eng(v, use_latex=True, **kwargs))
        else:
            ax.xaxis.set_major_formatter(lambda v, pos: format_eng(v, **kwargs))
        ax.fmt_xdata = lambda v: format_eng(v, **kwargs)
    if y:
        if 'use_si' not in kwargs and 'use_latex' not in kwargs:
            ax.yaxis.set_major_formatter(lambda v, pos: format_eng(v, use_latex=True, **kwargs))
        else:
            ax.yaxis.set_major_formatter(lambda v, pos: format_eng(v, **kwargs))
        ax.fmt_ydata = lambda v: format_eng(v, **kwargs)

axes_eng_format(ax)

格式化函数本身不应该与这个问题相关(因为它只是一个只接受单个参数的哑格式化程序,因此不能考虑值的范围),但是为了参考,它在这里:https://gist.github.com/abudden/3252252632bd8271c49c886771d8f82d
格式化程序只获取两个参数:的值(v)和图上的位置(pos)。只使用值v是可以的,也可以工作,但是0.5被渲染为500×10立方。如果长轴上的值范围是(比如)0到700×10立方,那么这正是我希望它被渲染的方式,但如果值的范围是(如下面的第二张图)0到3.5,我宁愿它被渲染为0.5,以与轴上的其他值保持一致。
目前我能想到的唯一方法是对所有的tick值进行后处理,但考虑到用户可以放大和缩小等,这似乎不是很健壮。
有没有更好的方法来达到我所追求的效果?我想我可以用pos做些什么,但我还没有能够得到我的头周围的工作。我应该如何去使用pos来调整一个数字的格式(或者有没有更好的方法)?
Y轴看起来不错:

Y轴看起来不太好:

klr1opcd

klr1opcd1#

pos参数只是当前格式化的tick标签的数字(一个整数值),从0开始计数。因此,使用它来实现上述目的并没有什么用处。
相反,您可以使用当前显示的值范围,即使用xmin, xmax = ax.get_xlim()ymin, ymax = ax.get_ylim()的轴的当前范围。并将其传递给format_eng函数。当然,format_eng需要相应地修改。
仅考虑正值的简单示例(关于x轴和y轴的最大值,而不是范围):

def format_eng(number, max_value, places=2, force_exponent=None, use_si=False, use_unicode=False, use_latex=False, return_components=False, strip_zeros=True, unit=None, pos_sign=False):
    if max_value < 10000:
        return number
    # ... add the rest of the formatter function code ...
    # si_lookup = {...}
    # ...
    return result
  
def axes_eng_format(ax, x=True, y=True, **kwargs):
    if x:
        if 'use_si' not in kwargs and 'use_latex' not in kwargs:
            ax.xaxis.set_major_formatter(lambda v, pos: format_eng(v, ax.get_xlim()[1], use_latex=True, **kwargs))
        else:
            ax.xaxis.set_major_formatter(lambda v, pos: format_eng(v, ax.get_xlim()[1], **kwargs))
        ax.fmt_xdata = lambda v: format_eng(v, ax.get_xlim()[1], **kwargs)
    if y:
        if 'use_si' not in kwargs and 'use_latex' not in kwargs:
            ax.yaxis.set_major_formatter(lambda v, pos: format_eng(v, ax.get_ylim()[1], use_latex=True, **kwargs))
        else:
            ax.yaxis.set_major_formatter(lambda v, pos: format_eng(v, ax.get_ylim()[1], **kwargs))
        ax.fmt_ydata = lambda v: format_eng(v, ax.get_ylim()[1],  **kwargs)

样地:

x = np.arange(0, 100, 0.00001)
y = np.sin(0.1 * np.pi * x) + 1
y = y * 10000

fig, ax = plt.subplots()
ax.plot(x, y)
axes_eng_format(ax)
plt.show()

放大:

相关问题