Remove the legend on a matplotlib figure - matplotlib

To add a legend to a matplotlib plot, one simply runs legend().
How to remove a legend from a plot?
(The closest I came to this is to run legend([]) in order to empty the legend from data. But that leaves an ugly white rectangle in the upper right corner.)

As of matplotlib v1.4.0rc4, a remove method has been added to the legend object.
Usage:
ax.get_legend().remove()
or
legend = ax.legend(...)
...
legend.remove()
See here for the commit where this was introduced.

If you want to plot a Pandas dataframe and want to remove the legend, add legend=None as parameter to the plot command.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df2 = pd.DataFrame(np.random.randn(10, 5))
df2.plot(legend=None)
plt.show()

You could use the legend's set_visible method:
ax.legend().set_visible(False)
draw()
This is based on a answer provided to me in response to a similar question I had some time ago here
(Thanks for that answer Jouni - I'm sorry I was unable to mark the question as answered... perhaps someone who has the authority can do so for me?)

if you call pyplot as plt
frameon=False is to remove the border around the legend
and '' is passing the information that no variable should be in the legend
import matplotlib.pyplot as plt
plt.legend('',frameon=False)

you have to add the following lines of code:
ax = gca()
ax.legend_ = None
draw()
gca() returns the current axes handle, and has that property legend_

According to the information from #naitsirhc, I wanted to find the official API documentation. Here are my finding and some sample code.
I created a matplotlib.Axes object by seaborn.scatterplot().
The ax.get_legend() will return a matplotlib.legned.Legend instance.
Finally, you call .remove() function to remove the legend from your plot.
ax = sns.scatterplot(......)
_lg = ax.get_legend()
_lg.remove()
If you check the matplotlib.legned.Legend API document, you won't see the .remove() function.
The reason is that the matplotlib.legned.Legend inherited the matplotlib.artist.Artist. Therefore, when you call ax.get_legend().remove() that basically call matplotlib.artist.Artist.remove().
In the end, you could even simplify the code into two lines.
ax = sns.scatterplot(......)
ax.get_legend().remove()

If you are not using fig and ax plot objects you can do it like so:
import matplotlib.pyplot as plt
# do plot specifics
plt.legend('')
plt.show()

Here is a more complex example of legend removal and manipulation with matplotlib and seaborn dealing with subplots:
From seaborn, get the Axes object created by sns.<some_plot>() and do ax.get_legend().remove() as indicated by #naitsirhc. The following example also shows how to put the legend aside, and how to deal in a context of subplots.
# imports
import seaborn as sns
import matplotlib.pyplot as plt
# get data
sns.set()
sns.set_theme(style="darkgrid")
tips = sns.load_dataset("tips")
# subplots
fig, axes = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(12,6))
fig.suptitle('Example of legend manipulations on subplots with seaborn')
g0 = sns.pointplot(ax=axes[0], data=tips, x="day", y="total_bill", hue="size")
g0.set(title="Pointplot with no legend")
g0.get_legend().remove() # <<< REMOVE LEGEND HERE
g1 = sns.swarmplot(ax=axes[1], data=tips, x="day", y="total_bill", hue="size")
g1.set(title="Swarmplot with legend aside")
# change legend position: https://www.statology.org/seaborn-legend-position/
g1.legend(bbox_to_anchor=(1.02, 1), loc='upper left', borderaxespad=0)

I made a legend by adding it to the figure, not to an axis (matplotlib 2.2.2). To remove it, I set the legends attribute of the figure to an empty list:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax1.plot(range(10), range(10, 20), label='line 1')
ax2.plot(range(10), range(30, 20, -1), label='line 2')
fig.legend()
fig.legends = []
plt.show()

If you are using seaborn you can use the parameter legend. Even if you are ploting more than once in the same figure. Example with some df
import seaborn as sns
# Will display legend
ax1 = sns.lineplot(x='cars', y='miles', hue='brand', data=df)
# No legend displayed
ax2 = sns.lineplot(x='cars', y='miles', hue='brand', data=df, legend=None)

you could simply do:
axs[n].legend(loc='upper left',ncol=2,labelspacing=0.01)
for i in [4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]:
axs[i].legend([])

Related

Undo plt.gcf().subplots_adjust when plotting and saving more than one chart

