Matplotlib 轴标签:如何找出它们的位置? [英] Matplotlib axis labels: how to find out where they will be located?

查看:65
本文介绍了Matplotlib 轴标签:如何找出它们的位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个例程来从大气模型输出中绘制垂直横截面.一个例子如下所示.我想要做的是显示两个垂直轴:在左侧我以对数刻度显示压力值,在右侧我以公里为单位显示高度.我认为在模型层的位置显示高度会很好 - 这就是它们不规则间隔的原因.除了右边的标签在底部附近重叠之外,其他所有方法都运行良好.我发现可以使用 ax2.get_yticklabels()[index] .set_visible(False)隐藏特定的标签.我的问题是:如何确定要隐藏哪些标签(索引)?我相信应该可以找出刻度标签的位置(在轴或图形坐标中).然后我可以使用

I have written a routine to draw vertical cross sections from atmospheric model output. An example is shown below. What I would like to do, is to show two vertical axes: on the left I display presure values on a log scale, and on the right I show altitudes in km. I thought it would be nice to show the altitudes at the locations of the model levels - this is why they are irregularly spaced. All works nicely, except that the labels on the right overlap near the bottom. I found out that I can hide specific labels using ax2.get_yticklabels()[index].set_visible(False). My problem is: how do I determine which labels (indices) I want to hide? I believe it should be possible to find out where the tick labels are positioned (in axis or figure coordinates). Then I could use a threshold distance as in

yp = -1  
for t in ax2.get_yticklabels():  
    y = t.get_position().y0   # this doesn't yield any useful bbox! 
    if y-yp < threshold:  
        t.set_visible(False)  
    else:  
        yp = y  

不幸的是,我还没有找到获取标签坐标的方法.有什么提示吗?

Unfortunately, I haven't found a way to get the label coordinates. Any hints?

这是示例图:

这是完成绘图的完整代码(数据是二维数组,x是纬度,y是压力值):

And here is the complete code that does the plotting (data is a 2-D array, x are latitudes, and y are pressure values):

def plotZM(data, x, y, plotOpt=None):
    """Create a zonal mean contour plot of one variable
    plotOpt is a dictionary with plotting options:
    'scale_factor': multiply values with this factor before plotting
    'units': a units label for the colorbar
    'levels': use list of values as contour intervals
    'title': a title for the plot
    """
    if plotOpt is None: plotOpt = {}
    # create figure and axes
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # scale data if requested
    scale_factor = plotOpt.get('scale_factor', 1.0)
    pdata = data * scale_factor
    # determine contour levels to be used; default: linear spacing, 20 levels
    clevs = plotOpt.get('levels', np.linspace(data.min(), data.max(), 20))
    # map contour values to colors
    norm=matplotlib.colors.BoundaryNorm(clevs, ncolors=256, clip=False)
    # draw the (filled) contours
    contour = ax1.contourf(x, y, pdata, levels=clevs, norm=norm) 
    # add a title
    title = plotOpt.get('title', 'Vertical cross section')
    ax1.set_title(title)   # optional keyword: fontsize="small"
    # add colorbar
    # Note: use of the ticks keyword forces colorbar to draw all labels
    fmt = matplotlib.ticker.FormatStrFormatter("%g")
    cbar = fig.colorbar(contour, ax=ax1, orientation='horizontal', shrink=0.8,
                        ticks=clevs, format=fmt)
    cbar.set_label(plotOpt.get('units', ''))
    for t in cbar.ax.get_xticklabels():
        t.set_fontsize("x-small")
    # change font size of x labels
    xlabels = ax1.get_xticklabels()
    for t in xlabels:
        t.set_fontsize("x-small")
    # set up y axes: log pressure labels on the left y axis, altitude labels
    # according to model levels on the right y axis
    ax1.set_ylabel("Pressure [hPa]")
    ax1.set_yscale('log')
    ax1.set_ylim(y.max(), y.min())
    subs = [1,2,5]
    print "y_max/y_min = ", y.max()/y.min()
    if y.max()/y.min() < 30.:
        subs = [1,2,3,4,5,6,7,8,9]
    loc = matplotlib.ticker.LogLocator(base=10., subs=subs)
    ax1.yaxis.set_major_locator(loc)
    fmt = matplotlib.ticker.FormatStrFormatter("%g")
    ax1.yaxis.set_major_formatter(fmt)
    ylabels = ax1.get_yticklabels()
    for t in ylabels:
        t.set_fontsize("x-small")
    # calculate altitudes from pressure values (use fixed scale height)
    z0 = 8.400    # scale height for pressure_to_altitude conversion [km]
    altitude = z0 * np.log(1015.23/y)
    # add second y axis for altitude scale
    ax2 = ax1.twinx()
    ax2.set_ylabel("Altitude [km]")
    ax2.set_ylim(altitude.min(), altitude.max())
    ax2.set_yticks(altitude)
    fmt = matplotlib.ticker.FormatStrFormatter("%6.1f")
    ax2.yaxis.set_major_formatter(fmt)
    # tweak altitude labels
    ylabels = ax2.get_yticklabels()
    for i,t in enumerate(ylabels):
        t.set_fontsize("x-small")
    # show plot
    plt.show()

推荐答案

这是 plotZM 例程的更新版本,它将模型级别绘制到右侧的单独面板中,并使用线性等距标记作为高度轴.添加了另一个选项来掩盖表面压力以下的区域.

Here is an updated version fo the plotZM routine which will plot the model levels into a separate panel to the right and use linear equidistant markers for the altitude axis. Another option has been added to mask out regions below the surface pressure.

