ggrepel
ggrepel copied to clipboard
Multiple limits for ggrepel in ggplot
I have a graph with data similar to this.
data <- data.frame(x = c(1:30),
y = rnorm(30, 0, 1),
z = rep(c("A", "B", "C"), 10))
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(aes(label = z), force = 1, segment.alpha = .5, box.padding = 1, xlim = c(-5, 35), ylim = c(-5, 5)) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
I am trying to get the labels to be contained a the borders of the graph, so on the x-axis they are always either >2.5 or <-2.5 and on the y-axis they are always either >30 or <-30. I know the xlim() and ylim() features can put the limits within one rectangle, but how could I create multiple limits so that they stay on the edges of the graph? Thank you!
Thanks for opening a new issue.
If I understand you correctly, you're asking if ggrepel supports excluding text labels from a rectangular region. Unfortunately, ggrepel does not support this feature. This might be an interesting new feature to consider for the future.
You might consider working around the limitations of ggrepel. One approach is to divide the data into multiple pieces. Then each piece could be limited to one rectangle.
library(ggrepel)
#> Loading required package: ggplot2
data <- data.frame(
x = c(1:30),
y = rnorm(30, 0, 1),
z = rep(c("A", "B", "C"), 10)
)
d1 <- data[data$y > quantile(data$y, 0.5),]
d2 <- data[data$y <= quantile(data$y, 0.5),]
ggplot() +
aes(x = x, y = y, group = z, color = z, label = z) +
geom_point(data = d1) +
geom_point(data = d2) +
geom_label_repel(
data = d1,
segment.alpha = .5,
ylim = c(2.5, Inf)
) +
geom_label_repel(
data = d2,
segment.alpha = .5,
ylim = c(-2.5, -Inf)
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
Created on 2020-04-09 by the reprex package (v0.3.0)
Session info
devtools::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 3.6.1 (2019-07-05)
#> os macOS Catalina 10.15.4
#> system x86_64, darwin15.6.0
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz America/New_York
#> date 2020-04-09
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date lib source
#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 3.6.0)
#> backports 1.1.6 2020-04-05 [1] CRAN (R 3.6.1)
#> callr 3.4.3 2020-03-28 [1] CRAN (R 3.6.2)
#> cli 2.0.2 2020-02-28 [1] CRAN (R 3.6.0)
#> colorspace 1.4-1 2019-03-18 [1] CRAN (R 3.6.0)
#> crayon 1.3.4 2017-09-16 [1] CRAN (R 3.6.0)
#> curl 4.3 2019-12-02 [1] CRAN (R 3.6.1)
#> desc 1.2.0 2018-05-01 [1] CRAN (R 3.6.0)
#> devtools 2.2.2 2020-02-17 [1] CRAN (R 3.6.0)
#> digest 0.6.25 2020-02-23 [1] CRAN (R 3.6.0)
#> dplyr 0.8.5 2020-03-07 [1] CRAN (R 3.6.0)
#> ellipsis 0.3.0 2019-09-20 [1] CRAN (R 3.6.0)
#> evaluate 0.14 2019-05-28 [1] CRAN (R 3.6.0)
#> fansi 0.4.1 2020-01-08 [1] CRAN (R 3.6.1)
#> farver 2.0.3 2020-01-16 [1] CRAN (R 3.6.0)
#> fs 1.4.1 2020-04-04 [1] CRAN (R 3.6.1)
#> ggplot2 * 3.3.0 2020-03-05 [1] CRAN (R 3.6.0)
#> ggrepel * 0.8.2 2020-03-08 [1] CRAN (R 3.6.0)
#> glue 1.4.0 2020-04-03 [1] CRAN (R 3.6.1)
#> gtable 0.3.0 2019-03-25 [1] CRAN (R 3.6.0)
#> highr 0.8 2019-03-20 [1] CRAN (R 3.6.0)
#> htmltools 0.4.0 2019-10-04 [1] CRAN (R 3.6.0)
#> httr 1.4.1 2019-08-05 [1] CRAN (R 3.6.0)
#> knitr 1.28 2020-02-06 [1] CRAN (R 3.6.0)
#> labeling 0.3 2014-08-23 [1] CRAN (R 3.6.0)
#> lifecycle 0.2.0 2020-03-06 [1] CRAN (R 3.6.0)
#> magrittr 1.5 2014-11-22 [1] CRAN (R 3.6.0)
#> memoise 1.1.0.9000 2020-01-12 [1] Github (r-lib/memoise@58d3972)
#> mime 0.9 2020-02-04 [1] CRAN (R 3.6.0)
#> munsell 0.5.0 2018-06-12 [1] CRAN (R 3.6.0)
#> pillar 1.4.3 2019-12-20 [1] CRAN (R 3.6.0)
#> pkgbuild 1.0.6 2019-10-09 [1] CRAN (R 3.6.0)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 3.6.1)
#> pkgload 1.0.2 2018-10-29 [1] CRAN (R 3.6.0)
#> prettyunits 1.1.1 2020-01-24 [1] CRAN (R 3.6.0)
#> processx 3.4.2 2020-02-09 [1] CRAN (R 3.6.1)
#> ps 1.3.2 2020-02-13 [1] CRAN (R 3.6.0)
#> purrr 0.3.3 2019-10-18 [1] CRAN (R 3.6.0)
#> R6 2.4.1 2019-11-12 [1] CRAN (R 3.6.0)
#> Rcpp 1.0.4.6 2020-04-07 [1] Github (RcppCore/Rcpp@a831639)
#> remotes 2.1.1 2020-02-15 [1] CRAN (R 3.6.0)
#> rlang 0.4.5 2020-03-01 [1] CRAN (R 3.6.1)
#> rmarkdown 2.1 2020-01-20 [1] CRAN (R 3.6.0)
#> rprojroot 1.3-2 2018-01-03 [1] CRAN (R 3.6.0)
#> scales 1.1.0 2019-11-18 [1] CRAN (R 3.6.0)
#> sessioninfo 1.1.1 2018-11-05 [1] CRAN (R 3.6.0)
#> stringi 1.4.6 2020-02-17 [1] CRAN (R 3.6.0)
#> stringr 1.4.0 2019-02-10 [1] CRAN (R 3.6.0)
#> testthat 2.3.2 2020-03-02 [1] CRAN (R 3.6.0)
#> tibble 3.0.0 2020-03-30 [1] CRAN (R 3.6.2)
#> tidyselect 1.0.0 2020-01-27 [1] CRAN (R 3.6.0)
#> usethis 1.5.1 2019-07-04 [1] CRAN (R 3.6.0)
#> vctrs 0.2.4 2020-03-10 [1] CRAN (R 3.6.1)
#> withr 2.1.2 2018-03-15 [1] CRAN (R 3.6.0)
#> xfun 0.12 2020-01-13 [1] CRAN (R 3.6.0)
#> xml2 1.3.0 2020-04-01 [1] CRAN (R 3.6.2)
#> yaml 2.2.1 2020-02-01 [1] CRAN (R 3.6.0)
#>
#> [1] /Library/Frameworks/R.framework/Versions/3.6/Resources/library
If you want to spread the labels to all four borders, then you could use nudge_x
, nudge_y
and direction
- position the labels in the nearst corner (abusing nudge as an aesthetic):
set.seed(0)
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf),
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
force = 0, # note: zero force for demonstration
segment.alpha = .5, box.padding = 1
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
- restrict in which direction specific labels are allowed to move (by ways of splitting the data) and increase the force to your liking
- set ylim on the part which is restricted to the y direction, or xlim on the part which is restricted to the x direction, to prevent overlapping labels in the corners.
Result:
set.seed(0)
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(
data=data[abs(scale(data$y)) > abs(scale(data$x)), ],
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf),
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
direction='x',
force = 2,
segment.alpha = .5,
box.padding = 1
) +
geom_label_repel(
data=data[abs(scale(data$y)) < abs(scale(data$x)), ],
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf),
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
direction='y',
force = 25,
segment.alpha = .5,
box.padding = 1,
ylim = c(-4.5, 4.5),
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
A few notes:
- if
direction
accepted a vector, we would not need to split the data, nor set the xlim/ylim (even better if this was an aesthetic) - I am getting
“Ignoring unknown aesthetics: nudge_y, nudge_x”
warning, but they appear to work. (ggplot 3.3.0, ggrepel from master)
Or maybe even better, only nudge in the direction of interest:
set.seed(0)
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(
data=data[abs(scale(data$y)) > abs(scale(data$x)), ],
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf)
),
direction='x',
force = 2,
segment.alpha = .5,
box.padding = 1
) +
geom_label_repel(
data=data[abs(scale(data$y)) < abs(scale(data$x)), ],
aes(
label=z,
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
direction='y',
force = 25,
segment.alpha = .5,
box.padding = 1,
ylim = c(-4.5, 4.5),
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
@krassowski Those graphs look perfect. The only problem is that I have copy and pasted the code but I get a different graph:
data <- data.frame(
x = c(1:30),
y = rnorm(30, 0, 1),
z = rep(c("A", "B", "C"), 10)
)
set.seed(0)
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf),
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
force = 0, # note: zero force for demonstration
segment.alpha = .5, box.padding = 1
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
set.seed(0)
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(
data=data[abs(scale(data$y)) > abs(scale(data$x)), ],
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf),
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
direction='x',
force = 2,
segment.alpha = .5,
box.padding = 1
) +
geom_label_repel(
data=data[abs(scale(data$y)) < abs(scale(data$x)), ],
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf),
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
direction='y',
force = 25,
segment.alpha = .5,
box.padding = 1,
ylim = c(-4.5, 4.5),
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
set.seed(0)
ggplot(data = data, aes(x = x, y = y, group = z, color = z, label = z)) +
geom_point() +
geom_label_repel(
data=data[abs(scale(data$y)) > abs(scale(data$x)), ],
aes(
label=z,
nudge_y=ifelse(scale(y) > 0, Inf, -Inf)
),
direction='x',
force = 2,
segment.alpha = .5,
box.padding = 1
) +
geom_label_repel(
data=data[abs(scale(data$y)) < abs(scale(data$x)), ],
aes(
label=z,
nudge_x=ifelse(scale(x) > 0, Inf, -Inf)
),
direction='y',
force = 25,
segment.alpha = .5,
box.padding = 1,
ylim = c(-4.5, 4.5),
) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5))
Could it be some problem in the installation of the package, or maybe the code didn't copy right on the site?
I think that it is because I use a newer (yet to be released) version. Sorry, I did not check if it works with the latest released version. If you are not afraid you could try:
devtools::install_github("slowkow/ggrepel")
# a restart of the session might be required
but, this is not a stable release yet and may have some rough edges.
Solved! I have tweeked the code by @krassowski and now it works for my session, thank you! I downloaded the newer version and restarted but the output was the same. With some minor changes I got this:
set.seed(0)
data <- data.frame(
x = c(1:30),
y = rnorm(30, 0, 1),
z = rep(c("A", "B", "C"), 10)
)
data$g <- as.factor(ifelse(abs(scale(data$y)) > abs(scale(data$x))
& scale(data$y) > 0, 1,
ifelse(abs(scale(data$y)) > abs(scale(data$x))
& scale(data$y) < 0, 2,
ifelse(abs(scale(data$x)) > abs(scale(data$y))
& scale(data$x) > 0, 3,
ifelse(abs(scale(data$x)) > abs(scale(data$y))
& scale(data$x) < 0, 4, NA)))))
ggplot(data = data, aes(x = x, y = y, group = z, color = g, label = z)) +
geom_point() +
geom_label_repel(data=data[data$g == 1, ],
aes(label=z), nudge_y = 10, ylim = c(-5, 5),
segment.alpha = .5, box.padding = 0.8, xlim = c(-5, 35)) +
geom_label_repel(data=data[data$g == 2, ],
aes(label=z), nudge_y = -10, ylim = c(-5, 5),
segment.alpha = .5, box.padding = 0.8, xlim = c(-5, 35)) +
geom_label_repel(data=data[data$g == 3, ],
aes(label=z), nudge_x = 10, ylim = c(-5, 5),
segment.alpha = .5, box.padding = 0.8, xlim = c(-5, 35)) +
geom_label_repel(data=data[data$g == 4, ],
aes(label=z), nudge_x = -10, ylim = c(-5, 5),
segment.alpha = .5, box.padding = 0.8, xlim = c(-5, 35)) +
scale_x_continuous(limits = c(-5, 35)) +
scale_y_continuous(limits = c(-5, 5)) +
theme(legend.position = "none")
By playing aroudn with the size, limits and padding, you can get a more "extreme" or "edgy" result.
Thanks for your help, Michał! 😀
I hadn't realized that nudge_x
and nudge_y
could be used in such a way.
After reading your note, maybe we should consider allowing a vector for direction
... will need to think about that.
Others have also mentioned that something more expressive for direction
instead of "x", "y", "both"
could be useful.