Visualising data is useful not only to explore the data and identify the relationship between different variables, but also to communicate the result of the analysis. The ggplot2 package allows us you to generate high quality plots in just a few lines of code. Any ggplot plot will have at least 3 components: the data, a coordinate system and a geometry (the visual representation of the data) and will be built in layers.

Let’s start by making plots!

First layer: the area of the graphic

The main function of ggplot2 is precisely ggplot(), which allows us to start the graph and also to define the global characteristics. The first argument of this function will be the data we want to visualize, always in a data.frame. In this case we use penguins.

The second argument is called mapping because it’s where we define how columns of the data “map” to visual properties of the geometries. This mapping is defined by the aes() function. In this case we indicate that in the x axis we want to plot the variable bill_length_mm and in the y-axis the variable bill_depth_mm.

But this alone is not enough, it only generates the first layer: the area of the graph.

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) 

Second layer: geometries

We need to add a new layer to our chart, the geometric elements or “geoms” that will represent the data. To do this we add a geom function, for example if we want to represent the data with points we will use geom_point().

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point()

We have our first plot!

You may have noticed that the dots are clustered in groups. Perhaps some other variable explains this behaviour.

To include information from other variables in our plot we can take advantage of the aesthetic characteristics of the geometries. In this case, we can “paint” the points according to the penguin species.

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point(aes(color = species))

Again, we use the aes() function to map a variable in our data to an element of the plot. And aha! Each species of penguins has different characteristics!

Adding geometries

Very often it is not enough to look at the raw data to identify the relationship between variables; it is necessary to use some statistical transformation to highlight those relationships, either by fitting a model or calculating some statistics.

For this, ggplot2 has geoms that calculate some common statistical transformations. Let’s try with geom_smoth() to fit a linear model to each species.

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point(aes(color = species)) +
  geom_smooth(aes(color = species), method = "lm")

By default geom_smooth() fits the data using the loess method (local linear regression) when there are less than 1000 data. But it is very common that you want to fit a global linear regression. In that case, you have to set method = "lm".

Let’s talk about the look of the plot

For now we used the default ggplot look. We could change the look of your plot to match the style of the institution where we work, of the journal where we are going to publish it or simply to make it more eye-catching.

Let’s start with colour. To change the aesthetic appearance of a plot element, we add a new layer with the scale_* function. In this case we’ll use scale_color_manual() to choose the colours of the points manually. We could also use previously defined colour palettes like Viridis or Color Brewer.

We’ll need 3 colors for the 3 species, let’s use "darkorange", "purple" and "cyan4" following the beautiful visualizations made by Allison Horst.

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point(aes(color = species)) +
  geom_smooth(aes(color = species), method = "lm") +
  scale_color_manual(values = c("darkorange","purple","cyan4")) 

We are getting there! Now, let’s add some text elements with a new ggplot layer: labs().

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point(aes(color = species)) +
  geom_smooth(aes(color = species), method = "lm") +
  scale_color_manual(values = c("darkorange","purple","cyan4")) +
  labs(title = "Penguin bill dimensions",
       subtitle = "Bill length and depth for Adelie, Chinstrap and Gentoo,  Penguins at Palmer Station LTER",
       x = "Bill length (mm)",
       y = "Bill depth (mm)",
       color = "Penguin species",
       shape = "Penguin species") 

Now the axes labels are more legible and we have a title and subtitle that explains what the plot is about.

We could keep changing this endlessly but we’ll finish with the general look of your plot.

The overall look of a plot is defined by its theme. ggplot2 has many themes available and for all tastes. But there are also other packages that extend the possibilities, for example ggthemes. By default ggplot2 uses theme_grey(), let’s try theme_minimal():

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point(aes(color = species)) +
  geom_smooth(aes(color = species), method = "lm") +
  scale_color_manual(values = c("darkorange","purple","cyan4")) +
  labs(title = "Penguin bill dimensions",
       subtitle = "Bill length and depth for Adelie, Chinstrap and Gentoo,  Penguins at Palmer Station LTER",
       x = "Bill length (mm)",
       y = "Bill depth (mm)",
       color = "Penguin species",
       shape = "Penguin species") +
  theme_minimal()

