Two y axes for a single plot - python

I'm trying to create a plot with two Y axes (left and right) for the same data, that is, one is a scaled version of the other. I would like also to preserve the tick positions and grid positions, so the grid will match the ticks at both sides.
I'm trying to do this by plotting twice the same data, one as-is and the other scaled, but they are not coincident.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(17, 27, 0.1)
y1 = 0.05 * x + 100
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x, y1, 'g-')
ax2.plot(x, y1/max(y1), 'g-')
ax1.set_xlabel('X data')
ax1.set_ylabel('Y data', color='g')
ax2.set_ylabel('Y data normalized', color='b')
plt.grid()
plt.show()
Any help will be appreciated.

Not sure if you can achieve this without getting ugly-looking numbers on your normalized axis. But if that doesn't bother you, try adding this to your code:
ax2.set_ylim([ax1.get_ylim()[0]/max(y1),ax1.get_ylim()[1]/max(y1)])
ax2.set_yticks(ax1.get_yticks()/max(y1))
Probably not the most elegant solution, but it scales your axis limits and tick positions similarly to what you do with the data itself so the grid matches both axes.

Related

Create a scaled secondary y-axis in Matplotlib

The objective is to plot a scatter plot and create secondary y-axis. Here, the secondary y-axis is just scaled copy of the original scatter plot.
Assume the scaling can be calculated
y2=y1/2.5
where, y1 and y2 is the y axis from the scatter plot,and scaled copy of the original scatter plot, respectively.
This can be visualized as below.
However, using the code below,
import numpy as np
import matplotlib.pyplot as plt
x, y = np.random.random((2,50))
fig, ax1 = plt.subplots()
ax1.scatter(x, y*10, c='b')
ax2 = ax1.twinx()
y2=y/2.5
ax2.plot(1, 1, 'w-')
ax1.set_xlabel('X1_z')
ax1.set_ylabel('x1_y', color='g')
ax2.set_ylabel('x2_y', color='r')
which produced
There are three issues
The secondary y-axis is not scaled properly
As expected but not intended the existence multiple horizontal line root from the secondary y-axis
Is there a possible way to create the scaled y-axis without the need of the line ax2.plot(1, 1, 'w-')
May I know how to handle this?
As suggested in the comment, using secondary_yaxis
x, y = np.random.random((2,50))
fig, ax = plt.subplots()
ax.scatter(x, y*10, c='b')
ax.set_xlabel('X1_z')
ax.set_ylabel('x1_y')
ax.set_title('Adding secondary y-axis')
def a2b(y):
return y/2.5
def b2a(y):
return 2.5*y
secax = ax.secondary_yaxis('right', functions=(a2b,b2a))
secax.set_ylabel('x2_y')
plt.show()
Produced

How to set equal number of ticks for two subplots?

I have two subplots of horizontal bars done in matplotlib. For the first subplot, the number of y-axis ticks is appropriate, but I'm unable to figure out why specifying number of ticks for the second subplot is coming out to be wrong. This is the code:
import matplotlib.pyplot as plt
import numpy as np
# Plot separate subplots for genders
fig, (axes1, axes2) = plt.subplots(nrows=1, ncols=2,
sharex=False,
sharey=False,
figsize=(15,10))
labels = list(out.index)
x = ["20%", "40%", "60%", "80%", "100%"]
y = np.arange(len(out))
width = 0.5
axes1.barh(y, female_distr, width, color="olive",
align="center", alpha=0.8)
axes1.ticks_params(nbins=6)
axes1.set_yticks(y)
axes1.set_yticklabels(labels)
axes1.set_xticklabels(x)
axes1.yaxis.grid(False)
axes1.set_xlabel("Occurence (%)")
axes1.set_ylabel("Language")
axes1.set_title("Language Distribution (Women)")
axes2.barh(y, male_distr, width, color="chocolate",
align="center", alpha=0.8)
axes2.locator_params(nbins=6)
axes2.set_yticks(y)
axes2.set_yticklabels(labels)
axes2.set_xticklabels(x)
axes2.yaxis.grid(False)
axes2.set_xlabel("Occurence (%)")
axes2.set_ylabel("Language")
axes2.set_title("Language Distribution (Men)")
The rest of the objects like out are simple data frames that I don't think need to be described here. The above code returns the following plot:
I would like the second subplot to have equal number of ticks but experimenting with nbins always results in either more or fewer ticks than the first subplot.
First, if you want your two plots to have the same x-axis, why not use sharex=True?
x_ticks = [0,20,40,60,80,100]
fig, (ax1,ax2) = plt.subplots(1,2, sharex=True)
ax1.set_xticks(x_ticks)
ax1.set_xticklabels(['{:.0f}%'.format(x) for x in x_ticks])
ax1.set_xlim(0,100)
ax1.grid(True, axis='x')
ax2.grid(True, axis='x')

How do I align gridlines for two y-axis scales using Matplotlib?

I'm plotting two datasets with different units on the y-axis. Is there a way to make the ticks and gridlines aligned on both y-axes?
The first image shows what I get, and the second image shows what I would like to get.
This is the code I'm using to plot:
import seaborn as sns
import numpy as np
import pandas as pd
np.random.seed(0)
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(pd.Series(np.random.uniform(0, 1, size=10)))
ax2 = ax1.twinx()
ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r')
I am not sure if this is the prettiest way to do it, but it does fix it with one line:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
np.random.seed(0)
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(pd.Series(np.random.uniform(0, 1, size=10)))
ax2 = ax1.twinx()
ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r')
# ADD THIS LINE
ax2.set_yticks(np.linspace(ax2.get_yticks()[0], ax2.get_yticks()[-1], len(ax1.get_yticks())))
plt.show()
I could solve it by deactivating ax.grid(None) in one of the grid`s axes:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(pd.Series(np.random.uniform(0, 1, size=10)))
ax2 = ax1.twinx()
ax2.plot(pd.Series(np.random.uniform(10, 20, size=10)), color='r')
ax2.grid(None)
plt.show()
I wrote this function that takes Matplotlib axes objects ax1, ax2, and floats minresax1 minresax2:
def align_y_axis(ax1, ax2, minresax1, minresax2):
""" Sets tick marks of twinx axes to line up with 7 total tick marks
ax1 and ax2 are matplotlib axes
Spacing between tick marks will be a factor of minresax1 and minresax2"""
ax1ylims = ax1.get_ybound()
ax2ylims = ax2.get_ybound()
ax1factor = minresax1 * 6
ax2factor = minresax2 * 6
ax1.set_yticks(np.linspace(ax1ylims[0],
ax1ylims[1]+(ax1factor -
(ax1ylims[1]-ax1ylims[0]) % ax1factor) %
ax1factor,
7))
ax2.set_yticks(np.linspace(ax2ylims[0],
ax2ylims[1]+(ax2factor -
(ax2ylims[1]-ax2ylims[0]) % ax2factor) %
ax2factor,
7))
It calculates and sets the ticks such that there are seven ticks. The lowest tick corresponds to the current lowest tick and increases the highest tick such that the separation between each tick is integer multiples of minrexax1 or minrexax2.
To make it general, you can set the total number of ticks you want by changing ever 7 you see to the total number of ticks, and change 6 to the total number of ticks minus 1.
I put a pull request in to incorporate some this into matplotlib.ticker.LinearLocator:
https://github.com/matplotlib/matplotlib/issues/6142
In the future (Matplotlib 2.0 perhaps?), try:
import matplotlib.ticker
nticks = 11
ax1.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks))
ax2.yaxis.set_major_locator(matplotlib.ticker.LinearLocator(nticks))
That should just work and choose convenient ticks for both y-axes.
I created a method to align the ticks of multiple y- axes (could be more than 2), with possibly different scales in different axes.
Below is an example figure:
There are 3 y- axes, one blue on the left, and a green and a red on the right. The 3 curves are plotted onto the y-axis with the corresponding color. Note that they all have very different order of magnitudes.
Left plot: No alignment.
Mid plot: Aligned at (approximately) the lower bound of each y axis.
Right plot: Aligned at specified values: 0 for blue, 2.2*1e8 for red, and 44 for green. Those are chosen arbitrarily.
What I'm doing is to scale each y array to be within the range of 1-100, then merge all scaled y-values into a single array, from which a new set of ticks is created using MaxNLocator. Then this new set of ticks is scaled back using the corresponding scaling factor to get the new ticks for each axis. If some specific alignment is required, y arrays are shifted before scaling, and shifted back afterwards.
Complete code here (the key function is alignYaxes()):
import matplotlib.pyplot as plt
import numpy as np
def make_patch_spines_invisible(ax):
'''Used for creating a 2nd twin-x axis on the right/left
E.g.
fig, ax=plt.subplots()
ax.plot(x, y)
tax1=ax.twinx()
tax1.plot(x, y1)
tax2=ax.twinx()
tax2.spines['right'].set_position(('axes',1.09))
make_patch_spines_invisible(tax2)
tax2.spines['right'].set_visible(True)
tax2.plot(x, y2)
'''
ax.set_frame_on(True)
ax.patch.set_visible(False)
for sp in ax.spines.values():
sp.set_visible(False)
def alignYaxes(axes, align_values=None):
'''Align the ticks of multiple y axes
Args:
axes (list): list of axes objects whose yaxis ticks are to be aligned.
Keyword Args:
align_values (None or list/tuple): if not None, should be a list/tuple
of floats with same length as <axes>. Values in <align_values>
define where the corresponding axes should be aligned up. E.g.
[0, 100, -22.5] means the 0 in axes[0], 100 in axes[1] and -22.5
in axes[2] would be aligned up. If None, align (approximately)
the lowest ticks in all axes.
Returns:
new_ticks (list): a list of new ticks for each axis in <axes>.
A new sets of ticks are computed for each axis in <axes> but with equal
length.
'''
from matplotlib.pyplot import MaxNLocator
nax=len(axes)
ticks=[aii.get_yticks() for aii in axes]
if align_values is None:
aligns=[ticks[ii][0] for ii in range(nax)]
else:
if len(align_values) != nax:
raise Exception("Length of <axes> doesn't equal that of <align_values>.")
aligns=align_values
bounds=[aii.get_ylim() for aii in axes]
# align at some points
ticks_align=[ticks[ii]-aligns[ii] for ii in range(nax)]
# scale the range to 1-100
ranges=[tii[-1]-tii[0] for tii in ticks]
lgs=[-np.log10(rii)+2. for rii in ranges]
igs=[np.floor(ii) for ii in lgs]
log_ticks=[ticks_align[ii]*(10.**igs[ii]) for ii in range(nax)]
# put all axes ticks into a single array, then compute new ticks for all
comb_ticks=np.concatenate(log_ticks)
comb_ticks.sort()
locator=MaxNLocator(nbins='auto', steps=[1, 2, 2.5, 3, 4, 5, 8, 10])
new_ticks=locator.tick_values(comb_ticks[0], comb_ticks[-1])
new_ticks=[new_ticks/10.**igs[ii] for ii in range(nax)]
new_ticks=[new_ticks[ii]+aligns[ii] for ii in range(nax)]
# find the lower bound
idx_l=0
for i in range(len(new_ticks[0])):
if any([new_ticks[jj][i] > bounds[jj][0] for jj in range(nax)]):
idx_l=i-1
break
# find the upper bound
idx_r=0
for i in range(len(new_ticks[0])):
if all([new_ticks[jj][i] > bounds[jj][1] for jj in range(nax)]):
idx_r=i
break
# trim tick lists by bounds
new_ticks=[tii[idx_l:idx_r+1] for tii in new_ticks]
# set ticks for each axis
for axii, tii in zip(axes, new_ticks):
axii.set_yticks(tii)
return new_ticks
def plotLines(x, y1, y2, y3, ax):
ax.plot(x, y1, 'b-')
ax.tick_params('y',colors='b')
tax1=ax.twinx()
tax1.plot(x, y2, 'r-')
tax1.tick_params('y',colors='r')
tax2=ax.twinx()
tax2.spines['right'].set_position(('axes',1.2))
make_patch_spines_invisible(tax2)
tax2.spines['right'].set_visible(True)
tax2.plot(x, y3, 'g-')
tax2.tick_params('y',colors='g')
ax.grid(True, axis='both')
return ax, tax1, tax2
#-------------Main---------------------------------
if __name__=='__main__':
# craft some data to plot
x=np.arange(20)
y1=np.sin(x)
y2=x/1000+np.exp(x)
y3=x+x**2/3.14
figure=plt.figure(figsize=(12,4),dpi=100)
ax1=figure.add_subplot(1, 3, 1)
axes1=plotLines(x, y1, y2, y3, ax1)
ax1.set_title('No alignment')
ax2=figure.add_subplot(1, 3, 2)
axes2=plotLines(x, y1, y2, y3, ax2)
alignYaxes(axes2)
ax2.set_title('Default alignment')
ax3=figure.add_subplot(1, 3, 3)
axes3=plotLines(x, y1, y2, y3, ax3)
alignYaxes(axes3, [0, 2.2*1e8, 44])
ax3.set_title('Specified alignment')
figure.tight_layout()
figure.show()
This code will ensure that grids from both axes align to each other, without having to hide gridlines from either set. In this example, it allows you to match whichever has the finer grid lines. This builds off of the idea from #Leo. Hope it helps!
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(pd.Series(np.random.uniform(0,1,size=10)))
ax2 = ax1.twinx()
ax2.plot(pd.Series(np.random.uniform(10,20,size=10)),color='r')
ax2.grid(None)
# Determine which plot has finer grid. Set pointers accordingly
l1 = len(ax1.get_yticks())
l2 = len(ax2.get_yticks())
if l1 > l2:
a = ax1
b = ax2
l = l1
else:
a = ax2
b = ax1
l = l2
# Respace grid of 'b' axis to match 'a' axis
b_ticks = np.linspace(b.get_yticks()[0],b.get_yticks()[-1],l)
b.set_yticks(b_ticks)
plt.show()
If you're using axis labels, Leo's solution can push them off the side, due to the precision of the numbers in the ticks.
So in addition to something like Leo's solution (repeated here),
ax2.set_yticks(np.linspace(ax2.get_yticks()[0],ax2.get_yticks()[-1],len(ax1.get_yticks())))
you can use the autolayout setting, as mentioned in this answer; e.g., earlier in your script you can update rcParams:
from matplotlib import rcParams
rcParams.update({'figure.autolayout': True})
In a few test cases, this appears to produce the expected result, with both lined-up ticks and labels fully contained in the output.
I had the same issue except this was for a secondary x axis. I solved by setting my secondary x axis equal to the limit of my primary axis.The example below is without setting the limit of the second axis equal to the first:ax2 = ax.twiny()
Once I set the limit of the second axis equal to the first ax2.set_xlim(ax.get_xlim()) here is my result:
fix the limits for both axis (from any number to any number)
divide both axis into same n parts
ax1.set_ylim(a,b)
ax1.set_yticks(np.linspace(a,b, n))
ax2.set_ylim(c,d)
ax2.set_yticks(np.linspace(c,d, n))

Adding y=x to a matplotlib scatter plot if I haven't kept track of all the data points that went in

Here's some code that does scatter plot of a number of different series using matplotlib and then adds the line y=x:
import numpy as np, matplotlib.pyplot as plt, matplotlib.cm as cm, pylab
nseries = 10
colors = cm.rainbow(np.linspace(0, 1, nseries))
all_x = []
all_y = []
for i in range(nseries):
x = np.random.random(12)+i/10.0
y = np.random.random(12)+i/5.0
plt.scatter(x, y, color=colors[i])
all_x.extend(x)
all_y.extend(y)
# Could I somehow do the next part (add identity_line) if I haven't been keeping track of all the x and y values I've seen?
identity_line = np.linspace(max(min(all_x), min(all_y)),
min(max(all_x), max(all_y)))
plt.plot(identity_line, identity_line, color="black", linestyle="dashed", linewidth=3.0)
plt.show()
In order to achieve this I've had to keep track of all the x and y values that went into the scatter plot so that I know where identity_line should start and end. Is there a way I can get y=x to show up even if I don't have a list of all the points that I plotted? I would think that something in matplotlib can give me a list of all the points after the fact, but I haven't been able to figure out how to get that list.
You don't need to know anything about your data per se. You can get away with what your matplotlib Axes object will tell you about the data.
See below:
import numpy as np
import matplotlib.pyplot as plt
# random data
N = 37
x = np.random.normal(loc=3.5, scale=1.25, size=N)
y = np.random.normal(loc=3.4, scale=1.5, size=N)
c = x**2 + y**2
# now sort it just to make it look like it's related
x.sort()
y.sort()
fig, ax = plt.subplots()
ax.scatter(x, y, s=25, c=c, cmap=plt.cm.coolwarm, zorder=10)
Here's the good part:
lims = [
np.min([ax.get_xlim(), ax.get_ylim()]), # min of both axes
np.max([ax.get_xlim(), ax.get_ylim()]), # max of both axes
]
# now plot both limits against eachother
ax.plot(lims, lims, 'k-', alpha=0.75, zorder=0)
ax.set_aspect('equal')
ax.set_xlim(lims)
ax.set_ylim(lims)
fig.savefig('/Users/paul/Desktop/so.png', dpi=300)
Et voilĂ 
In one line:
ax.plot([0,1],[0,1], transform=ax.transAxes)
No need to modify the xlim or ylim.
Starting with matplotlib 3.3 this has been made very simple with the axline method which only needs a point and a slope. To plot x=y:
ax.axline((0, 0), slope=1)
You don't need to look at your data to use this because the point you specify (i.e. here (0,0)) doesn't actually need to be in your data or plotting range.
If you set scalex and scaley to False, it saves a bit of bookkeeping. This is what I have been using lately to overlay y=x:
xpoints = ypoints = plt.xlim()
plt.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
or if you've got an axis:
xpoints = ypoints = ax.get_xlim()
ax.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
Of course, this won't give you a square aspect ratio. If you care about that, go with Paul H's solution.

Add second axis to polar plot

I try to plot two polar plots in one figure. See code below:
fig = super(PlotWindPowerDensity, self).get_figure()
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindSpeedDirectionAxes(fig, rect)
self.values_dict = collections.OrderedDict(sorted(self.values_dict.items()))
values = self.values_dict.items()
di, wpd = zip(*values)
wpd = np.array(wpd).astype(np.double)
wpdmask = np.isfinite(wpd)
theta = self.radar_factory(int(len(wpd)))
# spider plot
ax.plot(theta[wpdmask], wpd[wpdmask], color = 'b', alpha = 0.5)
ax.fill(theta[wpdmask], wpd[wpdmask], facecolor = 'b', alpha = 0.5)
# bar plot
ax.plot_bar(table=self.table, sectors=self.sectors, speedbins=self.wpdbins, option='wind_power_density', colorfn=get_sequential_colors)
fig.add_axes(ax)
return fig
The length of the bar is the data base (how many sampling points for this sector). The colors of the bars show the frequency of certain value bins (eg. 2.5-5 m/s) in the correspondent sector (blue: low, red: high). The blue spider plot shows the mean value for each sector.
In the shown figure, the values of each plot are similar, but this is rare. I need to assign the second plot to another axis and show this axis in another direction.
EDIT:
After the nice answer of Joe, i get the result of the figure.
That's almost everything i wanted to achieve. But there are some points i wasn't able to figure out.
The plot is made for dynamicly changing data bases. Therefore i need a dynamic way to get the same location of the circles. Till now I solve it with:
start, end = ax2.get_ylim()
ax2.yaxis.set_ticks(np.arange(0, end, end / len(ax.yaxis.get_ticklocs())))
means: for second axis i alter the ticks in order to fit the ticklocs to the one's of first axis.
In most cases i get some decimal places, but i don't want that, because it corrupts the clearness of the plot. Is there a way to solve this problem more smartly?
The ytics (the radial one's) range from 0 to the next-to-last circle. How can i achieve that the values range from the first circle to the very last (the border)? The same like for the first axis.
So, as I understand it, you want to display data with very different magnitudes on the same polar plot. Basically you're asking how to do something similar to twinx for polar axes.
As an example to illustrate the problem, it would be nice to display the green series on the plot below at a different scale than the blue series, while keeping them on the same polar axes for easy comparison.:
import numpy as np
import matplotlib.pyplot as plt
numpoints = 30
theta = np.linspace(0, 2*np.pi, numpoints)
r1 = np.random.random(numpoints)
r2 = 5 * np.random.random(numpoints)
params = dict(projection='polar', theta_direction=-1, theta_offset=np.pi/2)
fig, ax = plt.subplots(subplot_kw=params)
ax.fill_between(theta, r2, color='blue', alpha=0.5)
ax.fill_between(theta, r1, color='green', alpha=0.5)
plt.show()
However, ax.twinx() doesn't work for polar plots.
It is possible to work around this, but it's not very straight-forward. Here's an example:
import numpy as np
import matplotlib.pyplot as plt
def main():
numpoints = 30
theta = np.linspace(0, 2*np.pi, numpoints)
r1 = np.random.random(numpoints)
r2 = 5 * np.random.random(numpoints)
params = dict(projection='polar', theta_direction=-1, theta_offset=np.pi/2)
fig, ax = plt.subplots(subplot_kw=params)
ax2 = polar_twin(ax)
ax.fill_between(theta, r2, color='blue', alpha=0.5)
ax2.fill_between(theta, r1, color='green', alpha=0.5)
plt.show()
def polar_twin(ax):
ax2 = ax.figure.add_axes(ax.get_position(), projection='polar',
label='twin', frameon=False,
theta_direction=ax.get_theta_direction(),
theta_offset=ax.get_theta_offset())
ax2.xaxis.set_visible(False)
# There should be a method for this, but there isn't... Pull request?
ax2._r_label_position._t = (22.5 + 180, 0.0)
ax2._r_label_position.invalidate()
# Ensure that original axes tick labels are on top of plots in twinned axes
for label in ax.get_yticklabels():
ax.figure.texts.append(label)
return ax2
main()
That does what we want, but it looks fairly bad at first. One improvement would be to the tick labels to correspond to what we're plotting:
plt.setp(ax2.get_yticklabels(), color='darkgreen')
plt.setp(ax.get_yticklabels(), color='darkblue')
However, we still have the double-grids, which are rather confusing. One easy way around this is to manually set the r-limits (and/or r-ticks) such that the grids will fall on top of each other. Alternately, you could write a custom locator to do this automatically. Let's stick with the simple approach here:
ax.set_rlim([0, 5])
ax2.set_rlim([0, 1])
Caveat: Because shared axes don't work for polar plots, the implmentation I have above will have problems with anything that changes the position of the original axes. For example, adding a colorbar to the figure will cause all sorts of problems. It's possible to work around this, but I've left that part out. If you need it, let me know, and I'll add an example.
At any rate, here's the full, stand-alone code to generate the final figure:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
def main():
numpoints = 30
theta = np.linspace(0, 2*np.pi, numpoints)
r1 = np.random.random(numpoints)
r2 = 5 * np.random.random(numpoints)
params = dict(projection='polar', theta_direction=-1, theta_offset=np.pi/2)
fig, ax = plt.subplots(subplot_kw=params)
ax2 = polar_twin(ax)
ax.fill_between(theta, r2, color='blue', alpha=0.5)
ax2.fill_between(theta, r1, color='green', alpha=0.5)
plt.setp(ax2.get_yticklabels(), color='darkgreen')
plt.setp(ax.get_yticklabels(), color='darkblue')
ax.set_ylim([0, 5])
ax2.set_ylim([0, 1])
plt.show()
def polar_twin(ax):
ax2 = ax.figure.add_axes(ax.get_position(), projection='polar',
label='twin', frameon=False,
theta_direction=ax.get_theta_direction(),
theta_offset=ax.get_theta_offset())
ax2.xaxis.set_visible(False)
# There should be a method for this, but there isn't... Pull request?
ax2._r_label_position._t = (22.5 + 180, 0.0)
ax2._r_label_position.invalidate()
# Bit of a hack to ensure that the original axes tick labels are on top of
# whatever is plotted in the twinned axes. Tick labels will be drawn twice.
for label in ax.get_yticklabels():
ax.figure.texts.append(label)
return ax2
if __name__ == '__main__':
main()
Just to add onto #JoeKington 's (great) answer, I found that the "hack to ensure that the original axes tick labels are on top of whatever is plotted in the twinned axes" didn't work for me so as an alternative I've used:
from matplotlib.ticker import MaxNLocator
#Match the tick point locations by setting the same number of ticks in the
# 2nd axis as the first
ax2.yaxis.set_major_locator(MaxNLocator(nbins=len(ax1.get_yticks())))
#Set the last tick as the plot limit
ax2.set_ylim(0, ax2.get_yticks()[-1])
#Remove the tick label at zero
ax2.yaxis.get_major_ticks()[0].label1.set_visible(False)

Categories