Legend format lost after using: ax.legend(handles, labels ) - python

I have the same problem as in this post: Where the user wants to delete repeated entries in the legend:
Stop matplotlib repeating labels in legend
The answer works for me as well, however, when I use it, the legend format is completely lost. This happens when I apply the ax.legend(handles, labels) method. The following code (copied from http://matplotlib.org/examples/pylab_examples/legend_demo.html) illustrates this issue:
# Example data
a = np.arange(0,3, .02)
b = np.arange(0,3, .02)
c = np.exp(a)
d = c[::-1]
# Create plots with pre-defined labels.
# Alternatively, you can pass labels explicitly when calling `legend`.
fig, ax = plt.subplots()
ax.plot(a, c, 'k--', label='Model length')
ax.plot(a, d, 'k:', label='Data length')
ax.plot(a, c+d, 'k', label='Total message length')
# Now add the legend with some customizations.
legend = ax.legend(loc='upper center', shadow=True)
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels )
# The frame is matplotlib.patches.Rectangle instance surrounding the legend.
frame = legend.get_frame()
frame.set_facecolor('0.90')
# Set the fontsize
for label in legend.get_texts():
label.set_fontsize('large')
for label in legend.get_lines():
label.set_linewidth(1.5) # the legend line width
plt.show()
Result without using 'ax.legend(handles, labels)':
Resul using 'ax.legend(handles, labels)':
Any advice would be most welcomed
EDIT 1: Typo 'without' corrected

You have issued legend() call twice and the second time it is called without formatting arguments, replace:
legend = ax.legend(loc='upper center', shadow=True)
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels )
with
handles, labels = ax.get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
ax.legend(by_label.values(), by_label.keys(), loc='upper center', shadow=True)
Should do the trick.

Related

Exclude hue-variable from legend in pyplot

I struggle finding a way of properly displaying only the labels respective to the markers in a scatterplot. My code looks as follows:
fig, ax = plt.subplots(1,1)
plot_white = sns.scatterplot(data=df_white, x='EngCorr_Player', y='EngCorr_Opponent', hue='Elo_Opponent', ax=ax, marker='D', label='White')
plot_black = sns.scatterplot(data=df_black, x='EngCorr_Player', y='EngCorr_Opponent', hue='Elo_Opponent', ax=ax, marker='X', s=140, label='Black')
ax.legend()
plt.show()
The problem here, is that the variable for the hue is included in the legend. Plot 1
If I instead try to specify the labels when calling the legend, the marker of the second plot is wrong (circle, instead of star). Plot 2
ax.legend(labels=['White', 'Black'])
And if I specify the handles, with
ax.legend(handles=[plot_white, plot_black], labels=['White', 'Black'])
An empty legend is displayed and the error message "UserWarning: Legend does not support <AxesSubplot:xlabel='EngCorr_Player', ylabel='EngCorr_Opponent'> instances.
A proxy artist may be used instead." appears.
I tried to look into artists but don't grasp anything.
See if this what you are looking for... It is similar to what you have. Except that you get the handles and text of the legend using ax.get_legend_handles_labels(), then keep only those with names White and Black and then call ax.legend()
fig, ax = plt.subplots(1,1)
ax = sns.scatterplot(data=df_white, x='EngCorr_Player', y='EngCorr_Opponent', hue='Elo_Opponent', ax=ax, marker='D', label='White')
ax = sns.scatterplot(data=df_black, x='EngCorr_Player', y='EngCorr_Opponent', hue='Elo_Opponent', ax=ax, marker='X', s=140, label='Black')
hand, labl = ax.get_legend_handles_labels()
handout=[]
lablout=[]
for h,l in zip(hand,labl):
if l in ['White', 'Black']:
lablout.append(l)
handout.append(h)
ax.legend(handout, lablout)
plt.show()

How can I fix legend color issue in Bar graph in Python?

