I have a figure with subplots I created with data from a panda data frame:
# Definition of the dataframe
df = pd.DataFrame({'Colors': {0: 'Red', 1: 'Blue', 2: 'Green', 3: 'Blue', 4: 'Red'}, 'X_values': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5}, 'Y_values': {0: 2, 1: 4, 2: 8, 3: 10, 4: 4}, 'MinY_values': {0: 1.5, 1: 3, 2: 7.5, 3: 8, 4: 2}, 'MaxY_values': {0: 2.5, 1: 5, 2: 9.5, 3: 11, 4: 6}})
I defined two lists which contain the color for each point on the figure as well as the label I want to assign to each point (Then, I can change a label for a given point if needed):
color
Out[8]: ['red', 'blue', 'green', 'blue', 'red']
labels
Out[9]:
['It is a red point',
'What a wonderful blue point',
'Sounds to be a green point',
'What a wonderful blue point',
'It is a red point']
On each subfigure, I need to add an horizontal line.
Here is my problem: When I add a legend to the figure, the label is repeated if the color is repeated and the horizontal line is included in the legend which will "shift" the legend.
How can I ignore the repeated colors in the legend as well as not include the black line in it ?
Here is my code:
import pandas as pd
from matplotlib.pyplot import show, subplots
# Definition of the dataframe
df = pd.DataFrame({'Colors': {0: 'Red', 1: 'Blue', 2: 'Green', 3: 'Blue', 4: 'Red'}, 'X_values': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5}, 'Y_values': {0: 2, 1: 4, 2: 8, 3: 10, 4: 4}, 'MinY_values': {0: 1.5, 1: 3, 2: 7.5, 3: 8, 4: 2}, 'MaxY_values': {0: 2.5, 1: 5, 2: 9.5, 3: 11, 4: 6}})
# Definition of the different colors and labels
color = []
labels = []
for i in df['Colors']:
if i == 'Red':
color.append('red')
labels.append('It is a red point')
if i == 'Blue':
color.append('blue')
labels.append('What a wonderful blue point')
if i == 'Green':
color.append('green')
labels.append('Sounds to be a green point')
# Figure
fig,axes = subplots(3,1,sharex = True)
fig.subplots_adjust(top=0.975,
bottom=0.07,
left=0.05,
right=0.585,
hspace=0.0,
wspace=0.2)
for x_val,y_val,min_val,max_val,colors in zip(df['X_values'],df['Y_values'],df['MinY_values'],df['MaxY_values'],color):
axes[0].errorbar(x_val,y_val,yerr = [[min_val],[max_val]] ,color = colors,barsabove='True',fmt = 'o')
axes[0].axhline(y=5, color='black', linestyle='--')
for x_val,y_val,min_val,max_val,colors in zip(df['X_values'],df['Y_values'],df['MinY_values'],df['MaxY_values'],color):
axes[1].errorbar(x_val,y_val,yerr = [[min_val],[max_val]] ,color = colors,barsabove='True',fmt = 'o')
axes[1].axhline(y=5, color='black', linestyle='--')
for x_val,y_val,min_val,max_val,colors in zip(df['X_values'],df['Y_values'],df['MinY_values'],df['MaxY_values'],color):
axes[2].errorbar(x_val,y_val,yerr = [[min_val],[max_val]] ,color = colors,barsabove='True',fmt = 'o',label = labels)
axes[2].axhline(y=5, color='black', linestyle='--')
# Legend
fig.legend(labels)
fig.legend(bbox_to_anchor=(2,2), loc='center', ncol=1)
show()
In this case, the easiest is to create a custom legend.
Some notes:
plt.tight_layout()
can be used to fit the legend and the labels nicely into the plot; this is much more flexible than fig.subplots_adjust()
, especially when later changes are made to the plotsfig.legend()
uses the "current ax" (current subplot), which in this case is axes[2]
loc='upper left'
for the anchor point; 'loc' values that aren't at the left are too hard to control (bbox_to_anchor
sets the position of the anchor point, measured in 'axes coordinates' of the given subplot)Here is an example:
from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import pandas as pd
# Definition of the dataframe
df = pd.DataFrame(
{'Colors': {0: 'Red', 1: 'Blue', 2: 'Green', 3: 'Blue', 4: 'Red'}, 'X_values': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5},
'Y_values': {0: 2, 1: 4, 2: 8, 3: 10, 4: 4}, 'MinY_values': {0: 1.5, 1: 3, 2: 7.5, 3: 8, 4: 2},
'MaxY_values': {0: 2.5, 1: 5, 2: 9.5, 3: 11, 4: 6}})
fig, axes = plt.subplots(3, 1, sharex=True, gridspec_kw={'hspace': 0.0})
for x_val, y_val, min_val, max_val, color in zip(df['X_values'], df['Y_values'],
df['MinY_values'], df['MaxY_values'], df['Colors']):
axes[0].errorbar(x_val, y_val, yerr=[[min_val], [max_val]], color=color, barsabove='True', fmt='o')
axes[0].axhline(y=5, color='black', linestyle='--')
for x_val, y_val, min_val, max_val, color in zip(df['X_values'], df['Y_values'],
df['MinY_values'], df['MaxY_values'], df['Colors']):
axes[1].errorbar(x_val, y_val, yerr=[[min_val], [max_val]], color=color, barsabove='True', fmt='o')
axes[1].axhline(y=5, color='black', linestyle='--')
for x_val, y_val, min_val, max_val, color in zip(df['X_values'], df['Y_values'],
df['MinY_values'], df['MaxY_values'], df['Colors']):
axes[2].errorbar(x_val, y_val, yerr=[[min_val], [max_val]], color=color, barsabove='True', fmt='o')
axes[2].axhline(y=5, color='black', linestyle='--')
# Legend
legend_handles = [Line2D([], [], color='red', label='It is a red point', marker='o', ls='-'),
Line2D([], [], color='blue', label='What a wonderful blue point', marker='o', ls='-'),
Line2D([], [], color='green', label='Sounds to be a green point', marker='o', ls='-')]
axes[0].legend(handles=legend_handles, bbox_to_anchor=(1.01, 1), loc='upper left', ncol=1)
plt.tight_layout()
plt.show()