'Add minor data labels for date x-axis in ggplot

Considering the example data below:

df <- structure(list(Name = c("HFK", "AMW", "OPS", "QTM", "RKL", 
  "PDF", "CAD", "PMC", "NLY", "TWV"), Date = structure(c(18659, 19024, 
  19052, 19052, 19079, 19153, 19192, 19233, 19260, 19417), 
  class = "Date"), Value = c("121 M", "105 M", "90 M", "93 M", 
  "25 M", "56 M", "89 M", "121 M", "81 M", "50 M"), displ = 
  c(1, 0.45, 0.25, 0.75, 1, 0.50, 0.85, 0.25, 0.75, 1)), 
  row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))

I want to draw a timeline with ggplot:

ggplot(df, aes(Date, displ, label = Name)) + 
  geom_point(size = 2) + theme_classic() + guides(color = FALSE) +
  labs(title = "MY TITLE") +
  theme(axis.line.y = element_blank(), axis.ticks = element_blank(),
        axis.text.y = element_blank(), axis.title = element_blank(),
        plot.title = element_text(size = 20, hjust = 0.5, face = "bold",
          margin = margin(c(0.3,0,-1.7,0), unit = "in")), 
        axis.text.x = element_text(size = 13, color = "black",
           margin = margin(c(0.05,0,0,0), unit = "in"), face = "bold"),
        axis.line.x = element_line(size = 0.9)) +
  scale_x_date(date_breaks = "1 year", date_minor_breaks = "1 month", 
              date_labels = "%Y") +
  scale_y_continuous(expand = c(0, 0), limits = c(0,1.7)) + geom_label() + 
  geom_segment(aes(x = Date, xend = Date,
                   y = 0, yend = displ), size = 0.5)

enter image description here

I want to add the months to the plot as well, as a minor data label. but the only way I saw was using:

scale_x_date(date_breaks = "1 year", date_minor_breaks = "1 month", 
              date_labels = "%b-%Y") + 

But that repeats the year for every months. I ant each month and year to be mentioned only once as a timeline. The desired output is:

enter image description here



Solution 1:[1]

There is a problem with your example (no displ column in the data frame df). However, we can create one for demonstration purposes easily enough by doing

df$displ <- as.numeric(substr(df$Value, 1, 2)) / 100

Although there are ways to fake nested axes using facets, this becomes difficult with dates, and it may be best simply to label the axis with months and add the years as annotations:

ggplot(df, aes(Date, displ, label = Name)) + 
  geom_point(size = 2) + theme_classic() + guides(color = 'none') +
  labs(title = "MY TITLE") +
  scale_x_date(date_breaks = "1 month", date_labels = "%b") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 1.7)) + 
  geom_segment(aes(x = Date, xend = Date,
                   y = 0, yend = displ), size = 0.5) +
  geom_label(fill = 'white') +
  coord_cartesian(clip = 'off') +
  annotation_custom(grid::textGrob('2021'), ymin = -0.15, ymax = -0.15,
                    xmin = as.Date('2021-02-28'), xmax = as.Date('2021-02-28')) +
  annotation_custom(grid::textGrob('2022'), ymin = -0.15, ymax = -0.15,
                    xmin = as.Date('2022-02-28'), xmax = as.Date('2022-02-28')) +
  theme(axis.title.x = element_text(margin = margin(30, 10, 10, 10)),
        axis.line.y = element_blank(),
        axis.ticks.y = element_blank(),
        axis.text.y = element_blank(),
        axis.title.y = element_blank())

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Allan Cameron