In his paper A Layered Grammar of Graphics, Hadley Wickham re-builded Minard’s inforgraphic of Napoleon’s march on Russia to illustrate the layers mechanism in ggplot2. Since I’m the learning-by-doing type of person, in this post, I will reproduce Hadley’s example from start to finish.

Prepare data with dplyr package

The dataset is made available at this link.

minard <- read.csv("minard.csv", fileEncoding="UTF-8-BOM")

Let’s take a look at the all the original dataset. First, some column names are not good variable names, for example surviv = number of survivors, direc = direction, lonc, lont and lonp are different names for longitude. Second, the dataset is actually 3 tables merged together, the first 3 columns make up one table, the last 5 colums form another table. I’ll use those 2 tables to reproduce Minard’s graph.

minard

There is no need to reshape or tidy the dataset in this situation so the dplyr package is all we need to segment the original dataset.

library(dplyr)
#select relevant columns, rename columns's names and remove NA values
troops <- select(minard, long = lonp, lat = latp, survivors = surviv, direction = direc, division)
cities <- na.omit(select(minard, long = lonc, lat = latc, city))
#display tables
troops
cities

Plotting using ggplot2 package

ggplot2‘s layers mechanisim enables its users to “divide and conquer” a wide range of graphics. As a user, you simply layer your way to the final graphic. Subsequent layers inherent previous layers’ settings and can override those settings if needed.

First, we use ggplot() to create the “base layer”, the troops dataset will be passed to subsequent layers, similarly, long variable will be mapped to x-axis and lat varible will be mapped to y-axis in latter layers unless overrided.

library(ggplot2)
layer1 <- ggplot(troops, aes(long, lat))
layer1

This is the default code of geom_path() which makes up the second layer:

geom_path(mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., lineend = "butt", linejoin = "round", linemitre = 1, arrow = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
layer2 <- layer1 + geom_path(aes(size = survivors, color = direction, group = division), lineend = "round", linejoin = "round")
layer2

Now, to the third layer, the troops dataset specified in the call to ggplot() has now been overried with the cities dataset.

layer3 <- layer2 + geom_text(aes(label = city), size = 3, data = cities)
layer3

Did you notice that I didn’t name the next variable layer4 but named it finalGraph instead? I did that wasn’t because finalGraph is a better name, but because scale_size(), scale_color_manual(), xlab(), ylab(), ggtitle() add no new layer to the graph, they are SCALES functions, they control the mapping between data and aesthetics, in other words, they modify existing layers. But which layer do they modify? In this situation, they modified layer2 (i.e., the geom_path() layer).

The function scale_size() is very useful in this case to appropeiately display numeric values in the Survivors legend. The labels aesthetic can take function that takes the breaks value as input and returns labels as output, for example, here the comma() function takes the breaks value as input and changes a label from 1e+05 to 100,000.

You might be tempted to think that comma(breaks) also works but it doesn’t (I tried). Don’t let the = fools you, unlike v, breaks is the name of an aesthetic, not a variable in this case. Thus, you have to pass in the same vector assigned to breaks into comma(), here the vector is c(1, 2, 3) * 10^5. Now I hope you realize why, in R, = and <- should not be used interchangeably.

v <- c(1, 2, 3) * 10^5
finalGraph <- layer3 + scale_size("Survivors", range = c(1, 10), breaks = v, labels = comma(v)) +
  scale_color_manual("Direction", values = c("grey50", "red")) +
  xlab("Longitude") + ylab("Latitude") + ggtitle("Napoleon's march to Russia")
finalGraph

That’s it, thanks for reading!

LS0tDQp0aXRsZTogIlJlLWJ1aWxkIE1pbmFyZCdzIE5hcG9sZW9uJ3MgbWFyY2ggb24gUnVzc2lhIHdpdGggZHBseXIgJiBnZ3Bsb3QyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KSW4gaGlzIHBhcGVyIFsqQSBMYXllcmVkIEdyYW1tYXIgb2YgR3JhcGhpY3MqXShodHRwOi8vdml0YS5oYWQuY28ubnovcGFwZXJzL2xheWVyZWQtZ3JhbW1hci5wZGYpLCBIYWRsZXkgV2lja2hhbSByZS1idWlsZGVkIE1pbmFyZCdzIGluZm9yZ3JhcGhpYyBvZiBbTmFwb2xlb24ncyBtYXJjaCBvbiBSdXNzaWFdKGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpNaW5hcmQucG5nIy9tZWRpYS9GaWxlOk1pbmFyZC5wbmcpIHRvIGlsbHVzdHJhdGUgdGhlICoqbGF5ZXJzKiogbWVjaGFuaXNtIGluIGdncGxvdDIuIFNpbmNlIEknbSB0aGUgbGVhcm5pbmctYnktZG9pbmcgdHlwZSBvZiBwZXJzb24sIGluIHRoaXMgcG9zdCwgSSB3aWxsIHJlcHJvZHVjZSBIYWRsZXkncyBleGFtcGxlIGZyb20gc3RhcnQgdG8gZmluaXNoLiAgICAgICANCg0KI1ByZXBhcmUgZGF0YSB3aXRoIGBkcGx5cmAgcGFja2FnZSANClRoZSBkYXRhc2V0IGlzIG1hZGUgYXZhaWxhYmxlIGF0IHRoaXMgW2xpbmtdKGh0dHBzOi8vd3d3LmNzLnVpYy5lZHUvfndpbGtpbnNvbi9UaGVHcmFtbWFyT2ZHcmFwaGljcy9taW5hcmQudHh0KS4gIA0KDQpgYGB7cn0NCm1pbmFyZCA8LSByZWFkLmNzdigibWluYXJkLmNzdiIsIGZpbGVFbmNvZGluZz0iVVRGLTgtQk9NIikNCmBgYA0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgYWxsIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBGaXJzdCwgc29tZSBjb2x1bW4gbmFtZXMgYXJlIG5vdCBnb29kIHZhcmlhYmxlIG5hbWVzLCBmb3IgZXhhbXBsZSBgc3Vydml2YCA9IG51bWJlciBvZiBzdXJ2aXZvcnMsIGBkaXJlY2AgPSBkaXJlY3Rpb24sIGBsb25jYCwgYGxvbnRgIGFuZCBgbG9ucGAgYXJlIGRpZmZlcmVudCBuYW1lcyBmb3IgbG9uZ2l0dWRlLiBTZWNvbmQsIHRoZSBkYXRhc2V0IGlzIGFjdHVhbGx5IDMgdGFibGVzIG1lcmdlZCB0b2dldGhlciwgdGhlIGZpcnN0IDMgY29sdW1ucyBtYWtlIHVwIG9uZSB0YWJsZSwgdGhlIGxhc3QgNSBjb2x1bXMgZm9ybSBhbm90aGVyIHRhYmxlLiBJJ2xsIHVzZSB0aG9zZSAyIHRhYmxlcyB0byByZXByb2R1Y2UgTWluYXJkJ3MgZ3JhcGguICAgDQpgYGB7cn0NCm1pbmFyZA0KYGBgDQoNClRoZXJlIGlzIG5vIG5lZWQgdG8gcmVzaGFwZSBvciB0aWR5IHRoZSBkYXRhc2V0IGluIHRoaXMgc2l0dWF0aW9uIHNvIHRoZSBgZHBseXJgIHBhY2thZ2UgaXMgYWxsIHdlIG5lZWQgdG8gc2VnbWVudCB0aGUgb3JpZ2luYWwgZGF0YXNldC4gICAgDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQojc2VsZWN0IHJlbGV2YW50IGNvbHVtbnMsIHJlbmFtZSBjb2x1bW5zJ3MgbmFtZXMgYW5kIHJlbW92ZSBOQSB2YWx1ZXMNCnRyb29wcyA8LSBzZWxlY3QobWluYXJkLCBsb25nID0gbG9ucCwgbGF0ID0gbGF0cCwgc3Vydml2b3JzID0gc3Vydml2LCBkaXJlY3Rpb24gPSBkaXJlYywgZGl2aXNpb24pDQpjaXRpZXMgPC0gbmEub21pdChzZWxlY3QobWluYXJkLCBsb25nID0gbG9uYywgbGF0ID0gbGF0YywgY2l0eSkpDQojZGlzcGxheSB0YWJsZXMNCnRyb29wcw0KY2l0aWVzDQpgYGANCg0KI1Bsb3R0aW5nIHVzaW5nIGBnZ3Bsb3QyYCBwYWNrYWdlDQoNCmBnZ3Bsb3QyYCdzICoqbGF5ZXJzIG1lY2hhbmlzaW0qKiBlbmFibGVzIGl0cyB1c2VycyB0byAiZGl2aWRlIGFuZCBjb25xdWVyIiBhIHdpZGUgcmFuZ2Ugb2YgZ3JhcGhpY3MuIEFzIGEgdXNlciwgeW91IHNpbXBseSAqbGF5ZXIqIHlvdXIgd2F5IHRvIHRoZSBmaW5hbCBncmFwaGljLiBTdWJzZXF1ZW50IGxheWVycyBpbmhlcmVudCBwcmV2aW91cyBsYXllcnMnIHNldHRpbmdzIGFuZCBjYW4gb3ZlcnJpZGUgdGhvc2Ugc2V0dGluZ3MgaWYgbmVlZGVkLiAgDQoNCkZpcnN0LCB3ZSB1c2UgYGdncGxvdCgpYCB0byBjcmVhdGUgdGhlICoqImJhc2UgbGF5ZXIiKiosIHRoZSBgdHJvb3BzYCBkYXRhc2V0IHdpbGwgYmUgcGFzc2VkIHRvIHN1YnNlcXVlbnQgbGF5ZXJzLCBzaW1pbGFybHksIGBsb25nYCB2YXJpYWJsZSB3aWxsIGJlIG1hcHBlZCB0byB4LWF4aXMgYW5kIGBsYXRgIHZhcmlibGUgd2lsbCBiZSBtYXBwZWQgdG8geS1heGlzIGluIGxhdHRlciBsYXllcnMgdW5sZXNzIG92ZXJyaWRlZC4gIA0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxheWVyMSA8LSBnZ3Bsb3QodHJvb3BzLCBhZXMobG9uZywgbGF0KSkNCmxheWVyMQ0KYGBgDQoNClRoaXMgaXMgdGhlIFtkZWZhdWx0XShodHRwOi8vZG9jcy5nZ3Bsb3QyLm9yZy9jdXJyZW50L2dlb21fcGF0aC5odG1sKSBjb2RlIG9mIGBnZW9tX3BhdGgoKWAgd2hpY2ggbWFrZXMgdXAgdGhlIHNlY29uZCBsYXllcjogIA0KYGBge3J9DQpnZW9tX3BhdGgobWFwcGluZyA9IE5VTEwsIGRhdGEgPSBOVUxMLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCAuLi4sIGxpbmVlbmQgPSAiYnV0dCIsIGxpbmVqb2luID0gInJvdW5kIiwgbGluZW1pdHJlID0gMSwgYXJyb3cgPSBOVUxMLCBuYS5ybSA9IEZBTFNFLCBzaG93LmxlZ2VuZCA9IE5BLCBpbmhlcml0LmFlcyA9IFRSVUUpDQpgYGANCg0KKiBMZXQncyBzZWUgaG93IHNvbWUgaW1wb3J0YW50IHNldHRpbmdzIGFwcGx5IHRvIG91ciBjYXNlOiANCiAgICArICoqZGF0YSA9IE5VTEwqKjogYGxheWVyMmAgaW5oZXJpdHMgdGhlIGRhdGFzZXQgKGB0cm9vcHNgKSBzcGVjaWZpZWQgaW4gdGhlIGNhbGwgdG8gZ2dwbG90KCkgaW4gYGxheWVyMWANCiAgICArICoqaW5oZXJpdC5hZXMgPSBUUlVFKio6IGluaGVyaXRzIHRoZSBhZXN0aGV0aWNzIGluIGdncGxvdCgpLCB5b3UgZG9uJ3QgaGF2ZSB0byBjaGFuZ2UgdGhpcyB0byBgRkFMU0VgIHRvIG92ZXJyaWRlIGFueSBhZXN0aGV0aWMsIGBGQUxTRWAgbWVhbnMgaWdub3JlIGFsbCBhZXN0aGV0aWNzIGluIGdncGxvdCgpLCB1c2UgaXQgaWYgeW91IHdhbnQgdG8gc3RhcnQgYSBuZXcgbGF5ZXIgZnJvbSBmcm9tIHNjcmF0Y2ggb3IgdGhlcmUgYXJlIHRvbyBtYW55IGFlc3RoZXRpY3MgdG8gb3ZlcnJpZGUsIGBUUlVFYCBtZWFucyBrZWVwIGFlc3RoZXRpY3MgaW4gZ2dwbG90KCksIGNvbWJpbmVkIHRoZW0gd2l0aCBuZXcgYWVzdGhldGljcyBvciBvdmVycmlkZSB0aGVtLiBJbiBvdXIgY2FzZSwgd2hhdCBhcmUgaW5oZXJpdHRlZCBmcm9tIGdncGxvdCgpIGFyZTogbWFwIGBsb25nYCB2YXJpYWJsZSB0byB4LWF4aXMgYW5kIGBsYXRgIHZhcmlhYmxlIHRvIHktYXhpcy4NCiAgICArICoqc2hvdy5sZWdlbmQgPSBOQSoqOiBpbmNsdWRlcyBsZWdlbmQgaWYgYW55IGFlc3RoZXRpY3MgYXJlIG1hcHBlZCwgaGVyZSwgcGF0aCBzaXplcyBhcmUgbWFwcGVkIHRvIG51bWJlciBvZiBzdXJ2aXZvcnMsIGNvbG9yIHRvIGRpcmVjdGlvbiwgc28gdGhlcmUgd2lsbCBiZSB0d28gbGVnZW5kIGNvcnJlc3Nwb25kaW5nIHRvIHRob3NlIHR3byB2YXJpYmxlcy4NCiAgICArICoqbmEucm0gPSBGQUxTRSoqOiByZW1vdmVzIG1pc3NpbmcgdmFsdWVzIHdpdGggYSB3YXJuaW5nLCBzaW5jZSBJIGRlbGV0ZWQgbWlzaW5nIHZhbHVlcyBhdCBkYXRhIHByZXBhcmF0aW9uIHN0ZXAsIHRoZXJlIHdvbid0IGJlIGFueSB3YXJuaW5nIHNob3dpbmcgdXAsIGlmIHNldCB0byBgVFJVRWAsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgcmVtb3ZlZCB3aXRob3V0IGFueSB3YXJuaW5nLiBVbmxlc3MgSSBrbm93IHRoZSBkYXRhc2V0IGV4dHJlbWVseSB3ZWxsLCBJIHdpbGwgbmV2ZXIgc2V0IG5hLnJtID0gVFJVRSwgc2lsZW50IGVycm9ycyBhcmUgdGhlIGFtb25nIHRoZSB3b3JzdCB0eXBlIG9mIGVycm9ycy4NCg0KYGBge3J9DQpsYXllcjIgPC0gbGF5ZXIxICsgZ2VvbV9wYXRoKGFlcyhzaXplID0gc3Vydml2b3JzLCBjb2xvciA9IGRpcmVjdGlvbiwgZ3JvdXAgPSBkaXZpc2lvbiksIGxpbmVlbmQgPSAicm91bmQiLCBsaW5lam9pbiA9ICJyb3VuZCIpDQpsYXllcjINCmBgYA0KDQpOb3csIHRvIHRoZSB0aGlyZCBsYXllciwgdGhlIGB0cm9vcHNgIGRhdGFzZXQgc3BlY2lmaWVkIGluIHRoZSBjYWxsIHRvIGdncGxvdCgpIGhhcyBub3cgYmVlbiBvdmVycmllZCB3aXRoIHRoZSBgY2l0aWVzYCBkYXRhc2V0LiAgIA0KYGBge3J9DQpsYXllcjMgPC0gbGF5ZXIyICsgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGNpdHkpLCBzaXplID0gMywgZGF0YSA9IGNpdGllcykNCmxheWVyMw0KYGBgDQpEaWQgeW91IG5vdGljZSB0aGF0IEkgZGlkbid0IG5hbWUgdGhlIG5leHQgdmFyaWFibGUgYGxheWVyNGAgYnV0IG5hbWVkIGl0IGBmaW5hbEdyYXBoYCBpbnN0ZWFkPyBJIGRpZCB0aGF0IHdhc24ndCBiZWNhdXNlIGBmaW5hbEdyYXBoYCBpcyBhIGJldHRlciBuYW1lLCBidXQgYmVjYXVzZSBbc2NhbGVfc2l6ZSgpXShodHRwOi8vZG9jcy5nZ3Bsb3QyLm9yZy9jdXJyZW50L3NjYWxlX3NpemUuaHRtbCksIFtzY2FsZV9jb2xvcl9tYW51YWwoKV0oaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvY3VycmVudC9zY2FsZV9tYW51YWwuaHRtbCksIFt4bGFiKCksIHlsYWIoKSwgZ2d0aXRsZSgpXShodHRwOi8vZG9jcy5nZ3Bsb3QyLm9yZy9jdXJyZW50L2xhYnMuaHRtbCkgYWRkIG5vIG5ldyBsYXllciB0byB0aGUgZ3JhcGgsIHRoZXkgYXJlIFNDQUxFUyBmdW5jdGlvbnMsIHRoZXkgY29udHJvbCB0aGUgbWFwcGluZyBiZXR3ZWVuIGRhdGEgYW5kIGFlc3RoZXRpY3MsIGluIG90aGVyIHdvcmRzLCB0aGV5IG1vZGlmeSBleGlzdGluZyBsYXllcnMuIEJ1dCB3aGljaCBsYXllciBkbyB0aGV5IG1vZGlmeT8gSW4gdGhpcyBzaXR1YXRpb24sIHRoZXkgbW9kaWZpZWQgYGxheWVyMmAgKGkuZS4sIHRoZSBgZ2VvbV9wYXRoKClgIGxheWVyKS4gICANCg0KVGhlIGZ1bmN0aW9uIGBzY2FsZV9zaXplKClgIGlzIHZlcnkgdXNlZnVsIGluIHRoaXMgY2FzZSB0byBhcHByb3BlaWF0ZWx5IGRpc3BsYXkgbnVtZXJpYyB2YWx1ZXMgaW4gdGhlIGBTdXJ2aXZvcnNgIGxlZ2VuZC4gVGhlIGBsYWJlbHNgIGFlc3RoZXRpYyBjYW4gdGFrZSBmdW5jdGlvbiB0aGF0IHRha2VzIHRoZSBgYnJlYWtzYCAqdmFsdWUqIGFzIGlucHV0IGFuZCByZXR1cm5zIGxhYmVscyBhcyBvdXRwdXQsIGZvciBleGFtcGxlLCBoZXJlIHRoZSBgY29tbWEoKWAgZnVuY3Rpb24gdGFrZXMgdGhlIGBicmVha3NgICp2YWx1ZSogYXMgaW5wdXQgYW5kIGNoYW5nZXMgYSBsYWJlbCBmcm9tIGAxZSswNWAgdG8gYDEwMCwwMDBgLiANCg0KWW91IG1pZ2h0IGJlIHRlbXB0ZWQgdG8gdGhpbmsgdGhhdCBgY29tbWEoYnJlYWtzKWAgYWxzbyB3b3JrcyBidXQgaXQgZG9lc24ndCAoSSB0cmllZCkuIERvbid0IGxldCB0aGUgYD1gIGZvb2xzIHlvdSwgdW5saWtlIGB2YCwgYGJyZWFrc2AgaXMgdGhlIG5hbWUgb2YgYW4gYWVzdGhldGljLCBub3QgYSB2YXJpYWJsZSBpbiB0aGlzIGNhc2UuIFRodXMsIHlvdSBoYXZlIHRvIHBhc3MgaW4gdGhlIHNhbWUgdmVjdG9yIGFzc2lnbmVkIHRvIGBicmVha3NgIGludG8gYGNvbW1hKClgLCBoZXJlIHRoZSB2ZWN0b3IgaXMgYGMoMSwgMiwgMykgKiAxMF41YC4gTm93IEkgaG9wZSB5b3UgcmVhbGl6ZSB3aHksIGluIFIsIGA9YCBhbmQgYDwtYCBzaG91bGQgKipub3QqKiBiZSB1c2VkIGludGVyY2hhbmdlYWJseS4gICANCg0KYGBge3J9DQp2IDwtIGMoMSwgMiwgMykgKiAxMF41DQpmaW5hbEdyYXBoIDwtIGxheWVyMyArIHNjYWxlX3NpemUoIlN1cnZpdm9ycyIsIHJhbmdlID0gYygxLCAxMCksIGJyZWFrcyA9IHYsIGxhYmVscyA9IGNvbW1hKHYpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgiRGlyZWN0aW9uIiwgdmFsdWVzID0gYygiZ3JleTUwIiwgInJlZCIpKSArDQogIHhsYWIoIkxvbmdpdHVkZSIpICsgeWxhYigiTGF0aXR1ZGUiKSArIGdndGl0bGUoIk5hcG9sZW9uJ3MgbWFyY2ggdG8gUnVzc2lhIikNCmZpbmFsR3JhcGgNCmBgYA0KDQpUaGF0J3MgaXQsIHRoYW5rcyBmb3IgcmVhZGluZyENCg==