I have a problem about defining many color in legend part of bar graph.
After I've done some essential process, I draw a figure by using the code shown below.
ax = df.plot(kind='bar', stacked=True,figsize=(13,10))
plt.title('Title List', fontsize=20)
leg = ax.legend(loc='center right', bbox_to_anchor=(1.3, 0.5), ncol=1)
plt.tight_layout()
plt.savefig('images/image1.png', bbox_inches = "tight")
plt.show()
When I run the code, some colors are the same.
How can I define unique colors in legend part?
Here is the screenshot
My answer:
After I defining colormap as rainbow, All defined colors in legend parts became unique.
Change to code
ax = df.plot(kind='bar', stacked=True,figsize=(13,10))
to
ax = df.plot(kind='bar', stacked=True,figsize=(13,10), colormap='rainbow')

Matplotlib: how to vertically align multiple legends

I have a plot which has three legends of different length and width. I'm trying to find a way to place them nicely onto the plot. Currently I'm placing them with the default loc= which is great for dealing with the vertical placement. The issue is that by default the legends are right aligned, which looks messy.
Is there a way to use the default loc= to place them on the plot, but to have them left aligned?
Example:
From the legend guide.
import matplotlib.pyplot as plt
line1, = plt.plot([1,2,3], label="Line 1", linestyle='--')
line2, = plt.plot([3,2,1], label="Line 2\nThis is a \nvery long\nlegend", linewidth=4)
line3, = plt.plot([2,2,2], label="Can this be left justified?")
# Create a legend for the first two lines.
# 'loc' puts them in a nice place on the right.
first_legend = plt.legend(handles=[line1], loc=1)
second_legend = plt.legend(handles=[line2], loc=5)
# Add the legends manually to the current Axes.
ax = plt.gca().add_artist(first_legend)
ax = plt.gca().add_artist(second_legend)
# Create another legend for the last line.
plt.legend(handles=[line3], loc=4)
plt.show()
Which gives this
Now what I would really like is for the legends to left aligned but still on the right side of the plot. Like so:
I know I can place them at a specific location but to do this I need to specify both the x and y coords, which will be fiddly since all 3 have variable heights and widths.
You could use bbox_to_anchor to position legends precisely where you want them:
fig, ax = plt.subplots()
line1, = ax.plot([1,2,3], label="Line 1", linestyle='--')
line2, = ax.plot([3,2,1], label="Line 2\nThis is a \nvery long\nlegend", linewidth=4)
line3, = ax.plot([2,2,2], label="Can this be left justified?")
# Create a legend for the first two lines.
# 'loc' sets anchor position of the legend frame relative to itself,
# bbox_to_anchor puts legend's anchor to (x, y) in axes coordinates.
first_legend = ax.legend(handles=[line1], loc='upper left', bbox_to_anchor=(0.65, 1))
second_legend = ax.legend(handles=[line2], loc='center left', bbox_to_anchor=(0.65, 0.5))
# Add the legends manually to the current Axes.
ax.add_artist(first_legend)
ax.add_artist(second_legend)
# Create another legend for the last line.
ax.legend(handles=[line3], loc='lower left', bbox_to_anchor=(0.65, 0))
plt.show()
The only number you would need is x position of the legends' bbox_to_anchor to align to (0.65 in the example above).

Python subplots leaving space for common axis labels

