【问题标题】:How to change the position of some x axis tick labels on top of the bottom x axis in matplotlib?如何更改matplotlib底部x轴顶部的一些x轴刻度标签的位置?
【发布时间】:2021-02-06 12:31:59
【问题描述】:

这是我当前的脚本:

#!/usr/bin/env python3

import math
import numpy as np
import matplotlib
import matplotlib.pyplot as plt

"""
Setup for a typical explanatory-style illustration style graph.
"""

h = 2
x = np.linspace(-np.pi, np.pi, 100)
y = 2 * np.sin(x)
rc = {
    # Tick in the middle of the axis line.
    'xtick.direction' : 'inout',
    'ytick.direction' : 'inout',

    # Bold is easier to read when we have few ticks.
    'font.weight': 'bold',
    'xtick.labelbottom': False,
    'xtick.labeltop': True,
}
with plt.rc_context(rc):
    fig, ax = plt.subplots()
    ax.plot(x, y)
    ax.set_title(
        '2 sin(x), not $\\sqrt{2\\pi}$',
        # TODO make LaTeX part bold?
        # https://stackoverflow.com/questions/14324477/bold-font-weight-for-latex-axes-label-in-matplotlib
        fontweight='bold',
        # Too close otherwise.
        # https://stackoverflow.com/questions/16419670/increase-distance-between-title-and-plot-in-matplolib/56738085
        pad=20
    )

    # Custom visible plot area.
    # ax.set_xlim(-3, 3)
    ax.set_ylim(-2.5, 2.5)

    # Axes
    # Axes on center:
    # https://stackoverflow.com/questions/31556446/how-to-draw-axis-in-the-middle-of-the-figure
    ax.spines['left'].set_position('zero')
    ax.spines['right'].set_visible(False)
    ax.spines['bottom'].set_position('zero')
    ax.spines['top'].set_visible(False)
    # Axes with arrow:
    # https://stackoverflow.com/questions/33737736/matplotlib-axis-arrow-tip
    ax.plot(1, 0, ls="", marker=">", ms=10, color="k",
            transform=ax.get_yaxis_transform(), clip_on=False)
    ax.plot(0, 1, ls="", marker="^", ms=10, color="k",
            transform=ax.get_xaxis_transform(), clip_on=False)

    # Ticks
    ax.xaxis.set_ticks_position('bottom')
    ax.yaxis.set_ticks_position('left')
    # Make ticks a bit longer.
    ax.tick_params(width=1, length=10)
    # Select tick positions
    # https://stackoverflow.com/questions/12608788/changing-the-tick-frequency-on-x-or-y-axis-in-matplotlib
    xticks = np.arange(math.ceil(min(x)),     math.floor(max(x)) + 1, 1)
    yticks = np.arange(math.ceil(min(y)) - 1, math.floor(max(y)) + 2, 1)
    # Remove 0.
    xticks = np.setdiff1d(xticks, [0])
    yticks = np.setdiff1d(yticks, [0])
    ax.xaxis.set_ticks(xticks)
    ax.yaxis.set_ticks(yticks)
    # Another approach. But because I want to be able to remove the 0,
    # anyways, I just explicitly give all ticks instead.
    # ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(1.0))
    # ax.yaxis.set_major_locator(matplotlib.ticker.MultipleLocator(1.0))

    # Annotations.
    ax.plot([0, np.pi/2], [h, h], '--r')
    ax.plot([np.pi/2, np.pi/2], [h, 0], '--r')
    ax.plot(np.pi/2, h, marker='o', linewidth=2, markersize=10,
        markerfacecolor='w', markeredgewidth=1.5, markeredgecolor='black')

plt.savefig(
    'main.png',
    format='png',
    bbox_inches='tight'
)
plt.clf()

这是输出:

这就是我想要的(用 GIMP 破解),注意现在负刻度标签是如何在轴的另一侧的。

我尝试添加:

    for tick in ax.xaxis.get_majorticklabels():
        tick.set_verticalalignment("bottom")

How to move a tick's label in matplotlib? 的答案所示,但这并没有将刻度标签向上移动得足够多,而是使标签显示在轴的顶部。

在 matplotlib 3.2.2 上测试。

