0

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.

Emile Beukes
  • 82
  • 1
  • 6
  • 1
    `ax2.set_xticks([0.0, 0.3434, 0.63, 1.0])`Set the second xticks setting to [0.0,. Doesn't [0.0,..., .1.0] achieve the goal? – r-beginners Feb 22 '21 at 14:13
  • unfortunately not, setting first to 0.0 and last to 1.0 just stretches the whole thing, so there are no 'tails' as it were beyond the first and last ticks, but the first and last ticks now sit at 0.0 and 1.0, which is not what I need. The positions of the ticks need to stay fixed. I just want to force the axis line to start at the first tick and end at the last tick, instead of spanning the whole 'main' x-axis. – Emile Beukes Feb 22 '21 at 15:05

1 Answers1

1

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.

fdireito
  • 1,709
  • 1
  • 13
  • 19
  • Thanks a lot @fdireito! This does the trick. However, I got an AttributeError with the line `p1 = ax2.axis["bottom"].line.get_extents().get_points()` that told me 'BezierPath has no attribute get_extents()', so instead of 'retrieving' the y-coordinates of the original axis, I just played around until I got the correct y-position : `ax2.axis["bottom"].line.set_visible(False) line = lines.Line2D([0.058, 0.915], [-9, -9], lw=0.6, color='k', alpha=1) line.set_clip_on(False) ax2.add_line(line)` – Emile Beukes Feb 24 '21 at 16:59