Now it’s your turn. Choose a theme you like and try it out. Also, if you can think of a better title, modify it!

LS0tCnRpdGxlOiAiUGxvdHRpbmcgRGF0YSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IGZhbHNlCiAgICBoaWdobGlnaHQ6IHRhbmdvCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKCWVjaG8gPSBUUlVFLAoJbWVzc2FnZSA9IEZBTFNFLAoJd2FybmluZyA9IEZBTFNFCikKbGlicmFyeSh0aWR5dmVyc2UpCgpwZW5ndWlucyA8LSByZWFkX2NzdigiZGF0YS9wZW5ndWlucy5jc3YiKQpgYGAKClZpc3VhbGlzaW5nIGRhdGEgaXMgdXNlZnVsIG5vdCBvbmx5IHRvIGV4cGxvcmUgdGhlIGRhdGEgYW5kIGlkZW50aWZ5IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkaWZmZXJlbnQgdmFyaWFibGVzLCBidXQgYWxzbyB0byBjb21tdW5pY2F0ZSB0aGUgcmVzdWx0IG9mIHRoZSBhbmFseXNpcy4gVGhlICoqZ2dwbG90MioqIHBhY2thZ2UgYWxsb3dzIHVzIHlvdSB0byBnZW5lcmF0ZSBoaWdoIHF1YWxpdHkgcGxvdHMgaW4ganVzdCBhIGZldyBsaW5lcyBvZiBjb2RlLiBBbnkgZ2dwbG90IHBsb3Qgd2lsbCBoYXZlIGF0IGxlYXN0IDMgY29tcG9uZW50czogdGhlICoqZGF0YSoqLCBhICoqY29vcmRpbmF0ZSBzeXN0ZW0qKiBhbmQgYSAqKmdlb21ldHJ5KiogKHRoZSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRhdGEpIGFuZCB3aWxsIGJlIGJ1aWx0IGluIGxheWVycy4KCkxldCdzIHN0YXJ0IGJ5IG1ha2luZyBwbG90cyEKCiMjIEZpcnN0IGxheWVyOiB0aGUgYXJlYSBvZiB0aGUgZ3JhcGhpYwoKVGhlIG1haW4gZnVuY3Rpb24gb2YgZ2dwbG90MiBpcyBwcmVjaXNlbHkgYGdncGxvdCgpYCwgd2hpY2ggYWxsb3dzIHVzIHRvIHN0YXJ0IHRoZSBncmFwaCBhbmQgYWxzbyB0byBkZWZpbmUgdGhlIGdsb2JhbCBjaGFyYWN0ZXJpc3RpY3MuIFRoZSBmaXJzdCBhcmd1bWVudCBvZiB0aGlzIGZ1bmN0aW9uIHdpbGwgYmUgdGhlIGRhdGEgd2Ugd2FudCB0byB2aXN1YWxpemUsIGFsd2F5cyBpbiBhIGRhdGEuZnJhbWUuIEluIHRoaXMgY2FzZSB3ZSB1c2UgYHBlbmd1aW5zYC4KClRoZSBzZWNvbmQgYXJndW1lbnQgaXMgY2FsbGVkIGBtYXBwaW5nYCBiZWNhdXNlIGl0J3Mgd2hlcmUgd2UgZGVmaW5lIGhvdyBjb2x1bW5zIG9mIHRoZSBkYXRhICJtYXAiIHRvIHZpc3VhbCBwcm9wZXJ0aWVzIG9mIHRoZSBnZW9tZXRyaWVzLiBUaGlzIG1hcHBpbmcgaXMgZGVmaW5lZCBieSB0aGUgYGFlcygpYCBmdW5jdGlvbi4gSW4gdGhpcyBjYXNlIHdlIGluZGljYXRlIHRoYXQgaW4gdGhlIHggYXhpcyB3ZSB3YW50IHRvIHBsb3QgdGhlIHZhcmlhYmxlIGBiaWxsX2xlbmd0aF9tbWAgYW5kIGluIHRoZSB5LWF4aXMgdGhlIHZhcmlhYmxlIGBiaWxsX2RlcHRoX21tYC4KCkJ1dCB0aGlzIGFsb25lIGlzIG5vdCBlbm91Z2gsIGl0IG9ubHkgZ2VuZXJhdGVzIHRoZSBmaXJzdCBsYXllcjogdGhlIGFyZWEgb2YgdGhlIGdyYXBoLgoKYGBge3J9CmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYmlsbF9kZXB0aF9tbSkpIApgYGAKCiMjIFNlY29uZCBsYXllcjogZ2VvbWV0cmllcwoKV2UgbmVlZCB0byBhZGQgYSBuZXcgbGF5ZXIgdG8gb3VyIGNoYXJ0LCB0aGUgZ2VvbWV0cmljIGVsZW1lbnRzIG9yICJnZW9tcyIgdGhhdCB3aWxsIHJlcHJlc2VudCB0aGUgZGF0YS4gVG8gZG8gdGhpcyB3ZSBhZGQgYSBnZW9tIGZ1bmN0aW9uLCBmb3IgZXhhbXBsZSBpZiB3ZSB3YW50IHRvIHJlcHJlc2VudCB0aGUgZGF0YSB3aXRoIHBvaW50cyB3ZSB3aWxsIHVzZSBgZ2VvbV9wb2ludCgpYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0pKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKV2UgaGF2ZSBvdXIgZmlyc3QgcGxvdCEgCgpZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IHRoZSBkb3RzIGFyZSBjbHVzdGVyZWQgaW4gZ3JvdXBzLiBQZXJoYXBzIHNvbWUgb3RoZXIgdmFyaWFibGUgZXhwbGFpbnMgdGhpcyBiZWhhdmlvdXIuIAoKVG8gaW5jbHVkZSBpbmZvcm1hdGlvbiBmcm9tIG90aGVyIHZhcmlhYmxlcyBpbiBvdXIgcGxvdCB3ZSBjYW4gdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIGFlc3RoZXRpYyBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIGdlb21ldHJpZXMuIEluIHRoaXMgY2FzZSwgd2UgY2FuICJwYWludCIgdGhlIHBvaW50cyBhY2NvcmRpbmcgdG8gdGhlIHBlbmd1aW4gc3BlY2llcy4gCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gc3BlY2llcykpCmBgYAoKQWdhaW4sIHdlIHVzZSB0aGUgYGFlcygpYCBmdW5jdGlvbiB0byBtYXAgYSB2YXJpYWJsZSBpbiBvdXIgZGF0YSB0byBhbiBlbGVtZW50IG9mIHRoZSBwbG90LiBBbmQgYWhhISBFYWNoIHNwZWNpZXMgb2YgcGVuZ3VpbnMgaGFzIGRpZmZlcmVudCBjaGFyYWN0ZXJpc3RpY3MhCgojIyBBZGRpbmcgZ2VvbWV0cmllcwoKVmVyeSBvZnRlbiBpdCBpcyBub3QgZW5vdWdoIHRvIGxvb2sgYXQgdGhlIHJhdyBkYXRhIHRvIGlkZW50aWZ5IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXM7IGl0IGlzIG5lY2Vzc2FyeSB0byB1c2Ugc29tZSBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbiB0byBoaWdobGlnaHQgdGhvc2UgcmVsYXRpb25zaGlwcywgZWl0aGVyIGJ5IGZpdHRpbmcgYSBtb2RlbCBvciBjYWxjdWxhdGluZyBzb21lIHN0YXRpc3RpY3MuIAoKRm9yIHRoaXMsIGdncGxvdDIgaGFzIGdlb21zIHRoYXQgY2FsY3VsYXRlIHNvbWUgY29tbW9uIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9ucy4gTGV0J3MgdHJ5IHdpdGggYGdlb21fc21vdGgoKWAgdG8gZml0IGEgbGluZWFyIG1vZGVsIHRvIGVhY2ggc3BlY2llcy4gCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gc3BlY2llcykpICsKICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBzcGVjaWVzKSwgbWV0aG9kID0gImxtIikKYGBgCgpCeSBkZWZhdWx0IGBnZW9tX3Ntb290aCgpYCBmaXRzIHRoZSBkYXRhIHVzaW5nIHRoZSBsb2VzcyBtZXRob2QgKGxvY2FsIGxpbmVhciByZWdyZXNzaW9uKSB3aGVuIHRoZXJlIGFyZSBsZXNzIHRoYW4gMTAwMCBkYXRhLiBCdXQgaXQgaXMgdmVyeSBjb21tb24gdGhhdCB5b3Ugd2FudCB0byBmaXQgYSBnbG9iYWwgbGluZWFyIHJlZ3Jlc3Npb24uIEluIHRoYXQgY2FzZSwgeW91IGhhdmUgdG8gc2V0IGBtZXRob2QgPSAibG0iYC4KCiMjIExldCdzIHRhbGsgYWJvdXQgdGhlIGxvb2sgb2YgdGhlIHBsb3QgIAoKRm9yIG5vdyB3ZSB1c2VkIHRoZSBkZWZhdWx0IGdncGxvdCBsb29rLiBXZSBjb3VsZCBjaGFuZ2UgdGhlIGxvb2sgb2YgeW91ciBwbG90IHRvIG1hdGNoIHRoZSBzdHlsZSBvZiB0aGUgaW5zdGl0dXRpb24gd2hlcmUgd2Ugd29yaywgb2YgdGhlIGpvdXJuYWwgd2hlcmUgd2UgYXJlIGdvaW5nIHRvIHB1Ymxpc2ggaXQgb3Igc2ltcGx5IHRvIG1ha2UgaXQgbW9yZSBleWUtY2F0Y2hpbmcuIAoKTGV0J3Mgc3RhcnQgd2l0aCBjb2xvdXIuIFRvIGNoYW5nZSB0aGUgYWVzdGhldGljIGFwcGVhcmFuY2Ugb2YgYSBwbG90IGVsZW1lbnQsIHdlIGFkZCBhIG5ldyBsYXllciB3aXRoIHRoZSBgc2NhbGVfKmAgZnVuY3Rpb24uIEluIHRoaXMgY2FzZSB3ZSdsbCB1c2UgYHNjYWxlX2NvbG9yX21hbnVhbCgpYCB0byBjaG9vc2UgdGhlIGNvbG91cnMgb2YgdGhlIHBvaW50cyBtYW51YWxseS4gV2UgY291bGQgYWxzbyB1c2UgcHJldmlvdXNseSBkZWZpbmVkIGNvbG91ciBwYWxldHRlcyBsaWtlIFZpcmlkaXMgb3IgQ29sb3IgQnJld2VyLiAKCldlJ2xsIG5lZWQgMyBjb2xvcnMgZm9yIHRoZSAzIHNwZWNpZXMsIGxldCdzIHVzZSBgImRhcmtvcmFuZ2UiYCwgYCJwdXJwbGUiYCBhbmQgYCJjeWFuNCJgIGZvbGxvd2luZyB0aGUgYmVhdXRpZnVsIHZpc3VhbGl6YXRpb25zIG1hZGUgYnkgQWxsaXNvbiBIb3JzdC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0pKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzcGVjaWVzKSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IHNwZWNpZXMpLCBtZXRob2QgPSAibG0iKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtvcmFuZ2UiLCJwdXJwbGUiLCJjeWFuNCIpKSAKYGBgCgpXZSBhcmUgZ2V0dGluZyB0aGVyZSEgTm93LCBsZXQncyBhZGQgc29tZSB0ZXh0IGVsZW1lbnRzIHdpdGggYSBuZXcgZ2dwbG90IGxheWVyOiBgbGFicygpYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0pKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzcGVjaWVzKSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IHNwZWNpZXMpLCBtZXRob2QgPSAibG0iKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtvcmFuZ2UiLCJwdXJwbGUiLCJjeWFuNCIpKSArCiAgbGFicyh0aXRsZSA9ICJQZW5ndWluIGJpbGwgZGltZW5zaW9ucyIsCiAgICAgICBzdWJ0aXRsZSA9ICJCaWxsIGxlbmd0aCBhbmQgZGVwdGggZm9yIEFkZWxpZSwgQ2hpbnN0cmFwIGFuZCBHZW50b28sICBQZW5ndWlucyBhdCBQYWxtZXIgU3RhdGlvbiBMVEVSIiwKICAgICAgIHggPSAiQmlsbCBsZW5ndGggKG1tKSIsCiAgICAgICB5ID0gIkJpbGwgZGVwdGggKG1tKSIsCiAgICAgICBjb2xvciA9ICJQZW5ndWluIHNwZWNpZXMiLAogICAgICAgc2hhcGUgPSAiUGVuZ3VpbiBzcGVjaWVzIikgCmBgYAoKTm93IHRoZSBheGVzIGxhYmVscyBhcmUgbW9yZSBsZWdpYmxlIGFuZCB3ZSBoYXZlIGEgdGl0bGUgYW5kIHN1YnRpdGxlIHRoYXQgZXhwbGFpbnMgd2hhdCB0aGUgcGxvdCBpcyBhYm91dC4gCgpXZSBjb3VsZCBrZWVwIGNoYW5naW5nIHRoaXMgZW5kbGVzc2x5IGJ1dCB3ZSdsbCBmaW5pc2ggd2l0aCB0aGUgZ2VuZXJhbCBsb29rIG9mIHlvdXIgcGxvdC4gCgpUaGUgb3ZlcmFsbCBsb29rIG9mIGEgcGxvdCBpcyBkZWZpbmVkIGJ5IGl0cyB0aGVtZS4gZ2dwbG90MiBoYXMgbWFueSB0aGVtZXMgYXZhaWxhYmxlIGFuZCBmb3IgYWxsIHRhc3Rlcy4gQnV0IHRoZXJlIGFyZSBhbHNvIG90aGVyIHBhY2thZ2VzIHRoYXQgZXh0ZW5kIHRoZSBwb3NzaWJpbGl0aWVzLCBmb3IgZXhhbXBsZSBnZ3RoZW1lcy4gQnkgZGVmYXVsdCBnZ3Bsb3QyIHVzZXMgYHRoZW1lX2dyZXkoKWAsIGxldCdzIHRyeSBgdGhlbWVfbWluaW1hbCgpYDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0pKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzcGVjaWVzKSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IHNwZWNpZXMpLCBtZXRob2QgPSAibG0iKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtvcmFuZ2UiLCJwdXJwbGUiLCJjeWFuNCIpKSArCiAgbGFicyh0aXRsZSA9ICJQZW5ndWluIGJpbGwgZGltZW5zaW9ucyIsCiAgICAgICBzdWJ0aXRsZSA9ICJCaWxsIGxlbmd0aCBhbmQgZGVwdGggZm9yIEFkZWxpZSwgQ2hpbnN0cmFwIGFuZCBHZW50b28sICBQZW5ndWlucyBhdCBQYWxtZXIgU3RhdGlvbiBMVEVSIiwKICAgICAgIHggPSAiQmlsbCBsZW5ndGggKG1tKSIsCiAgICAgICB5ID0gIkJpbGwgZGVwdGggKG1tKSIsCiAgICAgICBjb2xvciA9ICJQZW5ndWluIHNwZWNpZXMiLAogICAgICAgc2hhcGUgPSAiUGVuZ3VpbiBzcGVjaWVzIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCj4gTm93IGl0J3MgeW91ciB0dXJuLiBDaG9vc2UgYSB0aGVtZSB5b3UgbGlrZSBhbmQgdHJ5IGl0IG91dC4gQWxzbywgaWYgeW91IGNhbiB0aGluayBvZiBhIGJldHRlciB0aXRsZSwgbW9kaWZ5IGl0IQo=