2
votes

Is there a way to add a secondary legend to a scatterplot, where the size of the scatter is proportional to some data?

I have written the following code that generates a scatterplot. The color of the scatter represents the year (and is taken from a user-defined df) while the size of the scatter represents variable 3 (also taken from a df but is raw data):

import pandas as pd 

colors = pd.DataFrame({'1985':'red','1990':'b','1995':'k','2000':'g','2005':'m','2010':'y'}, index=[0,1,2,3,4,5])

fig = plt.figure()
ax = fig.add_subplot(111)

for i in df.keys():
    df[i].plot(kind='scatter',x='variable1',y='variable2',ax=ax,label=i,s=df[i]['variable3']/100, c=colors[i])

ax.legend(loc='upper right')
ax.set_xlabel("Variable 1")
ax.set_ylabel("Variable 2")

This code (with my data) produces the following graph:

Representative figure with single legend

So while the colors/years are well and clearly defined, the size of the scatter is not.

How can I add a secondary or additional legend that defines what the size of the scatter means?

2
You could create your legends manually (see the Legend guide). Specifically this section as well as the section about multiple legends on the same axes might be interesting. The colors are clear but how do you know what the size of a marker is related to? How would you label the sizes? - a_guest
This question may also be of interest. - ImportanceOfBeingErnest

2 Answers

8
votes

You will need to create the second legend yourself, i.e. you need to create some artists to populate the legend with. In the case of a scatter we can use a normal plot and set the marker accordingly. This is shown in the below example. To actually add a second legend we need to add the first legend to the axes, such that the new legend does not overwrite the first one.

import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np; np.random.seed(1)
import pandas as pd
plt.rcParams["figure.subplot.right"] = 0.8
v = np.random.rand(30,4)
v[:,2] = np.random.choice(np.arange(1980,2015,5), size=30)
v[:,3] = np.random.randint(5,13,size=30)

df= pd.DataFrame(v, columns=["x","y","year","quality"])
df.year = df.year.values.astype(int)
fig, ax = plt.subplots()
for i, (name, dff) in enumerate(df.groupby("year")):
    c = matplotlib.colors.to_hex(plt.cm.jet(i/7.))
    dff.plot(kind='scatter',x='x',y='y', label=name, c=c, 
             s=dff.quality**2, ax=ax)

leg = plt.legend(loc=(1.03,0), title="Year")
ax.add_artist(leg)
h = [plt.plot([],[], color="gray", marker="o", ms=i, ls="")[0] for i in range(5,13)]
plt.legend(handles=h, labels=range(5,13),loc=(1.03,0.5), title="Quality")
plt.show()

enter image description here

4
votes

Have a look at http://matplotlib.org/users/legend_guide.html.

It shows how to have multiple legends (about halfway down) and there is another example that shows how to set the marker size.

If that doesn't work, then you can also create a custom legend (last example).