【问题讨论】:

    标签: matplotlib


    【解决方案1】:

    以下代码将调整刻度的垂直对齐方式,具体取决于它们是在负 x 值还是正 x 值。然而,这还不够,因为标签实际上锚定在刻度线的底部。因此,我稍微调整了它们的 y 位置,但是您必须使用该值才能获得所需的输出

    # adjust the xticks so that they are on top when x<0 and on the bottom when x≥0
    ax.spines['top'].set_visible(True)
    ax.spines['top'].set_position('zero')
    ax.spines['bottom'].set_visible(True)
    ax.spines['bottom'].set_position('zero')
    ax.xaxis.set_tick_params(which='both', top=True, labeltop=True,
                                 bottom=True, labelbottom=True)
    fig.canvas.draw()
    for tick in ax.xaxis.get_major_ticks():
        print(tick.get_loc())
        if tick.get_loc()<0:
            tick.tick1line.set_visible(False)
            tick.label1.set_visible(False)
        else:
            tick.tick2line.set_visible(False)
            tick.label2.set_visible(False)
    

    完整代码:

    import math
    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    
    """
    Setup for a typical explanatory-style illustration style graph.
    """
    
    h = 10
    x = np.linspace(-np.pi, np.pi, 100)
    y = h * np.sin(x)
    rc = {
        # Tick in the middle of the axis line.
        'xtick.direction' : 'inout',
        'ytick.direction' : 'inout',
    
        # Bold is easier to read when we have few ticks.
        'font.weight': 'bold',
        'xtick.labelbottom': False,
        'xtick.labeltop': True,
    }
    with plt.rc_context(rc):
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set_title(
            '2 sin(x), not $\\sqrt{2\\pi}$',
            # TODO make LaTeX part bold?
            # https://stackoverflow.com/questions/14324477/bold-font-weight-for-latex-axes-label-in-matplotlib
            fontweight='bold',
            # Too close otherwise.
            # https://stackoverflow.com/questions/16419670/increase-distance-between-title-and-plot-in-matplolib/56738085
            pad=20
        )
    
        # Custom visible plot area.
        # ax.set_xlim(-3, 3)
        ax.set_ylim(-2.5, 2.5)
    
        # Axes
        # Axes on center:
        # https://stackoverflow.com/questions/31556446/how-to-draw-axis-in-the-middle-of-the-figure
        ax.spines['left'].set_position('zero')
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_position('zero')
        ax.spines['top'].set_visible(False)
        # Axes with arrow:
        # https://stackoverflow.com/questions/33737736/matplotlib-axis-arrow-tip
        ax.plot(1, 0, ls="", marker=">", ms=10, color="k",
                transform=ax.get_yaxis_transform(), clip_on=False)
        ax.plot(0, 1, ls="", marker="^", ms=10, color="k",
                transform=ax.get_xaxis_transform(), clip_on=False)
    
        # Ticks
        ax.xaxis.set_ticks_position('bottom')
        ax.yaxis.set_ticks_position('left')
        # Make ticks a bit longer.
        ax.tick_params(width=1, length=10)
        # Select tick positions
        # https://stackoverflow.com/questions/12608788/changing-the-tick-frequency-on-x-or-y-axis-in-matplotlib
        xticks = np.arange(math.ceil(min(x)),     math.floor(max(x)) + 1, 1)
        yticks = np.arange(math.ceil(min(y)) - 1, math.floor(max(y)) + 2, 1)
        # Remove 0.
        xticks = np.setdiff1d(xticks, [0])
        yticks = np.setdiff1d(yticks, [0])
        ax.xaxis.set_ticks(xticks)
        ax.yaxis.set_ticks(yticks)
        # Another approach. But because I want to be able to remove the 0,
        # anyways, I just explicitly give all ticks instead.
        # ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(1.0))
        # ax.yaxis.set_major_locator(matplotlib.ticker.MultipleLocator(1.0))
        
        for g,t in zip(ax.get_xticks(),ax.get_xticklabels()):
            if g<0:
                t.set_va('bottom')
            else:
                t.set_va('top')
            t.set_transform(ax.transData)
            t.set_position((g,0.15*-(g/abs(g))))
    
        # Annotations.
        ax.plot([0, np.pi/2], [h, h], '--r')
        ax.plot([np.pi/2, np.pi/2], [h, 0], '--r')
        ax.plot(np.pi/2, h, marker='o', linewidth=2, markersize=10,
            markerfacecolor='w', markeredgewidth=1.5, markeredgecolor='black')
        
        
        # adjust the xticks so that they are on top when x<0 and on the bottom when x≥0
        ax.spines['top'].set_visible(True)
        ax.spines['top'].set_position('zero')
        ax.spines['bottom'].set_visible(True)
        ax.spines['bottom'].set_position('zero')
        ax.xaxis.set_tick_params(which='both', top=True, labeltop=True,
                                     bottom=True, labelbottom=True)
        fig.canvas.draw()
        for tick in ax.xaxis.get_major_ticks():
            print(tick.get_loc())
            if tick.get_loc()<0:
                tick.tick1line.set_visible(False)
                tick.label1.set_visible(False)
            else:
                tick.tick2line.set_visible(False)
                tick.label2.set_visible(False)
    

    【讨论】:

    • 我后来注意到,不幸的是,这个解决方案不能完美地适应数据大小,例如如果我设置h = 1000,那么数字开始触及刻度。如果我删除添加的 for 循环,它们不会。我猜可能有一个更合适的转换适用于魔术0.15,这样单个数字就可以适用于任何数据大小。
    • 我找到了一种更好的方法来处理标签。我已经修改了答案
    • 啊,鬼鬼祟祟,将两个轴都设置为可见,太棒了!
    猜你喜欢
    • 2020-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    相关资源
    最近更新 更多