Home > Mobile >  Matplotlib: Draw second y-axis with different length
Matplotlib: Draw second y-axis with different length

Time:12-05

I'm trying to make a matplotlib plot with a second y-axis. This works so far, but I was wondering, wether it was possible to shorten the second y-axis?

Furthermore, I struggle on some other formatting issues.

a) I want to draw an arrow on the second y-axis, just as drawn on the first y-axis.

b) I want to align the second y-axis at -1, so that the intersection of x- and 2nd y-axis is at(...; -1)

c) The x-axis crosses the x- and y-ticks at the origin, which I want to avoid.

d) How can I get a common legend for both y-axis?

Here is my code snippet so far.

fig, ax = plt.subplots()
bx = ax.twinx() # 2nd y-axis

ax.spines['bottom'].set_position(('data',0))
ax.spines['left'].set_position(('data',0)) 
ax.xaxis.set_ticks_position('bottom')

bx.spines['left'].set_position(('data',-1)) 
bx.spines['bottom'].set_position(('data',-1))

ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
bx.spines["top"].set_visible(False)
bx.spines["bottom"].set_visible(False)
bx.spines["left"].set_visible(False)

## Graphs
x_val = np.arange(0,10)
y_val = 0.1*x_val
ax.plot(x_val, y_val, 'k--')
bx.plot(x_val, -y_val 1, color = 'purple')

## Arrows
ms=2
#ax.plot(1, 0, ">k", ms=ms, transform=ax.get_yaxis_transform(), clip_on=False)
ax.plot(0, 1, "^k", ms=ms, transform=ax.get_xaxis_transform(), clip_on=False)
bx.plot(1, 1, "^k", ms=ms, transform=bx.get_xaxis_transform(), clip_on=False)

plt.ylim((-1, 1.2))
bx.set_yticks([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5])

## Legend
ax.legend([r'$A_{hull}$'], frameon=False, 
      loc='upper left', bbox_to_anchor=(0.2, .75))

plt.show()

I've uploaded a screenshot of my plot so far, annotating the questioned points.

enter image description here

EDIT: I've changed the plotted values in the code snippet so that the example is easier to reproduce. However, the question is more or less only related to formatting issues so that the acutual values are not too important. Image is not changed, so don't be surprised when plotting the edited values, the graphs will look differently.

CodePudding user response:

To avoid the strange overlap at x=0 and y=0, you could leave out the calls to ax.spines[...].set_position(('data',0)). You can change the transforms that place the arrows. Explicitly setting the x and y limits to start at 0 will also have the spines at those positions.

ax2.set_bounds(...) shortens the right y-axis.

To put items in the legend, each plotted item needs a label. get_legend_handles_labels can fetch the handles and labels of both axes, which can be combined in a new legend.

Renaming bx to something like ax2 makes the code easier to compare with existing example code. In matplotlib it often also helps to first put the plotting code and only later changes to limits, ticks and embellishments.

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

fig, ax = plt.subplots()
ax2 = ax.twinx()  # 2nd y-axis

## Graphs
x_val = np.arange(0, 10)
y_val = 0.1 * x_val
ax.plot(x_val, y_val, 'k--', label=r'$A_{hull}$')
ax2.plot(x_val, -y_val   1, color='purple', label='right axis')

ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax2.spines["top"].set_visible(False)
ax2.spines["bottom"].set_visible(False)
ax2.spines["left"].set_visible(False)
ax2_upper_bound = 0.55
ax2.spines["right"].set_bounds(-1, ax2_upper_bound)  # shorten the right y-axis

## add arrows to spines
ms = 2
# ax.plot(1, 0, ">k", ms=ms, transform=ax.get_yaxis_transform(), clip_on=False)
ax.plot(0, 1, "^k", ms=ms, transform=ax.transAxes, clip_on=False)
ax2.plot(1, ax2_upper_bound, "^k", ms=ms, transform=ax2.get_yaxis_transform(), clip_on=False)

# set limits to the axes
ax.set_xlim(xmin=0)
ax.set_ylim(ymin=0)
ax2.set_ylim((-1, 1.2))
ax2.set_yticks(np.arange(-1, 0.5001, 0.25))

## Legend
handles1, labels1 = ax.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
ax.legend(handles1   handles2, labels1   labels2, frameon=False,
          loc='upper left', bbox_to_anchor=(0.2, .75))

plt.show()

updated plot

  • Related