Search code examples
pythonmatplotlibmultiple-axes

Change axis line range in mpl_toolkits new_fixed_axis


I am struggling to modify my code to define a specific range of the secondary x-axis. Below is a snippet of the relevant code for creating 2 x-axes, and the output it generates:

from matplotlib import pyplot as plt
import matplotlib.ticker as ticker
from mpl_toolkits.axes_grid.parasite_axes import SubplotHost

...
    x = np.arange(1, len(metric1)+1)  # the label locations
    width = 0.3  # the width of the bars

    fig1 = plt.figure()
    ax1 = SubplotHost(fig1, 111)
    fig1.add_subplot(ax1)
    ax1.axis((0, 14, 0, 20))
    ax1.bar(x, [t[2] for t in metric1], width, label='metric1')
    ax1.bar(x + width, [t[2] for t in metric2], width, label='metric2')
    ax1.bar(x + 2*width, [t[2] for t in metric3], width, label='metric3')

    ax1.set_xticks(x+width)
    ax1.set_xticklabels(['BN', 'B', 'DO', 'N', 'BN', 'B', 'DO', 'N', 'BN', 'B', 'DO', 'N', 'BN', 'B', 'DO', 'N'])
    ax1.axis["bottom"].major_ticks.set_ticksize(0)
    ax2 = ax1.twiny()
    offset = 0, -25 # Position of the second axis
    new_axisline = ax2.get_grid_helper().new_fixed_axis
    ax2.axis["bottom"] = new_axisline(loc="bottom", axes=ax2, offset=offset)
    ax2.axis["top"].set_visible(False)
    ax2.axis["bottom"].minor_ticks.set_ticksize(0)
    ax2.axis["bottom"].major_ticks.set_ticksize(15)

    ax2.set_xticks([0.058, 0.3434, 0.63, 0.915])
    ax2.xaxis.set_major_formatter(ticker.NullFormatter())
    ax2.xaxis.set_minor_locator(ticker.FixedLocator([0.20125, 0.48825, 0.776]))
    ax2.xaxis.set_minor_formatter(ticker.FixedFormatter(['foo', 'bar', 'foo2']))
...

This is the current output:

This is the current output

What I would like to have, is to not have the secondary x-axis (foo, bar, foo2) line extend beyond the first and last x-tick, as follows (I edited in MS paint 😅):

enter image description here

Any help appreciated.


Solution

  • As there have been no other answers, I can suggest a non-elegant way of doing what you need.

    You can hide the axis line and "manually" create one line yourself:

    import matplotlib.lines as lines
    
    ax2.axis["bottom"].line.set_visible(False)
    
    p1 = ax2.axis["bottom"].line.get_extents().get_points()
    
    x1 = 0.058 * (p1[1][0]-p1[0][0]) / (1) + p1[0][0]
    x2 = 0.915 * (p1[1][0]-p1[0][0]) / (1) + p1[0][0]
    
    newL = lines.Line2D([x1,x2], [p1[0][1],p1[1][1]], transform=None, axes=ax2,color="k",linewidth=0.5)
    ax2.lines.extend([newL,])
    

    Which gives, in a simple example, something like this: enter image description here

    As opposed to:

    enter image description here

    Alternative

    One alternative for the creation of multiple axis is using spines (no parasite axis): https://matplotlib.org/stable/gallery/ticks_and_spines/multiple_yaxis_with_spines.html

    In this case, it is possible to do what you need simply by changing the bounds of the spines. For instance, by adding the following line to the code in the link

    par2.spines["right"].set_bounds(10,30)
    

    we get this:

    enter image description here

    Obviously, this does not strictly reply to the title of your question, and unfortunately, I do not know a proper way of doing it for new_fixed_axis as it can be done for the spines. I hope the "manually" created line solves your issue, in case nobody else comes with a better solution.