I need two overlay two datasets with different Y-axis scales in Matplotlib. The data contains both positive and negative values. I want the two axes to share one origin, but Matplotlib does not align the two scales by default.
import numpy as np import matplotlib.pyplot as plt fig = plt.figure() ax1 = fig.add_subplot(111) ax2 = ax1.twinx() ax1.bar(range(6), (2, -2, 1, 0, 0, 0)) ax2.plot(range(6), (0, 2, 8, -2, 0, 0)) plt.show()
I suppose it is possible to perform some computation with .get_ylim()
and .set_ylim()
two align the two scales. Is there an easier solution?
Sometimes it is natural to have more than one distinct group of Axes grids, in which case Matplotlib has the concept of SubFigure : SubFigure. A virtual figure within a figure.
The plt. axis() method allows you to set the x and y limits with a single call, by passing a list which specifies [xmin, xmax, ymin, ymax] : In [11]: plt.
use the align_yaxis() function:
import numpy as np import matplotlib.pyplot as plt def align_yaxis(ax1, v1, ax2, v2): """adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1""" _, y1 = ax1.transData.transform((0, v1)) _, y2 = ax2.transData.transform((0, v2)) inv = ax2.transData.inverted() _, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2)) miny, maxy = ax2.get_ylim() ax2.set_ylim(miny+dy, maxy+dy) fig = plt.figure() ax1 = fig.add_subplot(111) ax2 = ax1.twinx() ax1.bar(range(6), (2, -2, 1, 0, 0, 0)) ax2.plot(range(6), (0, 2, 8, -2, 0, 0)) align_yaxis(ax1, 0, ax2, 0) plt.show()
In order to ensure that the y-bounds are maintained (so no data points are shifted off the plot), and to balance adjustment of both y-axes, I made some additions to @HYRY's answer:
def align_yaxis(ax1, v1, ax2, v2): """adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1""" _, y1 = ax1.transData.transform((0, v1)) _, y2 = ax2.transData.transform((0, v2)) adjust_yaxis(ax2,(y1-y2)/2,v2) adjust_yaxis(ax1,(y2-y1)/2,v1) def adjust_yaxis(ax,ydif,v): """shift axis ax by ydiff, maintaining point v at the same location""" inv = ax.transData.inverted() _, dy = inv.transform((0, 0)) - inv.transform((0, ydif)) miny, maxy = ax.get_ylim() miny, maxy = miny - v, maxy - v if -miny>maxy or (-miny==maxy and dy > 0): nminy = miny nmaxy = miny*(maxy+dy)/(miny+dy) else: nmaxy = maxy nminy = maxy*(miny+dy)/(maxy+dy) ax.set_ylim(nminy+v, nmaxy+v)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With