I have the following code that makes four subplots in one figure:
f = figure( figsize=(7,7) )
f.add_axes([0.2,0.175,0.75,0.75])
f.subplots_adjust(left=0.15)
f.clf()
ax = f.add_subplot(111)
ax1 = f.add_subplot(221)
ax2 = f.add_subplot(222)
ax3 = f.add_subplot(223)
ax4 = f.add_subplot(224)
ax.xaxis.set_major_formatter( NullFormatter() )
ax.yaxis.set_major_formatter( NullFormatter() )
ax2.xaxis.set_major_formatter( NullFormatter() )
ax2.yaxis.set_major_formatter( NullFormatter() )
ax1.xaxis.set_major_formatter( NullFormatter() )
ax4.yaxis.set_major_formatter( NullFormatter() )
f.subplots_adjust(wspace=0,hspace=0)
ax1.plot(tbins[0:24], mean_yszth1, color='r', label='mean', marker='.', lw=3)
ax2.plot(tbins[0:24], mean_ysz1, color='r', label='mean', marker='.', lw=3)
ax3.plot(tbins[0:24], mean_yszth2, color='r', label='mean', marker='.', lw=3)
ax4.plot(tbins[0:24], mean_ysz2, color='r', label='mean', marker='.', lw=3)
ax1.set_xlim(0,12)
ax1.set_ylim(-0.5,0.5)
ax2.set_xlim(0,12)
ax2.set_ylim(-0.5,0.5)
ax3.set_xlim(0,12)
ax3.set_ylim(-0.5,0.5)
ax4.set_xlim(0,12)
ax4.set_ylim(-0.5,0.5)
ax.set_xlabel(r"$\mathrm{Time\ since\ last\ merger\ (Gyr)}$")
ax.set_ylabel(r"$\mathrm{\Delta Y_{SZ}/Y_{SZ}}$")
The result looks like this:
As you can see, the axis labels overlap with the ticks. I would like to move the common axis labels away from the axes a little. I can't figure out how best to do this.
Use labelpad parameter of set_ylabel and set_xlabel methods:
Definition: ax.set_ylabel(self, ylabel, fontdict=None, labelpad=None, **kwargs)
Docstring:
Call signature::
set_ylabel(ylabel, fontdict=None, labelpad=None, **kwargs)
Set the label for the yaxis
*labelpad* is the spacing in points between the label and the y-axis
This is what I get with labelpad set to 50 (x) and 60 (y). I had to modify manually figure margins as the labels were outside the figure frame when using the default configuration.
Edit
From your comments it seems you could be using a very old version of matplotlib. Labelpad parameter has been in matplotlib from many versions ago but the way to of setting it could be different (I do not know for sure).
In the web I found some comments that point to this usage:
ax.xaxis.LABELPAD = 8 # default is 5
also I have seen it like:
ax.xaxis.labelpad = 8

How to make custom legend in matplotlib

I currently generate my legend with matplotlib this way:
if t==25:
l1,l2 = ax2.plot(x320,vTemp320,'or',x320,vAnaTemp320,'-r')
elif t==50:
l3,l4 = ax2.plot(x320,vTemp320,'ob',x320,vAnaTemp320,'-b')
else:
l5,l6 = ax2.plot(x320,vTemp320,'og',x320,vAnaTemp320,'-g')
plt.legend((l1,l2,l3,l4,l5,l6), ('t=25 Simulation', 't=25 Analytical','t=50 Simulation', 't=50 Analytical','t=500 Simulation', 't=500 Analytical'),
bbox_to_anchor=(-.25, 1), loc=2, borderaxespad=0.,prop={'size':12})
Which somehow works see 1. But I have duplicated information in my legend.
I would prefer to seperate the legend. So that I have different colored lines corresponding to the time t. And a normal line as my Analytical solution an dots for the results of my simulation.
Something like that
--(red line) t = 25
--(blue line) t = 50
--(green line) t = 500
o Simulaton
-- Analytical Solution
Does anyone now how I could achieve this with matplotlib?
You can chose the artists and labels to display in the legend as follows. You'll need to create custom artists for the elements in the legend that are not actually plotted.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,10,31)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
#Plot analytic solution
ax.plot(x,1*x**2, color='r', label="t = 25")
ax.plot(x,2*x**2, color='b', label="t = 50")
ax.plot(x,3*x**2, color='g', label="t = 500")
#Plot simulation
ax.plot(x,1*x**2, color='r', linestyle='', marker='o')
ax.plot(x,2*x**2, color='b', linestyle='', marker='o')
ax.plot(x,3*x**2, color='g', linestyle='', marker='o')
#Get artists and labels for legend and chose which ones to display
handles, labels = ax.get_legend_handles_labels()
display = (0,1,2)
#Create custom artists
simArtist = plt.Line2D((0,1),(0,0), color='k', marker='o', linestyle='')
anyArtist = plt.Line2D((0,1),(0,0), color='k')
#Create legend from custom artist/label lists
ax.legend([handle for i,handle in enumerate(handles) if i in display]+[simArtist,anyArtist],
[label for i,label in enumerate(labels) if i in display]+['Simulation', 'Analytic'])
plt.show()

Categories

Resources