Seaborn / Matplotlib barplot with multiple y axis - python

I want to draw a barplot with 3 different y values which belong to RMSE, R2 and MAPE metrics.
My dataframe is;
DLscores = {"GRU":[293.7372606050454,0.961253983114077,86281.57826775634],
"LSTM":[285.9872902525968,0.9632715628933957,81788.73018602304],
"LSTM_Attention":[266.6285102384448,0.9680756432778241,71090.76247197246],
"TCN":[219.30770326715282,0.9784018398981137,48095.868712313546],
"Hybrid":[216.97781461699145,0.978858312741761,47079.372035965505]}
I am able to do this with linegraph. However when I change it to bar, they overlaps. My line plot code is;
# Create figure and axis #1
fig, ax1 = plt.subplots(figsize=(16,10))
# plot line chart on axis #1
p1, = ax1.plot(DLscores.columns, DLscores.iloc[1], color='blue')
ax1.set_ylabel('R2')
#ax1.set_ylim(0, 25)
#ax1.legend(['R2'], loc="upper left")
ax1.yaxis.label.set_color(p1.get_color())
ax1.yaxis.label.set_fontsize(14)
ax1.tick_params(axis='y', colors=p1.get_color(), labelsize=14)
# set up the 2nd axis
ax2 = ax1.twinx()
# plot bar chart on axis #2
p2, = ax2.plot(DLscores.columns, DLscores.iloc[0], color='green')
ax2.grid(False) # turn off grid #2
ax2.set_ylabel('RMSE')
#ax2.set_ylim(0, 90)
#ax2.legend(['RMSE'], loc="upper center")
ax2.yaxis.label.set_color(p2.get_color())
ax2.yaxis.label.set_fontsize(14)
ax2.tick_params(axis='y', colors=p2.get_color(), labelsize=14)
# set up the 3rd axis
ax3 = ax1.twinx()
# Offset the right spine of ax3. The ticks and label have already been placed on the right by twinx above.
ax3.spines.right.set_position(("axes", 1.2))
# Plot line chart on axis #3
p3, = ax3.plot(DLscores.columns, DLscores.iloc[2], color='red')
ax3.grid(False) # turn off grid #3
ax3.set_ylabel('MAPE')
#ax3.set_ylim(0, 8)
#ax3.legend(['MAPE'], loc="upper right")
ax3.yaxis.label.set_color(p3.get_color())
ax3.yaxis.label.set_fontsize(14)
ax3.tick_params(axis='y', colors=p3.get_color(), labelsize=14)
plt.show()
Output:
I also tried seaborn (I couldn't figure it out how can I merge it with "hue"), but code and result is in below:
# plot line chart on axis #1
ax1 = sns.barplot(
x=DLscores.index,
y=DLscores['RMSE'],
color='blue'
)
ax1.set_ylabel('RMSE')
#ax1.set_ylim(0, 8)
ax1.legend(['RMSE'], loc="upper left")
ax1.yaxis.label.set_color('blue')
ax1.yaxis.label.set_fontsize(14)
ax1.tick_params(axis='y', colors='blue', labelsize=14)
# set up the 2nd axis
ax2 = ax1.twinx()
# plot bar chart on axis #2
sns.barplot(
x=DLscores.index,
y=DLscores['R2'],
color='orange',
ax = ax2 # Pre-existing axes for the plot
)
ax2.grid(False) # turn off grid #2
ax2.set_ylabel('R2')
#ax2.set_ylim(0, 90)
ax2.legend(['R2'], loc="upper center")
ax2.yaxis.label.set_color('orange')
ax2.yaxis.label.set_fontsize(14)
ax2.tick_params(axis='y', colors='orange', labelsize=14)
# set up the 3rd axis
ax3 = ax1.twinx()
# Offset the right spine of ax3. The ticks and label have already been placed on the right by twinx above.
ax3.spines.right.set_position(("axes", 1.15))
# Plot line chart on axis #3
p3 = sns.barplot(
x=DLscores.index,
y=DLscores['MAPE'],
color='red',
ax = ax3 # Pre-existing axes for the plot
)
ax3.grid(False) # turn off grid #3
ax3.set_ylabel('MAPE')
#ax3.set_ylim(0, 8)
ax3.legend(['MAPE'], loc="upper right")
ax3.yaxis.label.set_color('red')
ax3.yaxis.label.set_fontsize(14)
ax3.tick_params(axis='y', colors='red', labelsize=14)
plt.show()
I assume the problem is clear.

Related

y-axis range is not logical (matplotlib python)

I just specify the x and y axis limitations but the numbers' order is wrong. how can I fix this?
here is my code:
fig, ax = plt.subplots(figsize=(20,10))
ax.plot(df.finish_price, label="Stock Values", color = 'blue')
plt.ylabel("Price", color='b')
# Generate a new Axes instance, on the twin-X axes (same position)
ax2 = ax.twinx()
ax2.plot(df.sentiment, label= 'Sentiment', color='green')
ax2.tick_params(axis='y', labelcolor='green')
plt.ylim(bottom = -1)
plt.ylim(top=1)
plt.xlabel("Days")
plt.ylabel("Sentiment", color='g')
fig.legend()
plt.show()
and here is the result:
as you can see the numbers' order on the right y-axis is wrong.

Subplotting subplots

I am creating two plots using matplotlib, each of them is a subplot showing two metrics on the same axis.
I'm trying to run them so they show as two charts but in one graphic, so that when I save the graphic I see both plots. At the moment, running the second plot overwrites the first in memory so I can only ever save the second.
How can I plot them together?
My code is below.
plot1 = plt.figure()
fig,ax1 = plt.subplots()
ax1.plot(dfSat['time'],dfSat['wind_at_altitude'], 'b-', label = "speed", linewidth = 5.0)
plt.title('Wind Speeds - Saturday - {}'.format(windloc))
plt.xlabel('Time of day')
plt.ylabel('Wind speed (mph)')
ax1.plot(dfSat['time'],dfSat['gust_at_altitude'], 'r-', label = "gust", linewidth = 5.0)
plt.legend(loc="upper right")
ax1.text(0.05, 0.95, calcmeassat, transform=ax1.transAxes, fontsize=30,
verticalalignment='top')
plt.ylim((0,100))
plot2 = plt.figure()
fig,ax2 = plt.subplots()
ax2.plot(dfSun['time'],dfSun['wind_at_altitude'], 'b-', label = "speed", linewidth = 5.0)
plt.title('Wind Speeds - Sunday - {}'.format(windloc))
plt.xlabel('Time of day')
plt.ylabel('Wind speed (mph)')
ax2.plot(dfSun['time'],dfSun['gust_at_altitude'], 'r-', label = "gust", linewidth = 5.0)
plt.legend(loc="upper right")
ax2.text(0.05, 0.95, calcmeassun, transform=ax2.transAxes, fontsize=30,
verticalalignment='top')
plt.ylim((0,100))
As mentioned, in your case you only need one level of subplots, e.g., nrows=1, ncols=2.
However, in matplotlib 3.4+ there is such a thing as "subplotting subplots" called subfigures, which makes it easier to implement nested layouts, e.g.:
How to create row titles for subplots
How to share colorbars within some subplots
How to share xlabels within some subplots
Subplots
For your simpler use case, create 1x2 subplots with ax1 on the left and ax2 on the right:
# create 1x2 subplots
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(16, 4))
# plot saturdays on the left
dfSat.plot(ax=ax1, x='date', y='temp_min')
dfSat.plot(ax=ax1, x='date', y='temp_max')
ax1.set_ylim(-20, 50)
ax1.set_title('Saturdays')
# plot sundays on the right
dfSun.plot(ax=ax2, x='date', y='temp_min')
dfSun.plot(ax=ax2, x='date', y='temp_max')
ax2.set_ylim(-20, 50)
ax2.set_title('Sundays')
Subfigures
Say you want something more complicated like having the left side show 2012 with its own suptitle and right side to show 2015 with its own suptitle.
Create 1x2 subfigures (left subfig_l and right subfig_r) with 2x1 subplots on the left (top ax_lt and bottom ax_lb) and 2x1 subplots on the right (top ax_rt and bottom ax_rb):
# create 1x2 subfigures
fig = plt.figure(constrained_layout=True, figsize=(12, 5))
(subfig_l, subfig_r) = fig.subfigures(nrows=1, ncols=2, wspace=0.07)
# create top/box axes in left subfig
(ax_lt, ax_lb) = subfig_l.subplots(nrows=2, ncols=1)
# plot 2012 saturdays on left-top axes
dfSat12 = dfSat.loc[dfSat['date'].dt.year.eq(2012)]
dfSat12.plot(ax=ax_lt, x='date', y='temp_min')
dfSat12.plot(ax=ax_lt, x='date', y='temp_max')
ax_lt.set_ylim(-20, 50)
ax_lt.set_ylabel('Saturdays')
# plot 2012 sundays on left-top axes
dfSun12 = dfSun.loc[dfSun['date'].dt.year.eq(2012)]
dfSun12.plot(ax=ax_lb, x='date', y='temp_min')
dfSun12.plot(ax=ax_lb, x='date', y='temp_max')
ax_lb.set_ylim(-20, 50)
ax_lb.set_ylabel('Sundays')
# set suptitle for left subfig
subfig_l.suptitle('2012', size='x-large', weight='bold')
# create top/box axes in right subfig
(ax_rt, ax_rb) = subfig_r.subplots(nrows=2, ncols=1)
# plot 2015 saturdays on left-top axes
dfSat15 = dfSat.loc[dfSat['date'].dt.year.eq(2015)]
dfSat15.plot(ax=ax_rt, x='date', y='temp_min')
dfSat15.plot(ax=ax_rt, x='date', y='temp_max')
ax_rt.set_ylim(-20, 50)
ax_rt.set_ylabel('Saturdays')
# plot 2015 sundays on left-top axes
dfSun15 = dfSun.loc[dfSun['date'].dt.year.eq(2015)]
dfSun15.plot(ax=ax_rb, x='date', y='temp_min')
dfSun15.plot(ax=ax_rb, x='date', y='temp_max')
ax_rb.set_ylim(-20, 50)
ax_rb.set_ylabel('Sundays')
# set suptitle for right subfig
subfig_r.suptitle('2015', size='x-large', weight='bold')
Sample data for reference:
import pandas as pd
from vega_datasets import data
df = data.seattle_weather()
df['date'] = pd.to_datetime(df['date'])
dfSat = df.loc[df['date'].dt.weekday.eq(5)]
dfSun = df.loc[df['date'].dt.weekday.eq(6)]
It doesn't work like that. Subplots are what they are called; plots inside a main plot.
That means if you need two subplots; then you need one plot containing two subplots in it.
# figure object NOT plot object
# useful when you want only one plot NO subplots
fig = plt.figure()
# 2 subplots inside 1 plot
# 1 row, 2 columns
fig, [ax1, ax2] = plt.subplots(1, 2)
# then call plotting method on each axis object to
# create plot on that subplot
sns.histplot(...., ax=ax1)
sns.violinplot(..., ax=ax2)
# or using matplotlib like this
ax1.plot()
ax2.plot()
Learn more about subplots

Problems rotating xtick labels when using twinx

I have problems with the rotation of my X-axis, I have tried to do the rotation the output plot without errors, but I do not have the results.
# Import Data
#df = pd.read_csv("https://github.com/selva86/datasets/raw/master/economics.csv")
x = total_test["Dia"].values[:]; y1 = total_test["Confirmados"].values[:]; y2 = total_test["Fallecidos"].values[:]
# Plot Line1 (Left Y Axis)
fig, ax1 = plt.subplots(1,1,figsize=(10,8), dpi= 200)
ax1.plot(x, y1,'g^', color='tab:red')
# Plot Line2 (Right Y Axis)
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
ax2.plot(x, y2,'bs', color='tab:blue')
# Just Decorations!! -------------------
# ax1 (left y axis)
ax1.set_xlabel('Dias', fontsize=10)
ax1.set_ylabel('Personas Confirmadas', color='tab:red', fontsize=20)
ax1.tick_params(axis='y', rotation=0, labelcolor='tab:red' )
# ax2 (right Y axis)
ax2.set_ylabel("Personas Fallecidas", color='tab:blue', fontsize=20)
ax2.tick_params(axis='y', rotation=0, labelcolor='tab:blue')
ax2.set_title("Personas Confirmadas y Fallecidas por Covid-19 Peru", fontsize=15)
#ax2.set_xticks(x)
ax2.set_xticklabels(x[::],fontsize=10,rotation=90)
plt.show()
Any commands for the xaxis need to occur before ax2.
Verify date is in a datetime format and set as the index.
import pandas as pd
import matplotlib.pyplot as plt
# read data
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/economics.csv")
# verify the date column is a datetime format and set as index
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
#plot
# create figure
fig, ax1 = plt.subplots(1, 1, figsize=(10,8))
# 1st plot
ax1.plot(df['pop'], color='tab:red')
# set xticks rotation before creating ax2
plt.xticks(rotation=90)
# 2nd plot (Right Y Axis)
ax2 = ax1.twinx() # create the 'twin' axis on the right
ax2.plot(df['unemploy'], color='tab:blue')
plt.show()
Plot directly with pandas.DataFrame.plot
# load data
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/economics.csv", parse_dates=True, index_col=[0])
# plot and rotate the tick labels with rot= in the first plot call
ax = df.plot(y='pop', color='tab:red', figsize=(10,8), rot=90)
ax2 = ax.twinx()
df.plot(y='unemploy', color='tab:blue', ax=ax2)
ax2.legend(loc='upper right')

