No handles with labels found to put in legend - python

I'm trying to create a parallelogram in PyPlot. I'm not up to drawing the parallelogram--first I'm putting in the vector arrows--using the following code:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
plt.axis([-5,5,-5,5])
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
plt.grid()
plt.arrow(0,0, 3,1, head_width=0.2, color='r', length_includes_head=True, label='u')
plt.arrow(0,0, 1,3, head_width=0.2, color='r', length_includes_head=True, label='v')
plt.arrow(0,0, 4,4, head_width=0.2, color='r', length_includes_head=True, label='u+v')
plt.legend()
This returns the following error:
No handles with labels found to put in legend.
I'm not sure why, because, based on the documentation for plt.arrow(), label is an acceptable kwarg, and plt.legend() should ostensibly be reading that. The rest of the figure draws fine; it's just missing the legend.

It might be late but for anyone with the same issue the solution is using the method legend() for the corresponding ax not as for plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
plt.axis([-5,5,-5,5])
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
plt.grid()
plt.arrow(0,0, 3,1, head_width=0.2, color='r', length_includes_head=True, label='u')
plt.arrow(0,0, 1,3, head_width=0.2, color='r', length_includes_head=True, label='v')
plt.arrow(0,0, 4,4, head_width=0.2, color='r', length_includes_head=True, label='u+v')
ax.legend()

You can explicitly define the elements in the legend.
For full control of which artists have a legend entry, it is possible to pass an iterable of legend artists followed by an iterable of legend labels respectively. Reference
Example:
arr1 = plt.arrow(0,0, 3,1, head_width=0.2, color='r', length_includes_head=True)
arr2 = plt.arrow(0,0, 1,3, head_width=0.2, color='g', length_includes_head=True)
arr3 = plt.arrow(0,0, 4,4, head_width=0.2, color='b', length_includes_head=True)
plt.xlim(0,5)
plt.ylim(0,5)
plt.legend([arr1, arr2, arr3], ['u','v','u+v'])

The error is thrown because you haven't specified the label text
Either do something like this
plt.hist([x01, x02,x03], color=["lightcoral","lightskyblue","slategrey"], stacked=True,
label=['Supressed','Active','Resolved'])
plt.legend()
Or
Do not use plt.legend() if you haven't specified the label text as in the following WRONG example:
plt.hist([x01])
plt.legend()
The above will throw the same error, so either remove legend function or provide what it needs -> label.
Side note: Here x01 is just a list of number for which I am creating a histogram, in the first example they are three list of numbers to create stacked bar chart
The bottom line is this error is thrown because of not specifying legend text and calling/initializing a legend

I had this error when using labels which started with an underscore
plt.plot(x, y, label = '_bad_name')
Removing the front underscore from the labels solved the issue

Assuming you have 2 plots ax and ax2, we can:
get the labels from each y-axis via ax.get_label()
.legend allows an array to be ingested
fig.legend([ax.get_ylabel(), ax2.get_ylabel()], loc='upper right')

I had this same issue and solved it with an understanding that .legend() function has to be after all the instructions that deal with the label attribute. This includes both plt and ax.
So moving ax.legend(*) as the last command.
I hope this helps you too.
Change
ax.plot(-trip_df.stop_lat, -trip_df.stop_lon, label = trip_id)
plt.legend()
to
ax.plot(-trip_df.stop_lat, -trip_df.stop_lon, label = trip_id)
ax.legend()
plt.legend()

