Search code examples
rggplot2geom-text

In ggplot, how to position a text at the very right end while having it left-aligned?


I'm trying to create a plot with ggplot() and geom_text(), to have a text annotation at the very right end of the plot, but still have the text aligned left. I've tried many combinations of x positioning and hjust, but so far to no avail.

Example

Let's create a boxplot based on ToothGrowth built-in dataset. At the initial stage, I want to have a geom_hline() specific to each facet mean as follows:

library(ggplot2)

mean_per_panel <- aggregate(len ~ supp, data = ToothGrowth, FUN = mean)

p <- 
  ggplot(ToothGrowth, aes(x = factor(dose), y = len)) +
  geom_boxplot() +
  geom_hline(data = mean_per_panel, 
             aes(yintercept = len, group = "supp"), 
             linetype = 2, 
             color = "red") +
  facet_wrap(~supp) +
  theme_bw()

p

Created on 2021-09-11 by the reprex package (v2.0.0)


So far so good. Here comes to problem: I want to add some annotation to explain the dashed line. And I want such text to be:

  • Flushed to the right, regardless of image rescaling (e.g., x = Inf)
  • Left-aligned

So the desired output should look like: desired_output


My unsuccessful attempts

First, I supplement my mean_per_panel data summary with a label column:

library(dplyr, warn.conflicts = FALSE)

mean_per_panel_with_label <-
  mean_per_panel %>%
  mutate(my_label = paste("mean for", supp, "is:", round(len, 2), sep = "\n"))

mean_per_panel_with_label
#>   supp      len                 my_label
#> 1   OJ 20.66333 mean for\nOJ\nis:\n20.66
#> 2   VC 16.96333 mean for\nVC\nis:\n16.96

Here are some attempts to achieve the desired output, all of them unsuccessful:

my_geom_text <- function(x_pos, ...) {
  geom_text(data = mean_per_panel_with_label, 
            aes(y = len, label = my_label),
            vjust = 1,
            x = x_pos,
            ...,
            color = "red") 
}

p +
  my_geom_text(x_pos = 2, hjust = 0)

p +
  my_geom_text(x_pos = 2.8, hjust = 0)

p +
  my_geom_text(x_pos = Inf, hjust = 1)

p +
  my_geom_text(x_pos = Inf, hjust = 1.2)

Created on 2021-09-11 by the reprex package (v2.0.0)


Is there a way to have the text show up at the very right always (like what x = Inf does) and at the same time be left-aligned?


Solution

  • I believe ggtext's geom_textbox() can do what you're looking for. In introduces a seperation of hjust and halign to seperately align the box and the text.

    library(ggtext)
    library(ggplot2)
    library(dplyr)
    
    mean_per_panel <- ToothGrowth %>%
      group_by(supp) %>%
      summarise(mean = mean(len)) %>%
      mutate(my_label = paste("mean for", supp, "is:", round(mean, 2), sep = "<br>"))
    
    ggplot(ToothGrowth, aes(as.factor(dose), len)) +
      geom_boxplot() +
      geom_hline(data = mean_per_panel, aes(yintercept = mean),
                 colour = "red") +
      geom_textbox(
        data = mean_per_panel,
        aes(y = mean, x = Inf, label = my_label),
        hjust = 1, halign = 0, 
        box.colour = NA, fill = NA, # Hide the box fill and outline
        box.padding = unit(rep(2.75, 4), "pt"), colour = "red",
        vjust = 1, width = NULL
      ) +
      facet_grid(~ supp)
    

    Created on 2021-09-11 by the reprex package (v2.0.1)