2013-01-04 59 views
6

我寫了一個例程來繪製大氣模型輸出的垂直截面。一個例子如下所示。我想要做的是顯示兩個垂直座標軸:左邊顯示的是對數刻度值,右邊顯示的是以km爲單位的高度。我認爲能夠在模型層的位置顯示海拔高度會很好 - 這就是爲什麼它們不規則間隔的原因。所有作品都很好,除了右邊的標籤重疊在底部附近之外。我發現我可以使用ax2.get_yticklabels()[index].set_visible(False)隱藏特定的標籤。我的問題是:如何確定我想隱藏哪些標籤(索引)?我相信應該有可能找出滴答標籤的位置(在軸或圖形座標中)。然後我可以使用閾值距離,因爲在Matplotlib軸標籤:如何找出它們的位置?

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 

不幸的是,我還沒有找到一種方法來獲得標籤座標。任何提示?

下面是示例圖: Vertical cross section plot

這裏是完整代碼,不會繪圖(數據是2- d陣列中,x是緯度,和y是壓力值):

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() 

回答

2

我寧願不做這樣的事情。這可能會產生問題,例如,在調整繪圖窗口大小或更改輸出圖像的dpi時。它肯定會看起來尷尬。

您應該對AX2

  1. 一套少蜱(和,在我oppinion,他們應該是線性比例爲海拔
  2. 使用來自matplotlib預定義的(或者甚至是自定義)定位器。我像自動定位和MaxNLocator,只是嘗試哪一個給你最好的結果。

如果您需要這方面的幫助,就問我。

2

這裏是一個更新的版本F plotZM例程,將模型水平繪製到右側的單獨面板中,並使用高度軸的線性等距標記。增加了另一個選項來掩蓋表面壓力以下的區域。

此代碼是「縮放安全」(即當您放大繪圖或平移時高度和壓力標籤會很好地改變,並且模型層次一致地改變)。它也包含了很多軸和標籤的調整,因此可能希望對其他人有用,作爲你可以用matplotlib做什麼的更復雜的例子。下圖顯示了一個示例圖。

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() 

vertical cross section plot with modified code