Hi
I'm trying to create plots which incorporate parallel conversion scales for two sets of units on the y-axis; using the two different styles of:
to replicate the style of the left-hand y-axes in the attached example images.
I'd like to find the simplest generic way of producing both of the above example plots, which also allows me to generate the y-axis conversion scales by defining the relationship between the two sets of units as a function (in this example: mmHg = kPa * 7.5).
If it's possible to add the third right-hand y axes (vapour concentration and water content) shown in these examples, which are unrelated to the left hand scales, this would be a bonus.
I've read related stackoverflow.com postings and examples on using multiple x and y axes using the twinx and twiny functions - e.g. here - as well as the Matplotlib cookbook, but I can't find an example which addresses this particular problem.
I'd be very grateful for any minimal working examples or links.
I'm using Matplotlib in Spyder 2.2.1 / Python 2.7.5
Many thanks in anticipation
Dave
In an Excel chart, can I have different Y-axis scales (a primary and secondary axis)? Yes, in Excel 2013 and Excel 2016 you can have two axes. Start by creating a chart with just one axis. Select the data series you wish to place on a secondary axis, by clicking on the series in the chart.
Select Plot > Multi-Panel/Axis: Multiple Y Axes.... Click the Multiple Y Axes... button on the 2D Graphs toolbar. Origin opens the plotmyaxes dialog box.
It is a kind of chart where you have more than 1 y-axis to be rendered on the same graph. When you have a significant difference in value ranges, multi-axis charts allow you to plot multiple data sets with different types of units. You can draw separate y-axis for each scale, or have multiple series on the same scale.
(Default) A linear scale on the Y axis represents equal distance and change on a chart. So for example, if grid lines are enabled, a change of three spaces on a line graph may represent an increase of 3MB of data.
For the first plot, I recommend axisartist
. The automatic scaling of the two y
-axis on the left-hand-side is achieved through a simple scaling factor that applies to the specified y
-limits. This first example is based on the explanations on parasite 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 will end up with this plot:
You can try to modify the above example to give you the second plot, too. One idea is, to reduce offset
to zero. However, with the axisartist
, certain tick functions are not supported. One of them is specifying if the ticks go inside or outside the axis.
Therefore, for the second plot, the following example (based on matplotlib: overlay plots with different scales?) is appropriate.
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()
This results in this figure:
Again, the set_tick_params(direction='out')
does not work with the axisartist
from the first example.
Somewhat counter-intuitive, both the y1
and y3
ticks have to be set to 'out'
. For y1
, this makes sense, and for y3
you have to remember that it started as a right-hand-side axis. Therefore, those ticks would appear outside (with the default 'in'
setting) when the axis is moved to the left.
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