Python - Synchronizing boxplot axis for comparison - python

this is my first question on Stack so please let me know if my post isn't very clear.
How can I synchronize the range of two boxplots so that the x axis grids will be in line?
In the example below, I want the upper plot to also show grids from -10 to 10 like the lower plot, but I don't want to fix it to real numbers so that the box plots would be synchronized even if the dataset changes.
two boxplots
fig, (ax0, ax1) = plt.subplots(2, 1, figsize=(10*mult, 8*mult), gridspec_kw={'height_ratios': [1, 4]})
sns_plot = sns.boxplot(y='Overall', x='RoR', data=data_s, ax=ax0, showfliers=False)
sns_plot.set_xlabel("")
sns_plot.set_ylabel("")
sns_plot = sns.boxplot(y='AUA Bucket', x='RoR', data=data_s, order=aua_buckets, ax=ax1,showfliers=False)
sns_plot.set_xlabel("")
sns_plot.set_ylabel("")
plt.subplots_adjust(left=0.12)
plt.subplots_adjust(bottom=0.05)
plt.subplots_adjust(right=0.98)
plt.subplots_adjust(top=0.98)
plt.savefig("dist_aua.png", format="png", dpi=75)
plt.close()

To illustrate with the example in the official reference, if you align the limits of each axis, the grid will be aligned.
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(style="whitegrid")
tips = sns.load_dataset("tips")
fig, (ax0, ax1) = plt.subplots(2, 1, figsize=(10, 8), gridspec_kw={'height_ratios': [1, 4]})
sns.boxplot(x=tips["total_bill"], ax=ax0, showfliers=False)
sns.boxplot(x=tips["total_bill"], y=tips['day'], ax=ax1, showfliers=False)
ax0.set_xlim(0,50)
ax1.set_xlim(0,50)
plt.show()

Related

How can i show the output of pies side by side?

I have following code which gives the output one below another. How can i show the output side by side? I will also add anouther pies in to this code, so i also want to know how would it be if i wanted to show 6 pies for instance.
Thanks in advance
data["Gender"].value_counts().plot.pie(autopct="%.1f%%")
plt.show()
data["Education_Level"].value_counts().plot.pie(autopct="%.1f%%")
You can create a subplot with a specification of your own, and then pass the current axis as a parameter. Here I'll create a subplot with 1 row and 2 columns:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({'mass': [0.33, 4.87, 5.97],
'radius': [2439.7, 6051.8, 6378.1]},
index=['Mercury', 'Venus', 'Earth']
)
fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(12, 12))
df.plot.pie(y='mass', ax=axs[0])
df.plot.pie(y='radius', ax=axs[1])
plt.show()
The code above produces the following result:
In case you wanted 6 figures next to each other, set the ncols parameter to 6, and then pass through all 6 axes. Here's a quick demo.
fig, axs = plt.subplots(nrows=1, ncols=6, figsize=(12, 12))
for ax in axs:
df.plot.pie(y='mass', ax=ax) # plots the same pie 6 times
Be sure to read more about matplotlib and how figures/axes work from their documentation.

tight_layout() reserves ylabel and yticklabels space even when disabled

