Search code examples
pythonmatplotlibplotlegendscatter

incomplete scatter plot legend: not enough sizes for the present points


I want to have in the legend all the sizes present in the plot. I don't know why i'm only printing 1 size and only 1 price. This is the code:

import numpy as np
import matplotlib.pyplot as plt

# canvas , blackboard learn, moodle, D2L, Edmodo
# green  ,    blue         ,yellow , red, orange

# x= CVSS v2 score (variabile 1)
x=[0.22,-1.4,-0.86,0.65,0.84]

# y= supported OS (variabile 2)
y=[1.16,-0.77,-1.25,0.68,0.19]

colors=['green','blue','black','red','orange']
labels=['Canvas','BlackBoard Learn','Moodle','D2L', 'Edmodo']
scale=[1.76,24,1.7,1.83,8.75]  

fig, ax = plt.subplots(figsize=(7,5))

for i in range(0,5):
    scale[i]*=200
    scatter=ax.scatter(x[i], y[i], s=scale[i], c=colors[i], label=labels[i], alpha=0.45)

    
lgnd= plt.legend(loc="upper right")
plt.xlabel('CVSS v2 score')
plt.ylabel('supported OS')
plt.ylim((-2,2))
plt.xlim((-1.5,4))

lgnd.legendHandles[0]._sizes = [60]
lgnd.legendHandles[1]._sizes = [60]
lgnd.legendHandles[2]._sizes = [60]
lgnd.legendHandles[3]._sizes = [60]
lgnd.legendHandles[4]._sizes = [60]
ax.add_artist(lgnd)

kw = dict(prop="sizes", num=20, color=scatter.cmap(0.1), fmt="$ {x:.2f}"+" / month", func=lambda s: s/200)
legend2 = ax.legend(*scatter.legend_elements(**kw), prop={'size':14}, loc="lower right", title="Price") 
plt.show()

This is the output:

enter image description here

I'd like to have something like this:

enter image description here


Solution

  • It is possible to do by creating virtual artists without data points as custom handles to the legend, in which you can specifiy how you want your legend to look like.

    Also, if you use the object oriented approach in matplotlib, you should be consistent and also set limits and axis labels using the object oriented approach by calling the respective methods of the axis instead (if did that in the code for you).

    import numpy as np
    import matplotlib.pyplot as plt
    
    # x= CVSS v2 score (variabile 1)
    x=[0.22,-1.4,-0.86,0.65,0.84]
    
    # y= supported OS (variabile 2)
    y=[1.16,-0.77,-1.25,0.68,0.19]
    
    colors = ['green','blue','black','red','orange']
    labels = ['Canvas','BlackBoard Learn','Moodle','D2L', 'Edmodo']
    prices = [1.76,24,1.7,1.83,8.75]
    scales = [price * 200 for price in prices]
    
    fig, ax = plt.subplots(figsize=(7,5))
    
    ax.set_xlabel('CVSS v2 score')
    ax.set_ylabel('supported OS')
    ax.set_ylim((-2,2))
    ax.set_xlim((-1.5,4))
    
    for i in range(0,5):
        scatter=ax.scatter(x[i], y[i], s=scales[i], c=colors[i], label=labels[i], alpha=0.45)
    
    labels1 = labels
    handles1 = [ax.scatter([], [], s=60, c=color, alpha=0.45) for color in colors]
    legend1 = ax.legend(handles=handles1, labels=labels1, loc="upper right")
    
    labels2 = [f'$ {price} / month' for price in sorted(prices)]
    handles2 = [ax.scatter([], [], s=size, c='purple', alpha=0.45) for size in sorted(scales)]
    legend2 = ax.legend(handles=handles2, labels=labels2, loc="lower right")
    ax.add_artist(legend1)
    
    plt.show()
    

    My result looks like this:

    enter image description here