Permalink
47b7a1f Jun 11, 2018
3 contributors

Users who have contributed to this file

@slowkow @AliciaSchep @seaaan
584 lines (442 sloc) 15.9 KB
title author date output vignette editor_options
ggrepel examples
Kamil Slowikowski
2018-06-11
prettydoc::html_pretty
theme highlight toc mathjax self_contained
hpstr
github
true
true
%\VignetteIndexEntry{ggrepel examples} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown}
chunk_output_type
console

Overview

ggrepel provides geoms for ggplot2 to repel overlapping text labels:

  • geom_text_repel()
  • geom_label_repel()

Text labels repel away from each other, away from data points, and away from edges of the plotting area.

Let's compare geom_text() and geom_text_repel():

library(ggrepel)
set.seed(42)

dat <- subset(mtcars, wt > 2.75 & wt < 3.45)
dat$car <- rownames(dat)

p <- ggplot(dat, aes(wt, mpg, label = car)) +
  geom_point(color = "red")

p1 <- p + geom_text() + labs(title = "geom_text()")

p2 <- p + geom_text_repel() + labs(title = "geom_text_repel()")

gridExtra::grid.arrange(p1, p2, ncol = 2)

plot of chunk comparison

Installation

ggrepel version 0.8.0.9000 is available on CRAN:

install.packages("ggrepel")

The latest development version may have new features, and you can get it from GitHub:

# Use the devtools package
# install.packages("devtools")
devtools::install_github("slowkow/ggrepel")

Options

Options available for geom_text() and geom_label() are also available for geom_text_repel() and geom_label_repel(), including size, angle, family, fontface, etc.

ggrepel provides additional options for geom_text_repel and geom_label_repel:

Option Default Description
force 1 force of repulsion between overlapping text labels
direction "both" move text labels "both" (default), "x", or "y" directions
max.iter 2000 maximum number of iterations to try to resolve overlaps
nudge_x 0 adjust the starting x position of the text label
nudge_y 0 adjust the starting y position of the text label
box.padding 0.25 lines padding around the text label
point.padding 0 lines padding around the labeled data point
segment.color "black" line segment color
segment.size 0.5 mm line segment thickness
segment.alpha 1.0 line segment transparency
arrow NULL render line segment as an arrow with grid::arrow()

Examples

Hide some of the labels

Set labels to the empty string "" to hide them. All data points repel the non-empty labels.

set.seed(42)

dat2 <- subset(mtcars, wt > 3 & wt < 4)
# Hide all of the text labels.
dat2$car <- ""
# Let's just label these items.
ix_label <- c(2,3,14)
dat2$car[ix_label] <- rownames(dat2)[ix_label]

ggplot(dat2, aes(wt, mpg, label = car)) +
  geom_text_repel() +
  geom_point(color = ifelse(dat2$car == "", "grey50", "red"))

plot of chunk empty_string

Thanks to the AABB.cc library by Lester Hedges, we can quickly repel a few text labels from many thousands of data points.

set.seed(42)

dat3 <- rbind(
  data.frame(
    wt  = rnorm(n = 10000, mean = 3),
    mpg = rnorm(n = 10000, mean = 19),
    car = ""
  ),
  dat2[,c("wt", "mpg", "car")]
)

ggplot(dat3, aes(wt, mpg, label = car)) +
  geom_point(data = dat3[dat3$car == "",], color = "grey50") +
  geom_text_repel(box.padding = 0.5) +
  geom_point(data = dat3[dat3$car != "",], color = "red")

plot of chunk empty_string_big

Do not repel labels from data points

Set point.padding = NA to prevent label repulsion away from data points.

Now labels move away from each other and away from the edges of the plot.

set.seed(42)
ggplot(dat, aes(wt, mpg, label = car)) +
  geom_point(color = "red") +
  geom_text_repel(point.padding = NA)

plot of chunk point_padding_na

Limit labels to a specific area

Use options xlim and ylim to constrain the labels to a specific area. Limits are specified in data coordinates. Use NA when there is no lower or upper bound in a particular direction.

Here we also use grid::arrow() to render the segments as arrows.

set.seed(42)

# All labels should be to the right of 3.
x_limits <- c(3, NA)

ggplot(dat, aes(wt, mpg, label = car, color = factor(cyl))) +
  geom_vline(xintercept = x_limits, linetype = 3) +
  geom_point() +
  geom_label_repel(
    arrow = arrow(length = unit(0.03, "npc"), type = "closed", ends = "first"),
    force = 5,
    xlim  = x_limits
  ) +
  scale_color_discrete(name = "cyl")

plot of chunk xlim

Align labels on the top or bottom edge

Use hjust or vjust to justify the text neatly:

  • hjust = 0 for left-align
  • hjust = 0.5 for center
  • hjust = 1 for right-align