I have a 2*4 subplots figure, with half of the ylabel and yticklabels disabled. Unfortunately, tight_layout() does not remove the extra white space which corresponds to the area where the ylabel and yticklabels would appear if they were not disabled. The ylabel and yticklabels are removed because I would like to have 4 pairs of comparison subplots. The plot looks something like this.
I am looking for an efficient way to remove the extra white space. In fact, I would like each pair of plots to be next to each other with no space at all. Here is an working example.
import matplotlib.pyplot as plt
fig, ((ax0, ax1, ax2, ax3), (ax4, ax5, ax6, ax7)) = plt.subplots(2, 4, figsize=(8, 4))
axs = [ax0, ax1, ax2, ax3, ax4, ax5, ax6, ax7]
for i in range(4):
axs[2*i].set_ylabel('parameter '+str(i))
axs[2*i+1].set_yticklabels([])
plt.tight_layout()
plt.show()
The code should yield the above plot. Any tips would be appreciated. Many thanks!
You can do it by having two subgrids and forcing a null distance between the axis (I followed this tutorial). The wspace parameter is described here.
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=False) # you must disable automatic spacing
gs = fig.add_gridspec(1, 2, figure=fig)
gs0 = gs[0].subgridspec(2, 2, wspace=0.0)
gs1 = gs[1].subgridspec(2, 2, wspace=0.0)
axs = [[],[]]
for i in range(2):
for j in range(2):
axs[0].append(fig.add_subplot(gs0[i, j]))
axs[1].append(fig.add_subplot(gs1[i, j]))
axs = [*axs[0],*axs[1]]
for i in range(4):
axs[2*i].set_ylabel('parameter ' + str(i))
axs[2*i+1].set_yticklabels([])
Since the automatic spacing is disabled, you may have to play with axis and figure properties to adapt the position of the axis, labels, etc.
A partial (distance won't be null, but may be interesting for other users), more elegant solution is to use constrained_layout when creating the figure. More info in matplotlib's documentation.
import matplotlib.pyplot as plt
fig, axs = plt.subplots(
2, 4,
figsize=(8, 4),
constrained_layout=True,
)
axs = axs.reshape([8])
for i in range(4):
axs[2*i].set_ylabel('parameter '+str(i))
axs[2*i+1].set_yticklabels([])
# plt.tight_layout() # not necessary
plt.show()

Seaborn heatmaps in subplots - align x-axis

I am trying to plot a figure containing two subplots, a seaborn heatmap and simple matplotlib lines. However, when sharing the x-axis for both plots, they do not align as can be seen in this figure:
It would seem that the problem is similar to this post, but when displaying ax[0].get_xticks() and ax[1].get_xticks() I get the same positions, so I don't know what to change. And in my picture the the deviation seems to be more than a 0.5 shift.
What am I doing wrong?
The code I used to plot the figure is the following:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
M_1=np.random.random((15,15))
M_2=np.random.random((15,15))
L_1=np.random.random(15)
L_2=np.random.random(15)
x=range(15)
cmap = sns.color_palette("hot", 100)
sns.set(style="white")
fig, ax = plt.subplots(2, 1, sharex='col', figsize=(10, 12))
ax[0].plot(x,L_1,'-', marker='o',color='tab:orange')
sns.heatmap(M_1, cmap=cmap, vmax=np.max(M_1), center=np.max(M_1)/2., square=False, ax=ax[1])
#Mr-T 's comment is spot on. The easiest would be to create the axes beforehand instead of letting heatmap() shrink your axes in order to make room for the colorbar.
There is the added complication that the labels for the heatmap are not actually placed at [0,1,...] but are in the middle of each cell at [0.5, 1.5, ...]. So if you want your upper plot to align with the labels at the bottom (and with the center of each cell), you may have to shift your plot by 0.5 units to the right:
M_1=np.random.random((15,15))
M_2=np.random.random((15,15))
L_1=np.random.random(15)
L_2=np.random.random(15)
x=np.arange(15)
cmap = sns.color_palette("hot", 100)
sns.set(style="white")
fig, ax = plt.subplots(2, 2, sharex='col', gridspec_kw={'width_ratios':[100,5]})
ax[0,1].remove() # remove unused upper right axes
ax[0,0].plot(x+0.5,L_1,'-', marker='o',color='tab:orange')
sns.heatmap(M_1, cmap=cmap, vmax=np.max(M_1), center=np.max(M_1)/2., square=False, ax=ax[1,0], cbar_ax=ax[1,1])

Drawing fewer plots than specified in matplotlib subplots

fig, ax = plt.subplots(3, 3, sharex='col', squeeze=False, figsize=(20, 10))
I want to plot 7 subplots and am using the command above. However it creates 9 plots (including 2 empty ones). How can I make sure that only 7 plots get drawn?
import matplotlib.pyplot as plt
fig, axs = plt.subplots(3,3)
fig.delaxes(axs[-1, -1])
fig.delaxes(axs[-1, -2])
plt.show()

Share axes in matplotlib for only part of the subplots

I am having a big plot where I initiated with:
import numpy as np
import matplotlib.pyplot as plt
fig, axs = plt.subplots(5, 4)
And I want to do share-x-axis between column 1 and 2; and do the same between column 3 and 4. However, column 1 and 2 does not share the same axis with column 3 and 4.
I was wondering that would there be anyway to do this, and not sharex=True and sharey=True across all figures?
PS: This tutorial does not help too much, because it is only about sharing x/y within each row/column; they cannot do axis sharing between different rows/columns (unless share them across all axes).
I'm not exactly sure what you want to achieve from your question. However, you can specify per subplot which axis it should share with which subplot when adding a subplot to your figure.
This can be done via:
import matplotlib.pylab as plt
fig = plt.figure()
ax1 = fig.add_subplot(5, 4, 1)
ax2 = fig.add_subplot(5, 4, 2, sharex = ax1)
ax3 = fig.add_subplot(5, 4, 3, sharex = ax1, sharey = ax1)
A slightly limited but much simpler option is available for subplots. The limitation is there for a complete row or column of subplots.
For example, if one wants to have common y axis for all the subplots but common x axis only for individual columns in a 3x2 subplot, one could specify it as:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(3, 2, sharey=True, sharex='col')
One can manually manage axes sharing using a Grouper object, which can be accessed via ax._shared_x_axes and ax._shared_y_axes. For example,
import matplotlib.pyplot as plt
def set_share_axes(axs, target=None, sharex=False, sharey=False):
if target is None:
target = axs.flat[0]
# Manage share using grouper objects
for ax in axs.flat:
if sharex:
target._shared_x_axes.join(target, ax)
if sharey:
target._shared_y_axes.join(target, ax)
# Turn off x tick labels and offset text for all but the bottom row
if sharex and axs.ndim > 1:
for ax in axs[:-1,:].flat:
ax.xaxis.set_tick_params(which='both', labelbottom=False, labeltop=False)
ax.xaxis.offsetText.set_visible(False)
# Turn off y tick labels and offset text for all but the left most column
if sharey and axs.ndim > 1:
for ax in axs[:,1:].flat:
ax.yaxis.set_tick_params(which='both', labelleft=False, labelright=False)
ax.yaxis.offsetText.set_visible(False)
fig, axs = plt.subplots(5, 4)
set_share_axes(axs[:,:2], sharex=True)
set_share_axes(axs[:,2:], sharex=True)
To adjust the spacing between subplots in a grouped manner, please refer to this question.
I used Axes.sharex /sharey in a similar setting
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.sharex.html#matplotlib.axes.Axes.sharex
import matplotlib.pyplot as plt
fig, axd = plt.subplot_mosaic([list(range(3))] +[['A']*3, ['B']*3])
axd[0].plot([0,0.2])
axd['A'].plot([1,2,3])
axd['B'].plot([1,2,3,4,5])
axd['B'].sharex(axd['A'])
for i in [1,2]:
axd[i].sharey(axd[0])
plt.show()

Categories

Resources