此代码是缩放安全的"(即,当您放大或平移绘图时,高度和压力标签会发生很好的变化,并且模型级别会持续变化).它还包含大量的轴和标签调整,因此有望作为对matplotlib可以执行的操作的更复杂示例,对其他人有用.示例图如下所示.

This code is "zoom-safe" (i.e. the altitude and pressure labels change nicely when you zoom into the plot or pan it, and the model levels change consistently). It also contains quite a bunch of axis and label tweaking and may therefore hopefully be useful to others as a more complex example of what you can do with matplotlib. An example figure is shown below.

def plotZM(data, x, y, plotOpt=None, modelLevels=None, surfacePressure=None):
    """Create a zonal mean contour plot of one variable
    plotOpt is a dictionary with plotting options:
      'scale_factor': multiply values with this factor before plotting
      'units': a units label for the colorbar
      'levels': use list of values as contour intervals
      'title': a title for the plot
    modelLevels: a list of pressure values indicating the model vertical resolution. If present,
        a small side panel will be drawn with lines for each model level
    surfacePressure: a list (dimension len(x)) of surface pressure values. If present, these will
        be used to mask out regions below the surface
    """
    # explanation of axes:
    #   ax1: primary coordinate system latitude vs. pressure (left ticks on y axis)
    #   ax2: twinned axes for altitude coordinates on right y axis
    #   axm: small side panel with shared y axis from ax2 for display of model levels
    # right y ticks and y label will be drawn on axr if modelLevels are given, else on ax2
    #   axr: pointer to "right axis", either ax2 or axm

    if plotOpt is None: plotOpt = {}
    labelFontSize = "small"
    # create figure and axes
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # scale data if requested
    scale_factor = plotOpt.get('scale_factor', 1.0)
    pdata = data * scale_factor
    # determine contour levels to be used; default: linear spacing, 20 levels
    clevs = plotOpt.get('levels', np.linspace(data.min(), data.max(), 20))
    # map contour values to colors
    norm=matplotlib.colors.BoundaryNorm(clevs, ncolors=256, clip=False)
    # draw the (filled) contours
    contour = ax1.contourf(x, y, pdata, levels=clevs, norm=norm) 
    # mask out surface pressure if given
    if not surfacePressure is None: 
        ax1.fill_between(x, surfacePressure, surfacePressure.max(), color="white")    
    # add a title
    title = plotOpt.get('title', 'Vertical cross section')
    ax1.set_title(title)
    # add colorbar
    # Note: use of the ticks keyword forces colorbar to draw all labels
    fmt = matplotlib.ticker.FormatStrFormatter("%g")
    cbar = fig.colorbar(contour, ax=ax1, orientation='horizontal', shrink=0.8,
                        ticks=clevs, format=fmt)
    cbar.set_label(plotOpt.get('units', ''))
    for t in cbar.ax.get_xticklabels():
        t.set_fontsize(labelFontSize)
    # set up y axes: log pressure labels on the left y axis, altitude labels
    # according to model levels on the right y axis
    ax1.set_ylabel("Pressure [hPa]")
    ax1.set_yscale('log')
    ax1.set_ylim(10.*np.ceil(y.max()/10.), y.min()) # avoid truncation of 1000 hPa
    subs = [1,2,5]
    if y.max()/y.min() < 30.:
        subs = [1,2,3,4,5,6,7,8,9]
    y1loc = matplotlib.ticker.LogLocator(base=10., subs=subs)
    ax1.yaxis.set_major_locator(y1loc)
    fmt = matplotlib.ticker.FormatStrFormatter("%g")
    ax1.yaxis.set_major_formatter(fmt)
    for t in ax1.get_yticklabels():
        t.set_fontsize(labelFontSize)
    # calculate altitudes from pressure values (use fixed scale height)
    z0 = 8.400    # scale height for pressure_to_altitude conversion [km]
    altitude = z0 * np.log(1015.23/y)
    # add second y axis for altitude scale 
    ax2 = ax1.twinx()
    # change values and font size of x labels
    ax1.set_xlabel('Latitude [degrees]')
    xloc = matplotlib.ticker.FixedLocator(np.arange(-90.,91.,30.))
    ax1.xaxis.set_major_locator(xloc)
    for t in ax1.get_xticklabels():
        t.set_fontsize(labelFontSize)
    # draw horizontal lines to the right to indicate model levels
    if not modelLevels is None:
        pos = ax1.get_position()
        axm = fig.add_axes([pos.x1,pos.y0,0.02,pos.height], sharey=ax2)
        axm.set_xlim(0., 1.)
        axm.xaxis.set_visible(False)
        modelLev = axm.hlines(altitude, 0., 1., color='0.5')
        axr = axm     # specify y axis for right tick marks and labels
        # turn off tick labels of ax2
        for t in ax2.get_yticklabels():
            t.set_visible(False)
        label_xcoor = 3.7
    else:
        axr = ax2
        label_xcoor = 1.05
    axr.set_ylabel("Altitude [km]")
    axr.yaxis.set_label_coords(label_xcoor, 0.5)
    axr.set_ylim(altitude.min(), altitude.max())
    yrloc = matplotlib.ticker.MaxNLocator(steps=[1,2,5,10])
    axr.yaxis.set_major_locator(yrloc)
    axr.yaxis.tick_right()
    for t in axr.yaxis.get_majorticklines():
        t.set_visible(False)
    for t in axr.get_yticklabels():
        t.set_fontsize(labelFontSize)
    # show plot
    plt.show()

这篇关于Matplotlib 轴标签:如何找出它们的位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