How to float ggplot2’s geom_label()

November 15, 2018 - 5 minutes
data viz ggplot2

Adding labels to a ggplot can be a nice way to display summary statistics and complement a visualization. But positioning these can be annoying.

This post is about how I take advantage of ggplot2’s positioning of inifinity, to make labels always “float” at the edge of plots. This is similar to the concept of floating elements in web design.

Doing it with a demo

library(tidyverse)

group_stats <- iris %>%
  group_by(Species) %>%
  summarise(lab = mean(Sepal.Length))

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_point(alpha = .25) +
  geom_label(data = group_stats, aes(label = lab, y = Inf), vjust = 1)

The secret sauce is pairing x/y = Inf and vjust = 1.

You can use -Inf the same way with vjust = 0.

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_point(alpha = .25) +
  geom_label(data = group_stats, aes(label = lab, y = -Inf), vjust = 0)

If you use a value outside of 0:1, you can continue moving the text further away from the edges, but these are the combinations to keep the labels always touching the edge.

If you accidentally use the wrong direction of vjust, you can actually push your labels beyond the edges of the plotting area, causing them to disappear. Notice no warning is raised.

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_point(alpha = .25) +
  geom_label(data = group_stats, aes(label = lab, y = Inf), vjust = -1)

Preventing labels from hiding the data

In the second plot, where I used -Inf, my labels actually covered up the lowest data point for setosa. This can become a bigger problem in facetted plots when the labels consume more of the plotting area and the panelled variables cover different ranges.

Using all four measured variables of iris for a demonstration.

iris_long <- gather(iris, "key", "value", - Species)

group_stats2 <- iris_long %>%
  group_by(Species, key) %>%
  summarise(lab = mean(value))

ggplot(iris_long, aes(x = Species, y = value)) +
  geom_point(alpha = .25) +
  geom_label(data = group_stats2, aes(label = lab, y = -Inf), vjust = 0) +
  facet_wrap(~key, scales = "free_y")

To prevent this I adjust ggplot’s default axis expansion margins.

ggplot(iris_long, aes(x = Species, y = value)) +
  geom_point(alpha = .25) +
  geom_label(data = group_stats2, aes(label = lab, y = -Inf), vjust = 0) +
  scale_y_continuous(expand = c(.25, .05)) + # default is c(.05, .05)
  facet_wrap(~key, scales = "free_y")

The vector being passed to expand = extends the axis limits by 20% from the lowest value and 5% from the uppermost. For continuous variables the default is to expand 5% on each side, to ensure a no geoms are plotted too close to the edge.

And now you know how to nicely float labels in your ggplot’s! What you put in those labels us up to you. I often use labels for percentage change and/or p-values, adjusting for multiple comparrisons of course :)

