I am trying to plot a Seaborn FacetGrid, with a single legend for all four facets. I do not want the legend to overlap the facets, however, I do want the legend to show the handles for all of the data, not just the handles from the last facet. I can either get the legend to be positioned correctly, or get all the handles to show, but not both. I have looked at other answers, but cannot seem to find a solution. In the post I have included images (using my full data set, rather than the dataset generated in the code below) illustrating the problem.
import pandas as pd
from datetime import datetime
from random import random, randrange
import matplotlib.pyplot as plt
from matplotlib import dates
import seaborn as sns
# Create a dataframe to test on
df = pd.DataFrame(columns=['WindowID', 'Date_Time', 'Occlusion', 'Floor', 'Month'])
dict = {'Month': [5, 8, 10, 11],
'Day': [15, 18, 30, 10]}
for j in range(4):
mon = dict['Month'][j]
dy = dict['Day'][j]
for i in range(10):
x = i+1
floor = randrange(1,12)
hr = 9
while hr < 18:
z = random()
y = datetime(year=2019, month=mon, day=dy, hour=hr, minute=00, second=00)
df = df.append({'WindowID': x, 'Date_Time': y, 'Occlusion': z, 'Floor': floor, 'Month':mon}, ignore_index=True)
hr += 2
subset_sel = 'Northeast'
byhue = 'Floor'
# This method produces the desired outcome, except that the legend overlaps the last facet
# Note: adding legend_out=True to sns.FacetGrid, does not change outcome
g = sns.FacetGrid(df, hue='Floor', col="Month", sharex=False, sharey=True)
g.map(sns.lineplot, "Date_Time", "Occlusion", legend="full")
for ax in g.axes.flatten():
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
ax.xaxis.set_major_formatter(dates.DateFormatter("%H:%M"))
ax.set_xticks(ax.get_xticks()[::2])
g.add_legend()
g.set_axis_labels("Hour", "Occlusion")
g.set(ylim=(0, 1.0))
plt.suptitle('Northeast', y=0.98, fontsize=14)
plt.tight_layout()
plt.show()
I've tried many things:
plt.legend(bbox_to_anchor=(1.04, 1), borderaxespad=0, title='Floor')
instead of g.add_legend()
produces a legend that is outside the facets but doesn't contain all of the handles for all of the facets.plt.subplots_adjust(right=0.7)
g.add_legend(bbox_to_anchor=(1.04, 0), loc=2, borderaxespad=0.)
plt.tight_layout
cuts off the x-axis labels.Any help would be greatly appreciated!
Facet Grid Plots with all handles, but overlapping last facet Facet Grid Plots with legend not overlapping, but missing handles 10 and 11
Your problem stems from the use of plt.tight_layout()
, which tries to adjust the margins of the axes to make sure that all labels are visible, but is not aware of the space required for the legend.
As you noted, if you remove plt.tight_layout()
, FacetGrid
leaves room for the legend on the right, but your x-labels are sticking out of the figure because of the rotation. So my advice would be to leave the layout from FacetGrid, but make more room for your x-tick labels using plt.subplots_adjust(bottom=0.25)
grid.map(sns.lineplot, 'transaction_date', 'units', alpha=0.5).add_legend()
grid.fig.set_size_inches(plt.rcParams['figure.figsize'])
plt.tight_layout()
# include_zero_on_y_axis(grid)
plt.show()
g = sns.FacetGrid(df, hue='Floor', col="Month", sharex=False, sharey=True)
g.map(sns.lineplot, "Date_Time", "Occlusion", legend="full")
for ax in g.axes.flatten():
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
ax.xaxis.set_major_formatter(dates.DateFormatter("%H:%M"))
ax.set_xticks(ax.get_xticks()[::2])
l = g.add_legend()
g.set_axis_labels("Hour", "Occlusion")
g.set(ylim=(0, 1.0))
plt.suptitle('Northeast', y=0.98, fontsize=14)
plt.subplots_adjust(bottom=0.25)
plt.show()