Multiple Y-axis conversion scales
Hello
I'm trying to create plots that include parallel conversion scales for two sets of y-axis units; using two different styles:
- offset ("stray") y-axis and
- superimposed / shared y-axes
to reproduce the style of the left y-axes in the attached example images.
I would like to find the simplest common way to create both of the above plots, which also allows me to generate y-axis transformation scales by defining the relationship between the two sets of units as a function (in this example: mmHg = kPa * 7.5).
If in these examples it is possible to add the third right y-axes (vapor concentration and water content) that are not related to the left hand weights, that would be a bonus.
I have read the related stackoverflow.com posts and examples of using multiple x and y axes using double and close functions - for example here - and the Matplotlib cookbook, but I cannot find an example that solves this problem.
I would greatly appreciate any minimal working examples or links.
I am using Matplotlib in Spyder 2.2.1 / Python 2.7.5
Thanks a lot pending
Dave
source to share
For the first graph, I recommend axisartist
. The autoscaling of the axis y
on the left is achieved with a simple scaling factor that is applied to the specified y
-limits. This first example is based on the explanations of parasitic axes :
import numpy as np
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt
# initialize the three axis:
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(left=0.25)
par1 = host.twinx()
par2 = host.twinx()
# secify the offset for the left-most axis:
offset = -60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="left", axes=par2, offset=(offset, 0))
par2.axis["right"].toggle(all=True)
# data ratio for the two left y-axis:
y3_to_y1 = 1/7.5
# y-axis limits:
YLIM = [0.0, 150.0,
0.0, 150.0]
# set up dummy data
x = np.linspace(0,70.0,70.0)
y1 = np.asarray([xi**2.0*0.032653 for xi in x])
y2 = np.asarray([xi**2.0*0.02857 for xi in x])
# plot data on y1 and y2, respectively:
host.plot(x,y1,'b')
par1.plot(x,y2,'r')
# specify the axis limits:
host.set_xlim(0.0,70.0)
host.set_ylim(YLIM[0],YLIM[1])
par1.set_ylim(YLIM[2],YLIM[3])
# when specifying the limits for the left-most y-axis
# you utilize the conversion factor:
par2.set_ylim(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1)
# set y-ticks, use np.arange for defined deltas
# add a small increment to the last ylim value
# to ensure that the last value will be a tick
host.set_yticks(np.arange(YLIM[0],YLIM[1]+0.001,10.0))
par1.set_yticks(np.arange(YLIM[2],YLIM[3]+0.001,10.0))
par2.set_yticks(np.arange(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1+0.001, 2.0))
plt.show()
You end up with this plot:
You can try modifying the above example to get the second plot. One idea is to reduce offset
to zero. However, axisartist
some of the tick functions are not supported . One of them indicates whether the pliers are in or out of axis.
So the following example is suitable for the second plot (based on matplotlib: overlays with different scales? ).
import numpy as np
import matplotlib.pyplot as plt
# initialize the three axis:
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax3 = ax1.twinx()
# data ratio for the two left y-axis:
y3_to_y1 = 1/7.5
# y-axis limits:
YLIM = [0.0, 150.0,
0.0, 150.0]
# set up dummy data
x = np.linspace(0,70.0,70.0)
y1 = np.asarray([xi**2.0*0.032653 for xi in x])
y2 = np.asarray([xi**2.0*0.02857 for xi in x])
# plot the data
ax1.plot(x,y1,'b')
ax2.plot(x,y2,'r')
# define the axis limits
ax1.set_xlim(0.0,70.0)
ax1.set_ylim(YLIM[0],YLIM[1])
ax2.set_ylim(YLIM[2],YLIM[3])
# when specifying the limits for the left-most y-axis
# you utilize the conversion factor:
ax3.set_ylim(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1)
# move the 3rd y-axis to the left (0.0):
ax3.spines['right'].set_position(('axes', 0.0))
# set y-ticks, use np.arange for defined deltas
# add a small increment to the last ylim value
# to ensure that the last value will be a tick
ax1.set_yticks(np.arange(YLIM[0],YLIM[1]+0.001,10.0))
ax2.set_yticks(np.arange(YLIM[2],YLIM[3]+0.001,10.0))
ax3.set_yticks(np.arange(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1+0.001, 2.0))
# for both letf-hand y-axis move the ticks to the outside:
ax1.get_yaxis().set_tick_params(direction='out')
ax3.get_yaxis().set_tick_params(direction='out')
plt.show()
As a result of this drawing:
Again, set_tick_params(direction='out')
doesn't work with axisartist
from the first example.
Somewhat counterintuitively, both tags y1
and y3
should be set to 'out'
. For y1
this it makes sense, and for y3
you have to remember that it started out as a right axis. Therefore, these ticks will be displayed outside (with the default setting 'in'
) when the axis is moved to the left.
source to share