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.

Combining Python’s Basemap and NetworkX

Recently I have been involved with a project mapping relationships between countries in terms of a social network. There are a lot of social network analysis packages around; I prefer Python’s NetworkX largely because I’m already so used to Python.

The first thing everyone wants to see when doing sna is the network graph…understandable of course as they look pretty visually attractive and are a welcome respite from a field (political science) which is dominated by text. However as we all know sna graphs can also be a bit misleading, unless you are very good at reading them. The fact that node position doesn’t (necessarily) mean anything is a bit of a disadvantage, and once you have more than a few nodes actually understanding link patterns is essentially impossible. Using a classic layout for my country relations SNA for example gives me this:

country-sna

Even with degree included as node size and colour I still don’t find it very informative. One way of improving the situation is to give some meaning to the node position, which is of course especially easy with countries. Displaying a sna as a world map has two advantages in my opinion: everyone knows the names of a lot of the countries (which saves you having to label nodes), and you can also get a quick handle on any geographical patterns.

Python’s Basemap module can be easily combined with NetworkX. The key is to build your NX graph’s ‘pos’ list as you are building your overall node list, using Basemap to transform node coordinates. Of course you will need a list of country longitudes and latitudes but there are plenty of those available.

Hence when I am building my overall graph, I do:

x,y = m(lon, lat)
G.add_node(country)
pos[country]=(x,y)

where m is any Basemap projection and lon, lat are the coordinates of the country in question.

country-sna-2

Much nicer.

By | 2013-01-01T16:05:39+00:00 January 1st, 2013|Basemap, NetworkX, Programming, Python|2 Comments