Sometimes the labels do not align perfectly. Try using direction = "x" to limit label movement to the x-axis (left and right) or direction = "y" to limit movement to the y-axis (up and down). The default is direction = "both".

Also try using xlim() and ylim() to increase the size of the plotting area so all of the labels fit comfortably.

set.seed(42)

ggplot(mtcars, aes(x = wt, y = 1, label = rownames(mtcars))) +
  geom_point(color = "red") +
  geom_text_repel(
    force        = 0.5,
    nudge_y      = 0.05,
    direction    = "x",
    angle        = 90,
    vjust        = 0,
    segment.size = 0.2
  ) +
  xlim(1, 6) +
  ylim(1, 0.8) +
  theme(
    axis.line.y  = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.y  = element_blank(),
    axis.title.y = element_blank()
  )

plot of chunk direction_x

Align text vertically with nudge_y and allow the labels to move horizontally with direction = "x":

set.seed(42)

dat <- mtcars
dat$car <- rownames(dat)

ggplot(dat, aes(qsec, mpg, label = car)) +
  geom_text_repel(
    data          = subset(dat, mpg > 30),
    nudge_y       = 36 - subset(dat, mpg > 30)$mpg,
    segment.size  = 0.2,
    segment.color = "grey50",
    direction     = "x"
  ) +
  geom_point(color = ifelse(dat$mpg > 30, "red", "black")) +
  scale_x_continuous(expand = c(0.05, 0.05)) +
  scale_y_continuous(limits = c(NA, 36))

plot of chunk neat-offset-x

Align labels on the left or right edge

Set direction to "y" and try hjust 0.5, 0, and 1:

set.seed(42)

p <- ggplot(mtcars, aes(y = wt, x = 1, label = rownames(mtcars))) +
  geom_point(color = "red") +
  ylim(1, 5.5) +
  theme(
    axis.line.x  = element_blank(),
    axis.ticks.x = element_blank(),
    axis.text.x  = element_blank(),
    axis.title.x = element_blank()
  )

p1 <- p +
  xlim(1, 1.375) +
  geom_text_repel(
    force        = 0.5,
    nudge_x      = 0.15,
    direction    = "y",
    hjust        = 0,
    segment.size = 0.2
  ) +
  ggtitle("hjust = 0")

p2 <- p + 
  xlim(1, 1.375) +
  geom_text_repel(
    force        = 0.5,
    nudge_x      = 0.2,
    direction    = "y",
    hjust        = 0.5,
    segment.size = 0.2
  ) +
  ggtitle("hjust = 0.5 (default)")

p3 <- p +
  xlim(0.25, 1) +
  scale_y_continuous(position = "right") +
  geom_text_repel(
    force        = 0.5,
    nudge_x      = -0.25,
    direction    = "y",
    hjust        = 1,
    segment.size = 0.2
  ) +
  ggtitle("hjust = 1")

gridExtra::grid.arrange(p1, p2, p3, ncol = 3)

plot of chunk direction_y

Align text horizontally with nudge_x and hjust, and allow the labels to move vertically with direction = "y":

set.seed(42)

dat <- subset(mtcars, wt > 2.75 & wt < 3.45)
dat$car <- rownames(dat)

ggplot(dat, aes(wt, mpg, label = car)) +
  geom_text_repel(
    data          = subset(dat, wt > 3),
    nudge_x       = 3.5 - subset(dat, wt > 3)$wt,
    segment.size  = 0.2,
    segment.color = "grey50",
    direction     = "y",
    hjust         = 0
  ) +
  geom_text_repel(
    data          = subset(dat, wt < 3),
    nudge_x       = 2.7 - subset(dat, wt < 3)$wt,
    segment.size  = 0.2,
    segment.color = "grey50",
    direction     = "y",
    hjust         = 1
  ) +
  scale_x_continuous(
    breaks = c(2.5, 2.75, 3, 3.25, 3.5),
    limits = c(2.4, 3.8)
  ) +
  geom_point(color = "red")

plot of chunk neat-offset-y

Label jittered points

Note: This example will not work with ggplot2 version 2.2.1 or older.

To get the latest development version of ggplot2, try:

# install.packages("devtools")
devtools::install_github("tidyverse/ggplot2")

If your ggplot2 is newer than 2.2.1, try this example:

mtcars$label <- rownames(mtcars)
mtcars$label[mtcars$cyl != 6] <- ""

# New! (not available in ggplot2 version 2.2.1 or earlier)
pos <- position_jitter(width = 0.3, seed = 2)

ggplot(mtcars, aes(factor(cyl), mpg, color = label != "", label = label)) +
  geom_point(position = pos) +
  geom_text_repel(position = pos) +
  theme(legend.position = "none") +
  labs(title = "position_jitter()")

plot of chunk jitter

You can also use other position functions, like position_quasirandom() from the ggbeeswarm package by Erik Clarke:

mtcars$label <- rownames(mtcars)
mtcars$label[mtcars$cyl != 6] <- ""

library(ggbeeswarm)
pos <- position_quasirandom()

ggplot(mtcars, aes(factor(cyl), mpg, color = label != "", label = label)) +
  geom_point(position = pos) +
  geom_text_repel(position = pos) +
  theme(legend.position = "none") +
  labs(title = "position_quasirandom()")

plot of chunk quasirandom

Word cloud

The force option controls the strength of repulsion.

The force_pull option controls the strength of the spring that pulls the text label toward its data point.

To make a word cloud, we can assign all of the text labels the same data point at the origin (0, 0) and set force_pull = 0 to disable the springs.

set.seed(42)
ggplot(mtcars) +
  geom_text_repel(
    aes(
      label  = rownames(mtcars),
      size   = mpg > 15,
      colour = factor(cyl),
      x      = 0,
      y      = 0
    ),
    force_pull    = 0, # do not pull text toward the point at (0,0)
    max.iter      = 1e4,
    segment.color = NA,
    point.padding = NA
  ) +
  theme_void() +
  theme(strip.text = element_text(size = 16)) +
  facet_wrap(~ factor(cyl)) +
  scale_color_discrete(name = "Cylinders") +
  scale_size_manual(values = c(2, 3)) +
  theme(
    strip.text   = element_blank(),
    panel.border = element_rect(size = 0.2, fill = NA)
  )

plot of chunk wordcloud

Polar coordinates

set.seed(42)

mtcars$label <- rownames(mtcars)
mtcars$label[mtcars$mpg < 25] <- ""

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), label = label)) +
  coord_polar(theta = "x") +
  geom_point(size = 2) +
  scale_color_discrete(name = "cyl") +
  geom_text_repel(show.legend = FALSE) + # Don't display "a" in the legend.
  theme_bw(base_size = 18)

plot of chunk polar

Mathematical expressions

d <- data.frame(
  x    = c(1, 2, 2, 1.75, 1.25),
  y    = c(1, 3, 1, 2.65, 1.25),
  math = c(
    NA,
    "integral(f(x) * dx, a, b)",
    NA,
    "lim(f(x), x %->% 0)",
    NA
  )
)

ggplot(d, aes(x, y, label = math)) +
  geom_point() +
  geom_label_repel(
    parse       = TRUE, # Parse mathematical expressions.
    size        = 8,
    box.padding = 2
  )

plot of chunk math

Animation

# This chunk of code will take a minute or two to run.
library(ggrepel)
library(animation)

plot_frame <- function(n) {
  set.seed(42)
  p <- ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars))) +
    geom_text_repel(
      size = 5, force = 1, max.iter = n
    ) +
    geom_point(color = "red") +
    # theme_minimal(base_size = 16) +
    labs(title = n)
  print(p)
}

xs <- ceiling(1.18^(1:52))
# xs <- ceiling(1.4^(1:26))
xs <- c(xs, rep(xs[length(xs)], 15))
# plot(xs)

saveGIF(
  lapply(xs, function(i) {
    plot_frame(i)
  }),
  interval   = 0.15,
  ani.width  = 800,
  ani.heigth = 600,
  movie.name = "animated.gif"
)

Click here to see the animation.

Source code

View the source code for this vignette on GitHub.

R Session Info

sessionInfo()
## R version 3.5.0 (2018-04-23)
## Platform: x86_64-apple-darwin17.4.0 (64-bit)
## Running under: macOS High Sierra 10.13.3
## 
## Matrix products: default
## BLAS/LAPACK: /usr/local/Cellar/openblas/0.3.0/lib/libopenblasp-r0.3.0.dev.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggbeeswarm_0.6.0   ggrepel_0.8.0.9000 ggplot2_2.2.1.9000
## [4] gridExtra_2.3      knitr_1.20        
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_0.12.17      bindr_0.1.1       magrittr_1.5     
##  [4] tidyselect_0.2.4  munsell_0.4.3     colorspace_1.3-2 
##  [7] R6_2.2.2          rlang_0.2.1       vipor_0.4.5      
## [10] highr_0.6         stringr_1.3.1     plyr_1.8.4       
## [13] dplyr_0.7.5       tools_3.5.0       grid_3.5.0       
## [16] beeswarm_0.2.3    gtable_0.2.0      withr_2.1.2      
## [19] digest_0.6.15     lazyeval_0.2.1    assertthat_0.2.0 
## [22] tibble_1.4.2      bindrcpp_0.2.2    purrr_0.2.4      
## [25] glue_1.2.0        evaluate_0.10.1   labeling_0.3     
## [28] stringi_1.2.2     compiler_3.5.0    pillar_1.2.3     
## [31] scales_0.5.0.9000 pkgconfig_2.0.1