Adding count plot totals and removing specific labels

Hi I have the following code. The code is in a for loop, and it makes over 300 plots.
sns.set(style='white', palette='cubehelix', font='sans-serif')
fig, axs = plt.subplots(2, 3, dpi =200);
fig.subplots_adjust(hspace=0.5, wspace=1)
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
#tmppath = 'path/{0}'.format(key);
##
sns.countplot(y='Ethnicity', data=value, orient='h', ax=axs[0,0]);
sns.despine(top=True, right=True, left=True, bottom=True,offset=True)
sns.countplot(y='Program Ratio', data=value,orient='v',ax=axs[1,0]);
sns.despine(offset=True)
sns.countplot(y='Site', data = value, ax=axs[0,1]);
sns.despine(offset=True)
sns.countplot(y='HOUSING_STATUS', data = value, ax = axs[1,1])
sns.despine(offset=True)
sns.countplot(y='Alt. Assessment', data = value, ax = axs[0,2])
sns.despine(offset=True)
pth = os.path.join(tmppath, '{0}'.format(key))
for p in axs.patches:
ax.text(p.get_x() + p.get_width()/2., p.get_width(), '%d' %
int(p.get_width()),
fontsize=12, color='red', ha='center', va='bottom')
#plt.tight_layout(pad=2.0, w_pad=1.0, h_pad=2.0);
plt.set_title('{0}'.format(key)+'Summary')
sns.despine()
axs[0,0].set_xticklabels('','Ethnicity')
axs[1,0].set_axis_labels('','Program Ratio')
axs[0,1].set_axis_labels('','Students by Site')
axs[1,1].set_axis_labels('','Housing Status')
axs[0,2].set_axis_labels('','Alt Assessment')
fig.tight_layout()
fig.subplots_adjust(top=0.88)
fig.suptitle('{0}'.format(key)+' Summary')
plt.suptitle('{0}'.format(key)+' Summary')
plt.savefig("path/{0}/{1}.pdf".format(key,key), bbox_inches = 'tight');
plt.clf()
plt.suptitle('{0} Summary'.format(key))
plt.savefig("path/{0}/{1}.pdf".format(key,key), bbox_inches = 'tight');
plt.clf()
I've checked out the links below ( and more):
Remove xticks in a matplotlib plot?
https://datascience.stackexchange.com/questions/48035/how-to-show-percentage-text-next-to-the-horizontal-bars-in-matplotlib
When I try the method from the second link. I end up with graphs like so
Without that the graph looks something like so
I want to get rid of the words count and the ticks on each subplot xaxis.
#ImportanceOfBeingErnest
Thanks, I followed your advice and this post.
Here is what is a compact version of what I ended up with
sns.set(style='white', palette=sns.palplot(sns.color_palette(ui)), font='sans-serif')
plt.figure(figsize=(20,20))
fig, axs2 = plt.subplots(2, 3, dpi =300);
fig.subplots_adjust(top=.8)
fig.subplots_adjust(hspace=1, wspace=1.5)
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
sns.countplot(y='column',palette = ui,order = df.value_counts().index, data=df,
orient='h', ax=axs2[0,0]);
axs2[0,0].set_xlabel('')
axs2[0,0].set_xticks([])
axs2[0,0].set_ylabel('')
axs2[0,0].set_title('label',size = 'small')
axs2[0,0].tick_params(axis='y', which='major', labelsize=8)
sns.despine(top=True, right=True, left=True, bottom=True,offset=True)
for p in axs2[0,0].patches:
axs2[0,0].annotate(int(p.get_width()),((p.get_x() + p.get_width()), p.get_y()), xytext=(15, -10), fontsize=8,color='#000000',textcoords='offset points'
,horizontalalignment='center')
fig.suptitle('{0}#{1}'.format(dur,key)+' Summary', va = 'top', ha= 'center') #size = 'small')
props = dict(boxstyle='square', facecolor='white', alpha=0.5)
fig.text(0.85, 0.925, dt.date.today().strftime("%b %d, %Y"), fontsize=9, verticalalignment='top', bbox=props)
fig.text(0.15, 0.925, 'No. of stuff'+ str(len(value['column'].unique())),fontsize = 10, va = 'top', ha = 'center')
plt.savefig("path/{0}/{1} # {2}.pdf".format(dur,dur,key), bbox_inches = 'tight');
plt.clf()
plt.close('all')
Excuse the black marks, didn't want to show the info

Python plot bar chart and percentage line chart on same graph

I am trying to plot one or more lines on the same chart as a bar chart to show a few different metrics. I heard I should use ax.twinx() for this, but I either get an error saying x and y must have the same first dimension, or a different error reading 0L based on the two things I tried. Here is my code;
x = df4['Date']
y = df4['Rate']
ax = df4[['Date','Qty']].set_index('Date') \
.plot(kind='bar',
stacked=False,
color = 'dodgerblue',
figsize=(13,4),
legend=True)
ax.set_xlabel(r"$\rm \bf{Date}$",
fontsize=18,
rotation = 0)
ax.set_ylabel(r'$\cal \bf{Qty}$ ',fontsize=18, rotation = 90)
ax2 = ax.twinx()
ax2.plot(x, y, color = 'green', linestyle = '--', linewidth= 2.0)
Note; df4 is a grouped pandas dataframe. Not sure how relevant that is but just in case.
Try this:
fig, ax = plt.subplots()
ax2 = ax.twinx()
df4[['Date','Qty']].set_index('Date') \
.plot(kind='bar',
ax=ax,
color = 'dodgerblue',
figsize=(13,4),
legend=False)
patches, labels = ax.get_legend_handles_labels()
ax.legend(patches, labels, loc='upper left')
ax.set_xlabel(r"$\rm \bf{Date}$", fontsize=18, rotation=0)
ax.set_ylabel(r'$\cal \bf{Qty}$ ',fontsize=18, rotation=90)
ax2.plot(range(len(df4)), df4['Rate'], 'green', label='Rate',
linestyle = '--', linewidth=2.0)
patches, labels = ax2.get_legend_handles_labels()
ax2.legend(patches, labels, loc='upper right')
NOTE: if you want a tested solution, please provide a sample data

Categories