data science, digital politics, smart cities...|jonathan.bright@oii.ox.ac.uk

Point size legends in matplotlib and basemap plots

Python’s matplotlib and basemap can do a lot. I think I still prefer R’s ggplot, but I much prefer manipulating data in Python and sometimes it’s more natural to do the analysis in the same language.

Recently I have been combining the two packages to create maps of events happening around the world. The size and colour of the marker on the map can be varied, meaning it’s possible to fit quite a lot of information in one graphic.

Meetup - Map

However one thing I really struggled with was the legend. If you use different colour points matplotlib makes it easy to add a colour bar, with something like:

c = plt.colorbar(orientation='vertical', shrink = 0.5)
c.set_label("My Title")

Shrink gives you a quick way of adjusting the size of the bar relative to the graphic.

However I couldn’t find an equivalent command which would give me a legend for the size of the points (something that ggplot does easily). After fiddling with get_label() for ages, and trying to capture and do something useful with the results of plt.scatter(), I finally came across this useful post, which basically says that this feature doesn’t really exist and if you want such a legend you have to make it yourself. However, the trick to doing it is quite simple – draw three or four points on your plot with location set to [], [], (so they won’t actually show up), each one representing a certain size in your scale. These points can then be passed to plt.legend with some hand written labels. Overall it looks something like this:

l1 = plt.scatter([],[], s=10, edgecolors='none')
l2 = plt.scatter([],[], s=50, edgecolors='none')
l3 = plt.scatter([],[], s=100, edgecolors='none')
l4 = plt.scatter([],[], s=200, edgecolors='none')

labels = ["10", "50", "100", "200"]

leg = plt.legend([l1, l2, l3, l4], labels, ncol=4, frameon=True, fontsize=12,
handlelength=2, loc = 8, borderpad = 1.8,
handletextpad=1, title='My Title', scatterpoints = 1)

The results:

map with legend

Well, I still think that should be easier, but at least it works and it also gives you a lot of flexibility with what goes on the legend.

By |2014-08-12T16:51:36+00:00August 12th, 2014|Basemap, matplotlib, Programming, Python, Social Science Computing|4 Comments

About the Author:

4 Comments

  1. Ian Foster 16 September 2014 at 3:22 pm

    Dear Jonathan:

    Thanks for this most helpful post. Could you share the rest of the code? I am doing something wrong, I think, because the size of the circles in the legend does not match that on the map. Thanks!!

    Ian.

    • jonathanbright 18 September 2014 at 1:48 pm

      Thanks for your comment Ian. I’m happy to share the code, which I’ve sent by email. You won’t be able to run it without the underlying data, but hopefully you should be able to see the logic in it (though I must admit it’s a bit of a mess). Let me know if it’s not clear. The most important part is:

      x,y = m(lon, lat)
      points = m.scatter(x,y,zorder=3,s=sizes, c=colors, alpha=0.3, edgecolors='none')

      Where lon, lat, colors and sizes are lists of (obviously) longitudes, latitudes, colours and sizes of points, in the same order each time.

      For your problem though I would suggest checking range of values you are using to determine point sizes (in the example above it would be the list sizes), with the max and min of the range being the smallest and largest point, perhaps rounding up or down. Good luck!

  2. Maximilian Haeussler 17 July 2015 at 9:04 am

    Matplotlib really needs a function for this… it’s an extremely common use case. Many thanks!

Leave A Comment