When plotting successive charts in a single Python script, plt.clf() does not reverse the effect of plt.gcf().subplots_adjust(bottom=0.5). The first chart is adjusted to allow x-axis labels more display space, but this change persists into the second chart. How can one plot the second chart in dist() to look normal? Even calling sns.set() or re-importing plt in the function dist() or after box_adj() is called didn't seem to fix the issue.
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
def box_adj():
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", hue="smoker",
data=tips, palette="Set3")
plt.gcf().subplots_adjust(bottom=0.5)
plt.savefig('box.png')
plt.clf()
def dist():
ax = sns.distplot(np.random.randn(1000), kde=False)
plt.savefig('dist.png')
plt.clf()
if __name__ == "__main__":
box_adj()
dist()
plt.clf() only clears the contents of the plot figure, leaving the figure intact. Its whitespace settings and its size don't change.
You could replace plt.clf() with plt.close() to fully close the figure (matplotlib will automatically create a new one when needed). Alternatively, you can explicitly call fig = plt.figure(...) or fig, ax = plt.subplots(...) to create a new figure with default settings.

How to bold the pandas boxplot

This is my boxplot. When putting in the paper, the boxplot lines look very thin. I tried whiskers but it only bold part of lines. Do you know how to bold all lines in the boxplot? Thank you.
I don't know which graph library you are using, but matplotlib allows you to use the one defined in boxprops. See the official reference.
import numpy as np
import matplotlib.pyplot as plt
# fake data
np.random.seed(100)
data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75)
labels = list('ABCD')
fs = 10 # fontsize
boxprops = dict(linestyle='-', linewidth=3, color='k')
whiskerprops = dict(linestyle='-', linewidth=3, color='k')
capprops = dict(linestyle='-', linewidth=3, color='k')
fig, ax = plt.subplots(1,1, figsize=(6, 6))
ax.boxplot(data, boxprops=boxprops, whiskerprops=whiskerprops, capprops=capprops)
ax.set_title('Custom boxprops', fontsize=fs)
plt.show()

How to use mode='expand' and center a figure-legend label given only one label entry?

I would like to generate a centered figure legend for subplot(s), for which there is a single label. For my actual use case, the number of subplot(s) is greater than or equal to one; it's possible to have a 2x2 grid of subplots and I would like to use the figure-legend instead of using ax.legend(...) since the same single label entry will apply to each/every subplot.
As a brief and simplified example, consider the code just below:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y, color='orange', label='$f(x) = sin(x)$')
fig.subplots_adjust(bottom=0.15)
fig.legend(mode='expand', loc='lower center')
plt.show()
plt.close(fig)
This code will generate the figure seen below:
I would like to use the mode='expand' kwarg to make the legend span the entire width of the subplot(s); however, doing so prevents the label from being centered. As an example, removing this kwarg from the code outputs the following figure.
Is there a way to use both mode='expand' and also have the label be centered (since there is only one label)?
EDIT:
I've tried using the bbox_to_anchor kwargs (as suggested in the docs) as an alternative to mode='expand', but this doesn't work either. One can switch out the fig.legend(...) line for the line below to test for yourself.
fig.legend(loc='lower center', bbox_to_anchor=(0, 0, 1, 0.5))
The handles and labels are flush against the left side of the legend. There is no mechanism to allow for aligning them.
A workaround could be to use 3 columns of legend handles and fill the first and third with a transparent handle.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y = np.sin(x)
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.15)
line, = ax.plot(x, y, color='orange', label='$f(x) = sin(x)$')
proxy = plt.Rectangle((0,0),1,1, alpha=0)
fig.legend(handles=[proxy, line, proxy], mode='expand', loc='lower center', ncol=3)
plt.show()

how to customize color legend when using for loop in matplotlib, scatter

