How to set equal number of ticks for two subplots? - python

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')

Related

How to duplicate the legend of one subplot into another subplot in matplotlib

I'm using Matplotlib to plot some simple figure. An exmaple is illustrated like follows:
x = np.linspace(0, 5, 1000)
fig =plt.figure(figsize=(7,7))
ax =plt.subplot(211)
ax.plot(x, np.sin(x), '-b', label='Sine')
ax.axis('equal')
leg1 = ax.legend(loc= (0.8,0.85));
leg2 = ax.legend(loc= (0.8,-0.15));
leg2.set_zorder(14)
ax.add_artist(leg1)
ax.add_artist(leg2)
ax =plt.subplot(212)
I want to keep the upper and lower subplot both have the same legend as the subplot 1.
However, when I turn down the leg2 in order to within the second subplot, it is covered by the canvas. Here is my question, Is there any method to duplicate the legend of one subplot to another subplot?
The problem is that the second legend is part of the first subplot, and the first subplot is completely behind the second.
Ususally, you would add the legend to the second subplot instead of the first.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 1000)
fig =plt.figure(figsize=(7,7))
ax =plt.subplot(211)
ax.plot(x, np.sin(x), '-b', label='Sine')
leg1 = ax.legend(loc= (0.8,0.85))
ax2 =plt.subplot(212)
leg2 = ax2.legend(*ax.get_legend_handles_labels(), loc= (0.8,0.85))
plt.show()

How to use twinx and still get square plot

How do I show a plot with twin axes such that the aspect of the top and right axes are 'equal'. For example, the following code will produce a square plot
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
But this changes as soon as you use the twinx function.
ax2 = ax.twinx()
ax2.set_ylim([0,2])
ax3 = ax.twiny()
ax3.set_xlim([0,2])
Using set_aspect('equal') on ax2 and ax3 seems to force it the the aspect of ax, but set_aspect(0.5) doesn't seem to change anything either.
Put simply, I would like the plot to be square, the bottom and left axes to run from 0 to 1 and the top and right axes to run from 0 to 2.
Can you set the aspect between two twined axes? I've tried stacking the axes:
ax3 = ax2.twiny()
ax3.set_aspect('equal')
I've also tried using the adjustable keyword in set_aspect:
ax.set_aspect('equal', adjustable:'box-forced')
The closest I can get is:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal', adjustable='box-forced')
ax.plot([0,1],[0,1])
ax2=ax.twinx()
ax3 = ax2.twiny()
ax3.set_aspect(1, adjustable='box-forced')
ax2.set_ylim([0,2])
ax3.set_xlim([0,2])
ax.set_xlim([0,1])
ax.set_ylim([0,1])
Which produces:
I would like to remove the extra space to the right and left of the plot
It seems overly complicated to use two different twin axes to get two independent set of axes. If the aim is to create one square plot with one axis on each side of the plot, you may use two axes, both at the same position but with different scales. Both can then be set to have equal aspect ratios.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
ax2 = fig.add_axes(ax.get_position())
ax2.set_facecolor("None")
ax2.set_aspect('equal')
ax2.plot([2,0],[0,2], color="red")
ax2.tick_params(bottom=0, top=1, left=0, right=1,
labelbottom=0, labeltop=1, labelleft=0, labelright=1)
plt.show()

matplotlib: reduce axes width in subplots

I have a matplotlib bar chart, which bars are colored according to some rules through a colormap. I need a colorbar on the right of the main axes, so I added a new axes with
fig, (ax, ax_cbar) = plt.subplots(1,2)
and managed to draw my color bar in the ax_bar axes, while I have my data displayed in the ax axes. Now I need to reduce the width of the ax_bar, because it looks like this:
How can I do?
Using subplots will always divide your figure equally. You can manually divide up your figure in a number of ways. My preferred method is using subplot2grid.
In this example, we are setting the figure to have 1 row and 10 columns. We then set ax to be the start at row,column = (0,0) and have a width of 9 columns. Then set ax_cbar to start at (0,9) and has by default a width of 1 column.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
num_columns = 10
ax = plt.subplot2grid((1,num_columns), (0,0), colspan=num_columns-1)
ax_cbar = plt.subplot2grid((1,num_columns), (0,num_columns-1))
The ususal way to add a colorbar is by simply putting it next to the axes:
fig.colorbar(sm)
where fig is the figure and sm is the scalar mappable to which the colormap refers. In the case of the bars, you need to create this ScalarMappable yourself. Apart from that there is no need for complex creation of multiple axes.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
fig , ax = plt.subplots()
x = [0,1,2,3]
y = np.array([34,40,38,50])*1e3
norm = matplotlib.colors.Normalize(30e3, 60e3)
ax.bar(x,y, color=plt.cm.plasma_r(norm(y)) )
ax.axhline(4.2e4, color="gray")
ax.text(0.02, 4.2e4, "42000", va='center', ha="left", bbox=dict(facecolor="w",alpha=1),
transform=ax.get_yaxis_transform())
sm = plt.cm.ScalarMappable(cmap=plt.cm.plasma_r, norm=norm)
sm.set_array([])
fig.colorbar(sm)
plt.show()
If you do want to create a special axes for the colorbar yourself, the easiest method would be to set the width already inside the call to subplots:
fig , (ax, cax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios" : [10,1]})
and later put the colorbar to the cax axes,
fig.colorbar(sm, cax=cax)
Note that the following questions have been asked for this homework assignment already:
Point picker event_handler drawing line and displaying coordinates in matplotlib
Matplotlib's widget to select y-axis value and change barplot
Display y axis value horizontal line drawn In bar chart
How to change colors automatically once a parameter is changed
Interactively Re-color Bars in Matplotlib Bar Chart using Confidence Intervals

Two y axes for a single plot

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.

Use matplotlib: plot error bars on two y axes

I'd like to plot a series with x and y error bars, then plot a second series with x and y error bars on a second y axis all on the same subplot. Can this be done with matplotlib?
import matplotlib.pyplot as plt
plt.figure()
ax1 = plt.errorbar(voltage, dP, xerr=voltageU, yerr=dPU)
ax2 = plt.errorbar(voltage, current, xerr=voltageU, yerr=currentU)
plt.show()
Basically, I'd like to put ax2 on a second axis and have the scale on the right side.
Thanks!
twinx() is your friend for adding a secondary y-axis, e.g.:
import matplotlib.pyplot as pl
import numpy as np
pl.figure()
ax1 = pl.gca()
ax1.errorbar(np.arange(10), np.arange(10), xerr=np.random.random(10), yerr=np.random.random(10), color='g')
ax2 = ax1.twinx()
ax2.errorbar(np.arange(10), np.arange(10)+5, xerr=np.random.random(10), yerr=np.random.random(10), color='r')
There is not a lot of documentation except for:
matplotlib.pyplot.twinx(ax=None)
Make a second axes that shares the x-axis. The new axes will overlay ax (or the current axes if ax is None). The ticks for ax2 will be placed on the right, and the ax2 instance is returned.
I was struggling to share the x-axis, but thank you #Bart you saved me!
The simple solution is use twiny instead of twinx
ax1.errorbar(layers, scores_means[str(epoch)][h,:],np.array(scores_stds[str(epoch)][h,:]))
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_xlabel('depth', color='b')
ax1.tick_params('x', colors='b')
ax2 = ax1.twiny()
ax2.errorbar(hidden_dim, scores_means[str(epoch)][:,l], np.array(scores_stds[str(epoch)][:,l]))
ax2.set_xlabel('width', color='r')
ax2.tick_params('x', colors='r')
fig.tight_layout()
plt.show()

Categories