I face similar problem like No handles with labels found to put in legend.
First My code look like
figure, axis = pyplot.subplots(nrows=1,ncols=2, figsize=(15, 6), tight_layout=True)
axis[0].legend(title='Country', title_fontsize = 12) #this line
axis[0].pie(x=piechart_result['value_eur'],labels=piechart_result['short_name'])
axis[1].pie(x=piechart_result['value_eur'],labels=piechart_result['short_name')
pyplot.show()
Then I changed to
figure, axis = pyplot.subplots(nrows=1,ncols=2, figsize=(15, 6), tight_layout=True)
axis[0].pie(x=piechart_result['value_eur'],labels=piechart_result['short_name'])
axis[0].legend(title='Country', title_fontsize = 12) # this line
axis[1].pie(x=piechart_result['value_eur'],labels=piechart_result['short_name')
pyplot.show()
this work for me in colab notebook

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 to create a single legend for subplots [duplicate]

This question already has answers here:
How do I make a single legend for many subplots?
(10 answers)
Closed 6 months ago.
I am trying to replicate the following plot but with a different set of data:
My current plot has everything you see except the legend in the top right corner. I am having a hard time figuring out how I am supposed to add this in with my current code:
fig = plt.figure()
plt.subplot(3, 1, 1)
plt.title('Task Switches and Avg Task Switches by Timestep', fontsize=10)
plt.ylabel('Task Switches', fontsize=9)
plt.xlim(-35, timestep_num + 35)
plt.xticks(np.arange(0, timestep_num+1, 50), fontsize=-1, color='white')
plt.yticks(np.arange(0, 61, 20), fontsize=6)
plt.plot([stepsum_list[i][6] for i in range(len(stepsum_list))], color='royalblue',
linewidth=0.7, linestyle='', marker='.', markersize=1)
plt.plot([stepsum_list[i][6]/(i+1) for i in range(len(stepsum_list))], color='limegreen',
linewidth=0.6,)
plt.subplot(3, 1, 2)
plt.title('Task Demand per Timestep by Task', fontsize=10)
plt.ylabel('Task Demand', fontsize=9)
plt.xlim(-35, timestep_num + 35)
plt.xticks(np.arange(0, timestep_num+1, 50), fontsize=-1, color='white')
plt.yticks(np.arange(0, 6, 1), fontsize=6)
plt.plot([stepdem_list[i][1] for i in range(len(stepdem_list))], color='darkorange',
linewidth=0.7, linestyle='', marker='.', markersize=1)
plt.plot([stepdem_list[i][2] for i in range(len(stepdem_list))], color='yellowgreen',
linewidth=0.7, linestyle='', marker='.', markersize=1)
plt.plot([stepdem_list[i][3] for i in range(len(stepdem_list))], color='purple',
linewidth=0.7, linestyle='', marker='.', markersize=1)
plt.plot([stepdem_list[i][4] for i in range(len(stepdem_list))], color='blue', linewidth=0.7,
linestyle='', marker='.', markersize=1)
plt.subplot(3, 1, 3)
plt.title('Target and Tracker Movement',fontsize=10)
plt.ylabel('Movement', fontsize=9)
plt.xlabel('Timesteps', fontsize=9)
plt.xlim(-35, timestep_num + 35)
plt.xticks(np.arange(0, timestep_num+1, 50), fontsize=8)
plt.yticks(np.arange(-10, 11, 10), fontsize=6)
plt.plot([stepsum_list[i][4] for i in range(len(stepsum_list))], color='blue', linewidth=.5)
plt.plot([stepsum_list[i][2] for i in range(len(stepsum_list))], color='red', linewidth=.5)
fig.align_labels()
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.4, hspace=0.4)
plt.savefig('prog02_output.png')
plt.show
I apologize for all of the repetitive code, I'm brand new to Python and this is my first time making a plot so I don't know all of the tricks just yet. I have found the function figlegend(), but I'm confused if this is what I am going to want to use, and if so how the parameters are working. Placing the legend in the correct spot (aligned with the top subplot) is also something I am trying to do, but can't seem to figure out.
I'm not asking anyone to write any code or rewrite what I have. Just for someone to point me in the right direction, whether that be explaining a function and what parameters it can take, or what might need to be changed in my current code to use figlegend().
The way I plot legends with my plots in Matplotlib is via the Axes.legend() function, shown below:
The source code is
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([0,1,2],[2,1,0], c='r', label='Plot 1')
ax.plot([0,1,2],[0,1,2], c='b', label='Plot 2')
ax.legend()
plt.show()
After you add labels to each of the data traces in your plot via the label keyword argument, then you can add a legend to the figure with
plt.gca().legend()
If your try to put more 3 variables, you can do this:
lns1 = plt.plot(x,y)
lns2 = plt.plot(x2,y)
lns3 = plt.plot(x3,y)
lns = lns1+lns2+lns3+lns4
labs = [l.get_label() for l in lns]
plt.figure()
When you go to plot, use this:
ax.legend(lns,labs,loc='lower center')

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)

Saving plot from ipython notebook produces a cut image

I am plotting a plot with 2 ylabels using ipython notebook and the image looks good when visualized inside the notebook.
Here is how I do it:
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
plt.title('TITLE')
plt.xlabel('X')
plt.plot(x, y1, '-', color='blue', label='SNR')
ax1.set_ylabel('y1', color='blue')
for tl in ax1.get_yticklabels():
tl.set_color('blue')
ax2 = ax1.twinx()
plt.plot(x, y2, '--', color='red', label='Ngal')
ax2.set_ylabel('y2', color='red')
for tl in ax2.get_yticklabels():
tl.set_color('red')
The problem is that when I try to save it with the command
plt.savefig('output.png', dpi=300)
since the output will be an image which is cut on the right side: basically I don't see the right ylabel if the right numbers are large.
By default, matplotlib leaves very little room for x and y axis labels and tick labels, therefore you need to adjust the figure to include more padding. Fortunately this could not be easier to do. Before you call savefig, you can call call
fig.tight_layout()
plt.savefig('output.png', dpi=300)
Alternatively, you can pass bbox_inches='tight' to savefig which will also adjust the figure to include all of the x and y labels
plt.savefig('output.png', dpi=300, bbox_inches='tight')

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

Categories

Resources