How do I align gridlines for two y-axis scales using Matplotlib?
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()
Double y axis with matching gridlines
Aligning the yticks
of the second axis manually is tricky because (a) not all yticks
are shown (do for example a print(ax.get_yticks())
and compare to your plot) and (b) because set_yticks()
also affects ylims
. Replacing your sections #The math
and #My best attempt
with the below works for me:
# The math
ylim1 = ax.get_ylim()
len1 = ylim1[1]-ylim1[0]
yticks1 = ax.get_yticks()
rel_dist = [(y-ylim1[0])/len1 for y in yticks1]
ylim2 = ax2.get_ylim()
len2 = ylim2[1]-ylim2[0]
yticks2 = [ry*len2+ylim2[0] for ry in rel_dist]
#My best attempt
ax2.set_yticks(yticks2)
ax2.set_ylim(ylim2) #<-- this line is needed to re-adjust the limits to the original values
ax.yaxis.grid(which="major", color='black', linestyle='-')
ax2.yaxis.grid(which="major", color='green', linestyle='--')
ax.legend(loc='upper left')
ax2.legend(loc='upper right')
and the resulting graph looks like this:Hope this helps.
How do I align gridlines for two y-axis scales using Matplotlib?
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()
matplotlib align twinx tick marks
You need to manually set the yticks
as it stands these are automatically calculated resulting in a variation. Adding something like this:
ax1.set_yticks(np.linspace(ax1.get_ybound()[0], ax1.get_ybound()[1], 5))
ax2.set_yticks(np.linspace(ax2.get_ybound()[0], ax2.get_ybound()[1], 5))
where we set the ytick
locations using an array of 5 points between the bounds of the axis. Since you have a histogram you could just set the lower value to zero in each case, and you may want to have the upper bound somewhat larger, so I would instead haveax1.set_yticks(np.linspace(0, ax1.get_ybound()[1]+1, 5))
ax2.set_yticks(np.linspace(0, ax2.get_ybound()[1]+1, 5))
Giving a plot (with a change of color and transparency (alpha) for clarity): How to put grid lines from the secondary axis behind the primary plot?
Your desired drawing order is (first is most to the back)
- grid for axes
- grid for twin axes
- plot in axes
- plot in twin axes
What this means is that you need 4 axes instead of two.you can't interleave the drawing orders of artists from one Axes with those from another
- axes for grid of primary y scale
- axes for grid of secondary y scale
- axes for plot on primary y scale
- axes for plot on secondary y scale
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
foo = np.random.randn(1000)
fig, ax1a = plt.subplots() # ax1a for the histogram grid
ax2a = ax1a.twinx() # ax2a for the cumulative step grid
ax1b = ax1a.twinx() # ax1b for the histogram plot
ax2b = ax1a.twinx() # ax2a for the cumulative step plot
# Link the respective y-axes for grid and plot
ax1a.get_shared_y_axes().join(ax1a, ax1b)
ax2a.get_shared_y_axes().join(ax2a, ax2b)
# Remove ticks and labels and set which side to label
ticksoff = dict(labelleft=False, labelright=False, left=False, right=False)
ax1a.tick_params(axis="y", **ticksoff)
ax2a.tick_params(axis="y", **ticksoff)
ax1b.tick_params(axis="y", labelleft=True, labelright=False, left=True, right=False)
ax2b.tick_params(axis="y", labelleft=False, labelright=True, left=False, right=True)
# Spines off
for ax in [ax1a, ax2a, ax1b]:
for k,v in ax.spines.items():
v.set_visible(False)
ax1b.hist(foo, bins=50)
ax2b.hist(
foo, bins=50, density=True, cumulative=True, histtype="step", color="tab:orange"
)
ax1a.grid()
ax2a.grid()
plt.show()
trouble aligning ticks for matplotlib twinx axes
Aligning the tick locations of two different scales would mean to give up on the nice automatic tick locator and set the ticks to the same positions on the secondary axes as on the original one.
The idea is to establish a relation between the two axes scales using a function and set the ticks of the second axes at the positions of those of the first.
import matplotlib.pyplot as plt
import matplotlib.ticker
fig, ax = plt.subplots()
# creates double-y axis
ax2 = ax.twinx()
ax.plot(range(5), [1,2,3,4,5])
ax2.plot(range(6), [13,17,14,13,16,12])
ax.grid()
l = ax.get_ylim()
l2 = ax2.get_ylim()
f = lambda x : l2[0]+(x-l[0])/(l[1]-l[0])*(l2[1]-l2[0])
ticks = f(ax.get_yticks())
ax2.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(ticks))
plt.show()
Note that this is a solution for the general case and it might result in totally unreadable labels depeding on the use case. If you happen to have more a priori information on the axes range, better solutions may be possible.Also see this question for a case where automatic tick locations of the first axes is sacrificed for an easier setting of the secondary axes tick locations.
How to align the bar and line in matplotlib two y-axes chart?
Just change the final line to:
ax2.plot(ax.get_xticks(),
df[['sales_gr','net_pft_gr']].values,
linestyle='-',
marker='o', linewidth=2.0)
You will be all set.I don't quite get your second question. The 1st and 2nd y axis are of different scale, what do you mean by aligning them to the same line? They can't be aligned to the same grid line (yes you can but the right axis will look ugly, having values like 0.687 and alike). Anyway, you can do:
ax.set_ylim((-10, 80.))
to align them, and the plot now looks ugly: Matplotlib -- twiny: how to align values of two x-axes in one plot?
Yay! I've managed to get the sought result without defining a new scale class! Here are the relevant code parts which have been added/modified in the script from the question (the variable step
will be later read from the user command line input, or I might find another way of automated tick frequency setting):
x_ut = []
x_phi = []
x_phi_ticks = []
x_phi_ticklabels = []
y_brightness = []
# populate lists for the phase angle ticks and labels
i = 0
step = 15
while i <= (len(x_ut)-step):
x_phi_ticks.append(x_ut[i])
x_phi_ticklabels.append(x_phi[i])
i += step
x_phi_ticks.append(x_ut[-1])
x_phi_ticklabels.append(x_phi[-1])
# plot'em all
fig, ax1 = plt.subplots()
ax1.plot(x_ut, y_brightness, marker='o', label='apparent brightness')
ax1.xaxis.set_major_locator(dates.MinuteLocator(interval=1))
ax1.xaxis.set_major_formatter(dates.DateFormatter('%H:%M'))
ax1.tick_params(axis='x', rotation=45)
ax1.minorticks_on()
ax1.legend()
ax1.grid(which='major', linestyle='-', color='#000000')
ax1.grid(which='minor', linestyle='--')
ax1.set_xlabel('time [h:m, UT]')
ax1.set_ylabel('apparent brightness [mag, CR]')
ax2 = ax1.twiny()
ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks(x_phi_ticks)
ax2.set_xticklabels(x_phi_ticklabels)
ax2.set_xlabel('phase angle (phi) [deg]')
plt.gca().invert_yaxis()
plt.tight_layout(pad=0)
plt.show()
Related Topics
How to Log a Python Error with Debug Information
"Pip Install --Editable ./" VS "Python Setup.Py Develop"
Scipy Curve_Fit Doesn't Like Math Module
Use Scikit-Learn to Classify into Multiple Categories
Python [Errno 98] Address Already in Use
Rotating a Two-Dimensional Array in Python
Django Post_Save() Signal Implementation
When to Use Get, Get_Queryset, Get_Context_Data in Django
Writing Unit Tests in Python: How to Start
Change to Sudo User Within a Python Script
Wrapping Long Y Labels in Matplotlib Tight Layout Using Setp
Pycharm Error: 'No Module' When Trying to Import Own Module (Python Script)
What Does Model.Train() Do in Pytorch