Extra Legend in Matplotlib Plot - matplotlib

I have the legend that matplotlib created from the data, but I would like to add an extra legend with some other information I need to include.
How can I do that?
I am new to Python and I read several forums, but I can't find anything on this

You need to manually add the different legends to your plot. You can add more than one legend to your plot by using .add_artist.
data1 = plt.scatter([1, 2, 3],[3,2,1])
data2 = plt.scatter([4,6,7],[3,2,8])
# Create a legend
legend_1 = plt.legend(handles=[data1])
# Add the legend to the current Axes.
ax = plt.gca().add_artist(legend_1)
# Create another legend for the second line.
plt.legend(handles=[data2])
More info on the legend guide: https://matplotlib.org/3.3.3/tutorials/intermediate/legend_guide.html#multiple-legends-on-the-same-axes

Related

How to get legend next to plot in Seaborn?

I am plotting a relplot with Seaborn, but getting the legend (and an empty axis plot) printed under the main plot.
Here is how it looks like (in 2 photos, as my screen isn't that big):
Here is the code I used:
fig, axes = plt.subplots(1, 1, figsize=(12, 5))
clean_df['tax_class_at_sale'] = clean_df['tax_class_at_sale'].apply(str)
sns.relplot(x="sale_price_millions", y='gross_sqft_thousands', hue="neighborhood", data=clean_df, ax=axes)
fig.suptitle('Sale Price by Neighborhood', position=(.5,1.05), fontsize=20)
fig.tight_layout()
fig.show()
Does someone has an idea how to fix that, so that the legend (maybe much smaller, but it's not a problem) is printed next to the plot, and the empty axis disappears?
Here is my dataset form (in 2 screenshot, to capture all columns. "sale_price_millions" is the target column)
Since you failed to provide a Minimal, Complete, and Verifiable example, no one can give you a final working answer because we can't reproduce your figure. Nevertheless, you can try specifying the location for placing the legend as following and see if it works as you want
sns.relplot(x="sale_price_millions", y='gross_sqft_thousands', hue="neighborhood", data=clean_df, ax=axes)
plt.legend(loc=(1.05, 0.5))

Second Matplotlib figure doesn't save to file

I've drawn a plot that looks something like the following:
It was created using the following code:
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
# 1. Plot a figure consisting of 3 separate axes
# ==============================================
plotNames = ['Plot1','Plot2','Plot3']
figure, axisList = plt.subplots(len(plotNames), sharex=True, sharey=True)
tempDF = pd.DataFrame()
tempDF['date'] = pd.date_range('2015-01-01','2015-12-31',freq='D')
tempDF['value'] = np.random.randn(tempDF['date'].size)
tempDF['value2'] = np.random.randn(tempDF['date'].size)
for i in range(len(plotNames)):
axisList[i].plot_date(tempDF['date'],tempDF['value'],'b-',xdate=True)
# 2. Create a new single axis in the figure. This new axis sits over
# the top of the axes drawn previously. Make all the components of
# the new single axis invisibe except for the x and y labels.
big_ax = figure.add_subplot(111)
big_ax.set_axis_bgcolor('none')
big_ax.set_xlabel('Date',fontweight='bold')
big_ax.set_ylabel('Random normal',fontweight='bold')
big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
big_ax.spines['right'].set_visible(False)
big_ax.spines['top'].set_visible(False)
big_ax.spines['left'].set_visible(False)
big_ax.spines['bottom'].set_visible(False)
# 3. Plot a separate figure
# =========================
figure2,ax2 = plt.subplots()
ax2.plot_date(tempDF['date'],tempDF['value2'],'-',xdate=True,color='green')
ax2.set_xlabel('Date',fontweight='bold')
ax2.set_ylabel('Random normal',fontweight='bold')
# Save plot
# =========
plt.savefig('tempPlot.png',dpi=300)
Basically, the rationale for plotting the whole picture is as follows:
Create the first figure and plot 3 separate axes using a loop
Plot a single axis in the same figure to sit on top of the graphs
drawn previously. Label the x and y axes. Make all other aspects of
this axis invisible.
Create a second figure and plot data on a single axis.
The plot displays just as I want when using jupyter-notebook but when the plot is saved, the file contains only the second figure.
I was under the impression that plots could have multiple figures and that figures could have multiple axes. However, I suspect I have a fundamental misunderstanding of the differences between plots, subplots, figures and axes. Can someone please explain what I'm doing wrong and explain how to get the whole image to save to a single file.
Matplotlib does not have "plots". In that sense,
plots are figures
subplots are axes
During runtime of a script you can have as many figures as you wish. Calling plt.save() will save the currently active figure, i.e. the figure you would get by calling plt.gcf().
You can save any other figure either by providing a figure number num:
plt.figure(num)
plt.savefig("output.png")
or by having a refence to the figure object fig1
fig1.savefig("output.png")
In order to save several figures into one file, one could go the way detailed here: Python saving multiple figures into one PDF file.
Another option would be not to create several figures, but a single one, using subplots,
fig = plt.figure()
ax = plt.add_subplot(611)
ax2 = plt.add_subplot(612)
ax3 = plt.add_subplot(613)
ax4 = plt.add_subplot(212)
and then plot the respective graphs to those axes using
ax.plot(x,y)
or in the case of a pandas dataframe df
df.plot(x="column1", y="column2", ax=ax)
This second option can of course be generalized to arbitrary axes positions using subplots on grids. This is detailed in the matplotlib user's guide Customizing Location of Subplot Using GridSpec
Furthermore, it is possible to position an axes (a subplot so to speak) at any position in the figure using fig.add_axes([left, bottom, width, height]) (where left, bottom, width, height are in figure coordinates, ranging from 0 to 1).

Add a new axis to the right/left/top-right of an axis

How do you add an axis to the outside of another axis, keeping it within the figure as a whole? legend and colorbar both have this capability, but implemented in rather complicated (and for me, hard to reproduce) ways.
You can use the subplots command to achieve this, this can be as simple as py.subplot(2,2,1) where the first two numbers describe the geometry of the plots (2x2) and the third is the current plot number. In general it is better to be explicit as in the following example
import pylab as py
# Make some data
x = py.linspace(0,10,1000)
cos_x = py.cos(x)
sin_x = py.sin(x)
# Initiate a figure, there are other options in addition to figsize
fig = py.figure(figsize=(6,6))
# Plot the first set of data on ax1
ax1 = fig.add_subplot(2,1,1)
ax1.plot(x,sin_x)
# Plot the second set of data on ax2
ax2 = fig.add_subplot(2,1,2)
ax2.plot(x,cos_x)
# This final line can be used to adjust the subplots, if uncommentted it will remove all white space
#fig.subplots_adjust(left=0.13, right=0.9, top=0.9, bottom=0.12,hspace=0.0,wspace=0.0)
Notice that this means things like py.xlabel may not work as expected since you have two axis. Instead you need to specify ax1.set_xlabel("..") this makes the code easier to read.
More examples can be found here.

Adding a collection to multiple axis?

I am trying to add a BrokenBarHCollection to multiple axis on my figure, as follows:
barcollection = collections.BrokenBarHCollection(...
ax1 = plt.subplot(211)
ax1.add_collection(barcollection)
ax2 = plt.subplot(212)
ax2.add_collection(barcollection)
plt.show()
As is, the figure only shows the collection in the second subplot. If I comment the ax2.add line out, it shows the collection only in the first subplot. Declaring the barcollection again between lines 3 and 4 makes it show up in both subplots. Why is this happening?
This is because the matplotlib objects know what plot they are attached to and will not attach to more than one.
If you use the copy module to make a shallow copy, then you can re-use most of the data structure across multiple axes.
import copy
bc2 = copy.copy(barcollection)
ax2.add_collection(bc2)
There was another question about this recently, but I am having trouble finding it.

How to create a scatter plot legend with only one symbol for each label?

How can I create a scatter plot legend without two symbols showing up each time? I can understand why you'd want this when you're joining symbols by lines, but for a pure scatter plot, all I want in the legend is one example of the symbol. This plot from a previous stackoverflow post shows the kind of thing I mean:
In the legend command you can use the scatterpoints option:
ax.legend(loc=0, scatterpoints = 1)
For a normal plot, it is the option numpoints.
Here you can find more information about the keyword arguments for the legend: http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.legend