ggplot: Add text on horizontal axis e.g. 2008(n = 100); 2009(n = 101)

In the following example, on the x-axis how could I add number of observations for each year? For example, instead of 2008, I would like to have 2008 (n = 100) . Note that n comes from the variable number_obs.

suppressWarnings(suppressMessages(library(tidyverse)))
#######
# Data
#######
set.seed(123)
toy_data <- tibble(
  year = 2008:2017,
  mean = rnorm(10, mean = 50, sd = 20),
  median = rnorm(10, mean = 40, sd = 15)
) %>% 
  pivot_longer(
    cols = mean:median,
    names_to = "stats",
    values_to = "delay"
  ) %>% 
  mutate(number_obs = rep(100:109, each = 2))

head(toy_data)
#> # A tibble: 6 x 4
#>    year stats  delay number_obs
#>   <int> <chr>  <dbl>      <int>
#> 1  2008 mean    38.8        100
#> 2  2008 median  58.4        100
#> 3  2009 mean    45.4        101
#> 4  2009 median  45.4        101
#> 5  2010 mean    81.2        102
#> 6  2010 median  46.0        102

#######
# Plot
######
toy_data %>% 
  ggplot(aes(x = year, y = delay , color = stats)) +
  geom_line() +
  geom_point() +
  stat_summary(geom = "text", fun = quantile,
               aes(label=sprintf("%1.0f", ..y..)),
               position = position_nudge (x= -0.25), size=2.5) +
  labs(
    subtitle = "Yearly Stat",
    color = "Statistics",
    y = "Delay",
    x = "Year"
  ) +
  scale_x_continuous(breaks = 2008:2018) +
  theme(text = element_text(size = 8),
        axis.text.x = element_text(angle = 90, size = 7))

Created on 2020-11-17 by the reprex package (v0.3.0)

In order to keep the axis values numeric, you can create a separate data frame of breaks and labels and apply that in scale_x_continuous. Also, to line the labels up under the tick marks, add vjust=0.5 in axis.text.x.

# x-axis breaks and labels
x = toy_data %>% 
  arrange(year) %>% 
  distinct(breaks=year, labs=paste0(year, " (n=",number_obs,")"))

toy_data %>% 
  ggplot(aes(x = year, y = delay , color = stats)) +
  geom_line() +
  geom_point() +
  stat_summary(geom = "text", fun = quantile,
               aes(label=sprintf("%1.0f", ..y..)),
               position = position_nudge (x= -0.25), size=2.5) +
  labs(
    subtitle = "Yearly Stat",
    color = "Statistics",
    y = "Delay",
    x = "Year"
  ) +
  scale_x_continuous(breaks = x$breaks, labels=x$labs) +
  theme(text = element_text(size = 8),
        axis.text.x = element_text(angle = 90, size = 7, vjust=0.5))
1 Like

Thanks a lot!
Quick question: is there a better way to put the number of observation i.e. other than putting next to the year?

I sometimes put them along the bottom or top of the plot area. In addition, you might consider using the y-values as the markers, which would be less cluttered than putting them next to the point markers. Also, I noticed that you used stat_summary to generate the text values, but you can use geom_text directly if you slice down to one value per year.

Below are a couple of variations using toy_data.

toy_data %>% 
  ggplot(aes(x = year, y = delay , color = stats)) +
  geom_line(size=0.3, alpha=0.5) +
  geom_text(aes(label=round(delay)), show.legend=FALSE, size=2.8) +
  labs(
    subtitle = "Yearly Stat",
    color = "Statistics",
    y = "Delay",
    x = "Year"
  ) +
  scale_x_continuous(breaks = x$breaks, labels=str_wrap(x$labs,5)) +
  theme_bw() +
  theme(text = element_text(size = 9)) +
  guides(colour=guide_legend(override.aes=list(size=2, alpha=1)))

Rplot

toy_data %>% 
  ggplot(aes(x = year, y = delay , color = stats)) +
  geom_hline(yintercept=0, colour="grey20", size=0.3) +
  geom_line(size=0.3, alpha=0.5) +
  geom_text(aes(label=round(delay)), show.legend=FALSE, size=2.8) +
  geom_text(data= . %>% group_by(year, stats) %>% slice(1),
            aes(label=number_obs, y=-1.5), colour="grey40", size=2.3) +
  labs(
    subtitle = "Yearly Stat",
    color = "Statistics",
    y = "Delay",
    x = "Year"
  ) +
  scale_x_continuous(breaks = x$breaks) +
  scale_y_continuous(limits=c(-5,NA), expand=expansion(c(0,0.05))) +
  theme_bw() +
  theme(text = element_text(size = 9)) +
  guides(colour=guide_legend(override.aes=list(size=2, alpha=1)))

Rplot01

1 Like

Many thanks for the suggestions and insights. I find them very useful :heart_eyes:

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.