Skip to content

Instantly share code, notes, and snippets.

@slowkow
Last active May 2, 2018 21:07
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slowkow/003b4d9f3f59cee8551c to your computer and use it in GitHub Desktop.
Save slowkow/003b4d9f3f59cee8551c to your computer and use it in GitHub Desktop.
Repel text labels away from each other in a ggplot2 figure.
library(ggplot2)
library(FField)
# You'll have to play with repulsion, cex.x, and cex.y to get satisfactory results.
plot_text <- function(x, y, label, repulsion = 1, cex.x = 110, cex.y = 40) {
dat <- data.frame(xpos = x, ypos = y, label = label)
dat$label <- as.character(dat$label)
# Use the FField package to repel the text labels away from each other.
dat <- cbind(
dat,
FFieldPtRep(coords = dat[,c("xpos", "ypos")], rep.fact = repulsion)
)
# Get the bounding box sizes of the text labels.
dat$w <- local({
plot.new()
retval <- strwidth(dat$label, cex = cex.x)
dev.off()
retval
})
dat$h <- local({
plot.new()
retval <- strheight(dat$label, cex = cex.y)
dev.off()
retval
})
# Feel free to customize this as you wish.
ggplot(dat) +
# Points for the data.
geom_point(
aes(x = xpos, y = ypos),
size = 4
) +
# Line segments connecting the text labels to points.
geom_segment(
aes(x = xpos, xend = x, y = ypos, yend = y),
color = "#666666"
) +
# White rectangles behind text labels to enhance legibility.
geom_rect(
aes(xmin = x - w / 2, xmax = x + w / 2,
ymin = y - h / 2, ymax = y + h / 2),
fill = "white", alpha = 0.5
) +
# Text labels.
geom_text(aes(x = x, y = y, label = label), size = 8) +
theme_bw(base_size = 24)
}
# Seed for reproducibility.
set.seed(43)
d <- data.frame(
x = runif(10),
y = runif(10),
label = letters[1:10]
)
plot_text(d$x, d$y, d$label, repulsion = 0.001, cex.x = 10, cex.y = 20)
library(ggplot2)
library(wordcloud)
# Seed for reproducibility.
set.seed(43)
d <- data.frame(
xpos = runif(10),
ypos = runif(10),
label = replicate(paste(sample(letters, 6), collapse=""), n = 10)
)
font.size <- 12
# d <- cbind(d, wordlayout(d$x, d$y, d$label, units = "inches"))
d <- cbind(d, wordlayout(d$x, d$y, d$label, units = "figure", cex = font.size / 4))
d$x <- d$x + d$width / 2
d$y <- d$y + d$ht / 2
ggplot(d) +
# Points for the data.
geom_point(
aes(x = xpos, y = ypos),
size = 4
) +
# Line segments connecting the text labels to points.
geom_segment(
aes(x = xpos, xend = x, y = ypos, yend = y),
color = "#666666"
) +
# White rectangles behind text labels to enhance legibility.
geom_rect(
aes(xmin = x - width / 2, xmax = x + width / 2,
ymin = y - ht / 2, ymax = y + ht / 2),
fill = "blue", alpha = 0.5
) +
# Text labels.
geom_text(aes(x = x, y = y, label = label), size = font.size) +
theme_bw(base_size = 24)
@slowkow
Copy link
Author

slowkow commented Sep 9, 2015

Thanks to Grigori Kapoustin for the FField package. I learned about strwidth and strheight from this stackoverflow post.

If this does not suit you, please consider some alternatives:

Thinking about how to make a new geom or grob...

@slowkow
Copy link
Author

slowkow commented Oct 10, 2017

After exploring the limitations of these packages I decided to make my own package: https://github.com/slowkow/ggrepel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment