Matplotlib subplots: legend and axis-scale - python

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)

Related

Reverse vertical one of the subplots in python

I have two data frames to plot. One of bar chart, another one is a line chart.
I want to plot the barchart upside to down. (vertically reversed.)
How can i handle this?
fig, ax1 = plt.subplots(figsize=(10, 5))
tidy = results.melt(id_vars='Day').rename(columns=str.title)
ax1 = sns.lineplot(x='Day', y='Value', hue='Variable', data=tidy, ax=ax1)
tidy2 = drugs.melt(id_vars='Day').rename(columns=str.title)
ax1 = sns.barplot(x='Day', y='Value', hue='Variable', data=tidy2, ax=ax1)
ax1.xaxis.set_major_locator(ticker.MultipleLocator(10))
ax1.legend(loc=1)
ax1.tick_params(axis='x', labelrotation=45)

How to show plt.axhline in both subplots from a for loop in python 3

Below code is to create two parameter subplots of data per pdf page and I'm using the 'axhline' function to plot the data limits of each parameter. However when I use it inside the for loop, only the second subplot had the data limits - the first one always doesn't show. I suppose what happens is axhline works one at a time in the for loop? Instead of having it in both subplots by end of the for loop? Please have a look in my code below:
fig, ax = plt.subplots(2, 1)
ax = ax.flatten()
for i, col in enumerate(hib_list[0:2]):
ax1 = sns.boxplot(x=lotid, y=cc_df[col], ax=ax[i], hue=temp)
ax1.set_ylabel(col,fontsize=8)
ax1.get_legend().remove()
plt.tight_layout(pad=1.0)
#Below line is to set data limits
plt.axhline(y=500, color='r', linestyle='--')
pdf.savefig()
plt.close()
Below is a corrected version of your code. You can set once per loop ax=axes[i] and then use ax for all operations. plt.(…) works only on the last plotted axes object.
fig, axes = plt.subplots(2, 1)
axes = axes.flatten()
for i, col in enumerate(hib_list[0:2]):
ax = axes[i]
sns.boxplot(x=lotid, y=cc_df[col], ax=ax, hue=temp)
ax.set_ylabel(col,fontsize=8)
ax.get_legend().remove()
#Below line is to set data limits
ax.axhline(y=500, color='r', linestyle='--')
plt.tight_layout(pad=1.0)
pdf.savefig()
plt.close()

Trying to plot 2 charts side-by-side, but one of them always comes out empty

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)

Make single legend for two subplots of DataFrame

I create a plot with two axes on different subplots. Currently one overlays another. The problem is to make legend to contain both labels in stack. How can I do this?
d = data.groupby('atemp_rounded').sum().reset_index()
fig = plt.figure()
ax1 = fig.add_subplot(111) # don't know what 111 stands for...
ax2 = ax1.twinx()
d.plot(ax=ax1, y='casual')
d.plot(ax=ax2, y='registered', color='g')
plt.show()
You may set the legend of the individual plots off and instead create a figure legend. To have this placed within the axes boundaries the position needs to be specified in axes coordinates.
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({"A" : [3,2,1], "B" : [2,2,1]})
fig = plt.figure()
ax1 = fig.add_subplot(111) # don't know what 111 stands for...
ax2 = ax1.twinx()
df.plot(ax=ax1, y='A', legend=False)
df.plot(ax=ax2, y='B', color='g', legend=False)
fig.legend(loc="upper right", bbox_to_anchor=(0,0,1,1), bbox_transform=ax1.transAxes)
plt.show()

How to plot multiple figures in a row using seaborn

I have a dataframe df that looks like this:
df.head()
id feedback nlp_model similarity_score
0xijh4 1 tfidf 0.36
0sdnj7 -1 lda 0.89
kjh458 1 doc2vec 0.78
....
I want to plot similairty_score versus feedback in a boxplot form using seaborn for each of the unique values in the model column: tfidf, lda, doc2vec. My code for this is as follows:
fig, ax = plt.subplots(figsize=(10,8))
ax = sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='tfidf'])
ax = sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='tfidf'], color="0.25")
fig, ax = plt.subplots(figsize=(10,8))
ax = sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='lda'])
ax = sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='lda'], color="0.25")
fig, ax = plt.subplots(figsize=(10,8))
ax = sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='doc2vec'])
ax = sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='doc2vec'], color="0.25")
plt.show()
The problem is this creates 3 plots one on top of the other.
How can I generate these same plots but all on a single line, with one axis marking "Similarity Score" on the left most plot only, and "Feedback" axis label directly below each plot?
You are creating new figures, each time you plot. So you can remove all but one of the calls to plt.subplots()
The seaborn swarmplot() and boxplot() accept ax arguments i.e. you can tell it which axes to plot to. Therefore, create your figure, subplots and axes using:
fig, (ax1, ax2, ax3) = plt.subplots(1, 3)
Then you can do something like:
sns.boxplot(x="x_vals", y="y_vals", data=some_data, ax=ax1)
You can then manipulate the axes as you see fit. For example, removing the y axis labels only on certain subplots etc.
fig, (ax1, ax2, ax3) = plt.subplots(1,3,figsize=(10,8))
sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='tfidf'], ax=ax1)
sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='tfidf'], color="0.25", ax=ax1)
sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='lda'], ax=ax2)
sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='lda'], color="0.25", ax=ax2)
ax2.set_ylabel("") # remove y label, but keep ticks
sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='doc2vec'], ax=ax3)
sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='doc2vec'], color="0.25", ax=ax3)
ax3.set_ylabel("") # remove y label, but keep ticks
plt.show()

Categories

Resources