I want to draw a 3D scatter, in which the data is colored by group. Here is the data sample:
aa=pd.DataFrame({'a':[1,2,3,4,5],
'b':[2,3,4,5,6],
'c':[1,3,4,6,9],
'd':[0,0,1,2,3],
'e':['abc','sdf','ert','hgf','nhkm']})
Here, a, b, c are axis x, y, z. e is the text shown in the scatter. I need d to group the data and show different colors.
Here is my code:
fig = plt.figure()
ax = fig.gca(projection='3d')
zdirs = aa.loc[:,'e'].__array__()
xs = aa.loc[:,'a'].__array__()
ys = aa.loc[:,'b'].__array__()
zs = aa.loc[:,'c'].__array__()
colors = aa.loc[:,'d'].__array__()
colors1=np.where(colors==0,'grey',
np.where(colors==1,'yellow',
np.where(colors==2,'green',
np.where(colors==3,'pink','red'))))
for i in range(len(zdirs)): #plot each point + it's index as text above
ax.scatter(xs[i],ys[i],zs[i],color=colors1[i])
ax.text(xs[i],ys[i],zs[i], '%s' % (str(zdirs[i])), size=10, zorder=1, color='k')
ax.set_xlabel('a')
ax.set_ylabel('b')
ax.set_zlabel('c')
plt.show()
But I do not know how to put a legend on the plot. I hope my legend is like:
The colors and the numbers should match and be ordered.
Could anyone help me with how to customize the color bar?
First of all, I've taken the liberty to reduce your code a bit:
I'd suggest to create a ListedColormap to map integer->color, which allows you to pass the color column via c=aa['d'] (note it's c=, not color=!)
you don't need to use __array__() here, in the code below you can directly use aa['a']
finally, you can add an empty scatter plot for each color in the ListedColormap, and this can then be rendered correctly by ax.legend()
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib.colors import ListedColormap
import matplotlib.patches as mpatches
aa=pd.DataFrame({'a':[1,2,3,4,5],
'b':[2,3,4,5,6],
'c':[1,3,4,6,9],
'd':[0,0,1,2,3],
'e':['abc','sdf','ert','hgf','nhkm']})
fig = plt.figure()
ax = fig.gca(projection='3d')
cmap = ListedColormap(['grey', 'yellow', 'green', 'pink','red'])
ax.scatter(aa['a'],aa['b'],aa['c'],c=aa['d'],cmap=cmap)
for x,y,z,label in zip(aa['a'],aa['b'],aa['c'],aa['e']):
ax.text(x,y,z,label,size=10,zorder=1)
# Create a legend through an *empty* scatter plot
[ax.scatter([], [], c=cmap(i), label=str(i)) for i in range(len(aa))]
ax.legend()
ax.set_xlabel('a')
ax.set_ylabel('b')
ax.set_zlabel('c')
plt.show()

matplotlib: shorten a colorbar by half when the colorbar is created using axes_grid1

I am trying to shorten a colorbar by half. Does anyone know how to do this? I tried cax.get_position() and then cax.set_position(), but this method did not work.
Besides, it seems that axes created by axes_grid1 has the same bbox positions as the original axes. Is this a bug?
PS. I have to use axes_grid1 to create colorbar axes, because I need to use tight_layout() afterwards, and tight_layout() only applies to axes created by axes_grid1 but not ones created by add_axes().
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
bbox1 = ax.get_position()
print(bbox1)
bbox1 = cax.get_position()
print(bbox1)
plt.colorbar(im, cax=cax)
plt.show()
The whole point of the axes_divider is to divide the axes to make space for a new axes. This ensures that all axes have the same surrounding box. And that is the box you see being printed.
Some of the usual ways to create a colorbar, at a certain location in the figue are shown in this question. Here the problem seems to be to be able to call tight_layout. This is achievable with the following two options. (There might be others still.)
A. using gridspec
I'm not too sure about the exact requirements here, but it seems that using a normal grid layout would be more in the direction of what you need here.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
gs = gridspec.GridSpec(2, 2, width_ratios=[95,5],)
ax = fig.add_subplot(gs[:, 0])
im = ax.imshow(np.arange(100).reshape((10,10)))
cax = fig.add_subplot(gs[1, 1])
fig.colorbar(im, cax=cax, ax=ax)
plt.tight_layout()
plt.show()
B. Using axes_grid1
If you really need to use axes_grid1, it might become a little bit more complicated.
import matplotlib.pyplot as plt
import matplotlib.axes
from mpl_toolkits.axes_grid1 import make_axes_locatable, Size
import numpy as np
fig, ax = plt.subplots()
im = ax.imshow(np.arange(100).reshape((10,10)))
divider = make_axes_locatable(ax)
pad = 0.03
pad_size = Size.Fraction(pad, Size.AxesY(ax))
xsize = Size.Fraction(0.05, Size.AxesX(ax))
ysize = Size.Fraction(0.5-pad/2., Size.AxesY(ax))
divider.set_horizontal([Size.AxesX(ax), pad_size, xsize])
divider.set_vertical([ysize, pad_size, ysize])
ax.set_axes_locator(divider.new_locator(0, 0, ny1=-1))
cax = matplotlib.axes.Axes(ax.get_figure(),
ax.get_position(original=True))
locator = divider.new_locator(nx=2, ny=0)
cax.set_axes_locator(locator)
fig.add_axes(cax)
fig.colorbar(im, cax=cax)
plt.tight_layout()
plt.show()