devtools::session_info()
##  setting  value                       
##  version  R version 3.5.0 (2018-04-23)
##  system   x86_64, darwin15.6.0        
##  ui       X11                         
##  language (EN)                        
##  collate  en_US.UTF-8                 
##  tz       America/New_York            
##  date     2018-11-15                  
## 
##  package    * version    date       source                            
##  assertthat   0.2.0      2017-04-11 CRAN (R 3.5.0)                    
##  backports    1.1.2      2017-12-13 CRAN (R 3.5.0)                    
##  base       * 3.5.0      2018-04-24 local                             
##  bindr        0.1.1      2018-03-13 CRAN (R 3.5.0)                    
##  bindrcpp     0.2.2      2018-03-29 CRAN (R 3.5.0)                    
##  blogdown     0.6        2018-04-18 CRAN (R 3.5.0)                    
##  bookdown     0.7        2018-02-18 CRAN (R 3.5.0)                    
##  broom        0.5.0      2018-07-17 CRAN (R 3.5.0)                    
##  cellranger   1.1.0      2016-07-27 CRAN (R 3.5.0)                    
##  cli          1.0.1      2018-09-25 cran (@1.0.1)                     
##  colorspace   1.3-2      2016-12-14 CRAN (R 3.5.0)                    
##  compiler     3.5.0      2018-04-24 local                             
##  crayon       1.3.4      2018-11-02 Github (r-lib/crayon@0398b12)     
##  datasets   * 3.5.0      2018-04-24 local                             
##  devtools     1.13.6     2018-06-27 CRAN (R 3.5.0)                    
##  digest       0.6.18     2018-10-10 cran (@0.6.18)                    
##  dplyr      * 0.7.8      2018-11-10 CRAN (R 3.5.0)                    
##  evaluate     0.12       2018-10-09 cran (@0.12)                      
##  forcats    * 0.3.0      2018-02-19 CRAN (R 3.5.0)                    
##  ggplot2    * 3.1.0.9000 2018-11-05 Github (tidyverse/ggplot2@15ddc22)
##  glue         1.3.0      2018-07-17 cran (@1.3.0)                     
##  graphics   * 3.5.0      2018-04-24 local                             
##  grDevices  * 3.5.0      2018-04-24 local                             
##  grid         3.5.0      2018-04-24 local                             
##  gtable       0.2.0      2016-02-26 CRAN (R 3.5.0)                    
##  haven        1.1.2      2018-06-27 cran (@1.1.2)                     
##  hms          0.4.2      2018-03-10 CRAN (R 3.5.0)                    
##  htmltools    0.3.6      2017-04-28 CRAN (R 3.5.0)                    
##  httr         1.3.1      2017-08-20 CRAN (R 3.5.0)                    
##  jsonlite     1.5        2017-06-01 CRAN (R 3.5.0)                    
##  knitr        1.20       2018-02-20 CRAN (R 3.5.0)                    
##  labeling     0.3        2014-08-23 CRAN (R 3.5.0)                    
##  lattice      0.20-35    2017-03-25 CRAN (R 3.5.0)                    
##  lazyeval     0.2.1      2017-10-29 CRAN (R 3.5.0)                    
##  lubridate    1.7.4      2018-04-11 CRAN (R 3.5.0)                    
##  magrittr     1.5        2014-11-22 CRAN (R 3.5.0)                    
##  memoise      1.1.0      2017-04-21 CRAN (R 3.5.0)                    
##  methods    * 3.5.0      2018-04-24 local                             
##  modelr       0.1.1      2017-07-24 CRAN (R 3.5.0)                    
##  munsell      0.5.0      2018-06-12 cran (@0.5.0)                     
##  nlme         3.1-137    2018-04-07 CRAN (R 3.5.0)                    
##  pillar       1.3.0.9000 2018-08-18 Github (r-lib/pillar@3fabb4e)     
##  pkgconfig    2.0.2      2018-08-16 cran (@2.0.2)                     
##  plyr         1.8.4      2016-06-08 CRAN (R 3.5.0)                    
##  purrr      * 0.2.5      2018-05-29 cran (@0.2.5)                     
##  R6           2.3.0      2018-10-04 cran (@2.3.0)                     
##  Rcpp         1.0.0      2018-11-07 cran (@1.0.0)                     
##  readr      * 1.1.1      2017-05-16 CRAN (R 3.5.0)                    
##  readxl       1.1.0      2018-04-20 CRAN (R 3.5.0)                    
##  rlang        0.3.0.1    2018-10-25 cran (@0.3.0.1)                   
##  rmarkdown    1.10       2018-06-11 CRAN (R 3.5.0)                    
##  rprojroot    1.3-2      2018-01-03 CRAN (R 3.5.0)                    
##  rstudioapi   0.8        2018-10-02 cran (@0.8)                       
##  rvest        0.3.2      2016-06-17 CRAN (R 3.5.0)                    
##  scales       1.0.0      2018-08-09 cran (@1.0.0)                     
##  stats      * 3.5.0      2018-04-24 local                             
##  stringi      1.2.4      2018-07-20 cran (@1.2.4)                     
##  stringr    * 1.3.1      2018-05-10 CRAN (R 3.5.0)                    
##  tibble     * 1.4.2      2018-01-22 CRAN (R 3.5.0)                    
##  tidyr      * 0.8.2      2018-10-28 cran (@0.8.2)                     
##  tidyselect   0.2.5      2018-10-11 cran (@0.2.5)                     
##  tidyverse  * 1.2.1      2017-11-14 CRAN (R 3.5.0)                    
##  tools        3.5.0      2018-04-24 local                             
##  utils      * 3.5.0      2018-04-24 local                             
##  withr        2.1.2.9000 2018-10-18 Github (jimhester/withr@be57595)  
##  xfun         0.2        2018-06-16 cran (@0.2)                       
##  xml2         1.2.0      2018-01-24 CRAN (R 3.5.0)                    
##  yaml         2.1.19     2018-05-01 cran (@2.1.19)

Maps with the new ggplot2 v3.0.0

August 4, 2018 - 2 minutes
Civic Data ggplot2 tidyverse

gg_interaction_plot

April 30, 2018 - 4 minutes
Friendly syntax for a investigating interaction in models.
Modeling ggplot2