This question already has an answer here:
seaborn is not plotting within defined subplots
(1 answer)
Closed 21 days ago.
I am not able to understand why my plots are being plotted outside my subplots.
Can anyone tell me where I am going wrong?
Here is my code snippet:
figure, (ax1,ax2,ax3) = plt.subplots(1, 3, sharex=True)
figure.suptitle('repeat_retailer')
sns.catplot(ax= ax1, data=ds, x="repeat_retailer", y="distance_from_home", hue="fraud", jitter = True)
sns.catplot(ax= ax2, data=ds, x="repeat_retailer", y="distance_from_last_transaction", hue="fraud", jitter = True)
sns.catplot(ax= ax3, data=ds, x="repeat_retailer", y="ratio_to_median_purchase_price", hue="fraud", jitter = True)
plt.show()
The output is as shown in the image.
It seems that there is a problem with the size of the subplots or the figure size. You can try specifying the figure size using figure.set_size_inches(width, height), and also adjusting the size of the subplots using fig.tight_layout() or specifying the height and width of the subplots using fig.subplots_adjust(hspace=height, wspace=width).
Additionally, you can also check if there is any overlap with the title and subplots by specifying the subplot_adjust parameter.
A general syntax of subplot is as follows:
Syntax: fig, ax = plt.subplots(nrows, ncols)
nrows: The number of rows of subplots in the figure.
ncols: The number of columns of subplots in the figure.
For example, in the code
figure, (ax1,ax2,ax3) = plt.subplots(1, 3, sharex=True),
there is one row and three columns of subplots in the figure, and ax1, ax2, and ax3 are the axes objects for each subplot.
You can then plot on each subplot using the corresponding axis object, e.g.
sns.catplot(ax= ax1, data=ds, x="repeat_retailer",
y="distance_from_home", hue="fraud", jitter = True)
plots on the first subplot using ax1.
EDITED
Then maybe the issue is with seaborns catplots. Have you tried using scatter plot and see if this works?
figure, (ax1, ax2, ax3) = plt.subplots(1, 3, sharex=True)
sns.scatterplot(ax=ax1, data=ds, x="repeat_retailer",
y="distance_from_home", hue="fraud", jitter=True)
sns.scatterplot(ax=ax2, data=ds, x="repeat_retailer", y="distance_from_last_transaction", hue="fraud", jitter=True)
sns.scatterplot(ax=ax3, data=ds, x="repeat_retailer", y="ratio_to_median_purchase_price", hue="fraud", jitter=True)
plt.show()
Related
As per this question, moving the xticks and labels of an AxesSubplot object can be done with ax.xaxis.tick_top(). However, I cannot get this to work with multiple axes inside a figure.
Essentially, I want to move the xticks to the very top of the figure (only displayed at the top for the subplots in the first row).
Here's a silly example of what I'm trying to do:
fig, axs = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
fig.set_figheight(5)
fig.set_figwidth(10)
for ax in axs.flatten():
ax.xaxis.tick_top()
plt.show()
Which shows
My desired result is this same figure but with the xticks and xticklabels at the top of the two plots in the first row.
Credits to #BigBen for the sharex comment. It is indeed what's preventing tick_top to work.
To get your results, you can combine using tick_top for the two top plots and use tick_params for the bottom two:
fig, axs = plt.subplots(2, 2, sharex=False) # Do not share xaxis
for ax in axs.flatten()[0:2]:
ax.xaxis.tick_top()
for ax in axs.flatten()[2:]:
ax.tick_params(axis='x',which='both',labelbottom=False)
See a live implementation here.
I have a code that plots multiple plots - a heatmap and a barplot in a single plot in Seaborn. However, both plots are sized equally, i.e, one half of overall figure is heatmap, other half is barplot. Is there a way to control individual plot sizes such that heatmap occupies 75% of the plot size while barplot occupies only 25% of the plot?
Reference Code:
ig, ax = plt.subplots(1, 2, figsize=(7, 5))
heatmap = np.random.uniform(0, 1, size=(12, 12))
sns.heatmap(heatmap_scores, linewidth=0.5, cmap="OrRd", ax=ax[0])
ax[0].set_xlabel('Head')
ax[0].set_ylabel('Layer')
ax[0].set_title('Attention Heatmap')
x = np.mean(heatmap_scores, axis=1)
y = np.arange(0, 12)
sns.barplot(x=x, y=y, ax=ax[1], orient='h', color='r', dodge=False)
ax[1].set_title('Layer Average')
ax[1].set(yticklabels=[])
plt.savefig('fig.png')
plt.close()
You can customize GridSpec options, passing them to subplots, with the key gridspec_kw:
fig, ax = plt.subplots(1, 2, figsize=(7, 5), gridspec_kw={'width_ratios': [.75, .25]})
I'm creating subplots. I would like to set the same xtick for all subplots. I was able to set the xlabel in common for all subplots but I really don't know how to do for xticks. Any help?
fig, axs = plt.subplots(2, 2)
axs[0,0].plot(np.float64(datatime),np.float64(Tm),'--',color='black')
axs[0,0].set_ylim([min(Tm)-10,max(Tm)+10])
axs[0,0].set_ylabel('Temp. [°C]')
axs[0,1].plot(np.float64(datatime),np.float64(precip),'--',color='black')
axs[0,1].set_ylim([min(precip),max(precip)+20])
axs[0,1].set_ylabel('Rainfall [mm]')
axs[1,0].plot(np.float64(datatime),np.float64(PET),color='magenta')
axs[1,0].set_ylim([min(PET),max(PET)+10])
axs[1,0].set_ylabel('PET [mm]')
axs[1,1].plot(np.float64(datatime),np.float64(delta),color='cyan')
axs[1,1].set_ylim([min(delta),max(delta)+10])
axs[1,1].set_ylabel('P-PET [mm]')
plt.xticks(np.arange(min(datatime), max(datatime)+1, 12)) #here i define xticks
for ax in axs.flat:
ax.set(xlabel='Time [months]')
plt.show()
Within the for loop you can set the same ticks for each subplot
for ax in axs.flat:
ax.set(xlabel='Time [months]')
ax.set_xticks(np.arange(min(datatime), max(datatime)+1, 12))
I have two plots that I generated from my data:
Here the second plot shows the distribution of results from the first one.
What I want is to plot them side-by-side so you could see both the data and the distribution on the same plot. And I want plots to share y-axis as well.
I tried to do the following:
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(40, 15), sharey=True)
ax1 = sns.lineplot(plotting_df.index, plotting_df.error, color=('#e65400'), lw=2, label='random forest residual error')
ax1 = sns.lineplot(plotting_df.index, plotting_df.val, color=('#9b9b9b'), lw=1, label='current model residual error')
ax1 = sns.lineplot(plotting_df.index, 0, color=('#2293e3'), lw=1)
ax1.xaxis.set_visible(False)
ax1.set_ylabel('Residual Fe bias', fontsize=16)
ax1.set_title('Models residual error comparison', fontsize=20, fontweight='bold')
sns.despine(ax=ax1, top=True, bottom=True, right=True)
ax2 = sns.distplot(results_df.error, hist=True, color=('#e65400'), bins=81,
label='Random forest model', vertical=True)
ax2 = sns.distplot(plotting_df.val, hist=True, color=('#9b9b9b'),
bins=81, label='Rolling averages model', vertical=True)
ax2.set_title('Error distribution comparison between models', fontsize=20, fontweight='bold')
sns.despine(ax=ax2, top=True, right=True)
fig.savefig("blabla.png", format='png')
But when I do run it I get strange results - the first chart is in the second column, whereas I wanted it on the left and the second chart is completely blank. Not sure what I did wrong here.
Both lineplot and distplot accept a matplotlib axes object as an argument, which tells it which axes to plot onto. If no axes is passed into it, then the plot is placed onto the current axes.
You create a figure and 2 axes using :
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(40, 15), sharey=True)
Therefore, ax2 will be the current axes. So your distplot is being plotted on top of your lineplot, both in ax2.
You need to pass the axes into the seaborn plotting functions.
sns.lineplot(..., ax=ax1)
sns.distplot(..., ax=ax2)
I am plotting 4 subplots (i.e 2 rows 2 columns) in this way:
fig1= plt.figure(figsize=(8,6))
ax1 = fig1.add_subplot(221)
ax1.errorbar((r1),(xi1),fmt='',yerr=(low_err_1,upp_err_1),ls='none',color='black')
ax1.scatter((r1),(xi1),c='red',marker="o",s=30,label= r'$\xi(r)$ $0.0<z<0.5$')
ax1.plot((r1),(curve_y_1),'--',label='fit $0.0<z<0.5$')
ax1.set_xscale('log')
ax1.set_yscale('log')
ax2 = fig1.add_subplot(222)
ax2.errorbar((r2),(xi2),fmt='',yerr=(low_err_2,upp_err_2),ls='none',color='black')
ax2.scatter((r2),(xi2),c='blue',marker="o",s=30,label=r'$\xi(r)$ $0.5<z<1.0$')
ax2.plot((r2),(curve_y_2),'--',label='fit $0.5<z<1.0$')
ax2.set_xscale('log')
ax2.set_yscale('log')
ax3 = fig1.add_subplot(223)
ax3.errorbar((r3),(xi3),fmt='',yerr=(low_err_3,upp_err_3),ls='none',color='black')
ax3.scatter((r3),(xi3),c='yellow',marker="o",s=30,label=r'$\xi(r)$ $1.0<z<1.5$')
ax3.plot((r3),(curve_y_3),'--',label='fit $1.0<z<1.5$')
ax3.set_xscale('log')
ax3.set_yscale('log')
ax4 = fig1.add_subplot(224)
ax4.errorbar((r4),(xi4),fmt='',yerr=(low_err_4,upp_err_4),ls='none',color='black')
ax4.scatter((r4),(xi4),c='black',marker="o",s=30,label=r'$\xi(r)$ $1.5<z<2.0$')
ax4.plot((r4),(curve_y_4),'--',label='fit $1.5<z<2.0$')
ax4.set_xscale('log')
ax4.set_yscale('log')
My questions are:
Is there a way to add legends to all these subplots using a single (common) command, instead of typing ax1.legend(loc = 'best'), ax2.legend(loc = 'best') and so on separately for each subplot?
I would like to set log-scaling for each subplot using a single (common) command. As you can see, now I am setting the axis-scales separately to log for each subplot.
Just define a axes formatting function:
def style_ax(ax):
ax.legend(loc='best')
ax.set_yscale('log')
ax.set_xscale('log')
And than call it when finished:
for ax in [ax1, ax2, ax3, ax4]:
style_ax(ax)