rggplot2plotaxesggh4x

How to put axis labels in between the axis ticks in ggplot2


In base R, I like to make plots with time on the x-axis where the labels are shown in between long tick marks. For example, there may be tick marks at June 1 and June 31, but the text "June" shows up in between, centered around June 15. In base R, I simply draw 2 axes, one with the ticks and one with the labels.

However, I haven't been able to figure out how to make this style of axis in ggplot2.

I think something like this might be possible with the ggh4x package but I haven't been able to figure it out. I will be happy for any solution compatible with ggplot2, regardless of which package.


Solution

  • My solution is to assign the major x-axis breaks to the 15th of each month and the minor axis breaks to the 1st of each month. Then, in the theme() function, I remove the major axis breaks and place more visual emphasis on the "minor" breaks.

    Here is some data.

    library(tidyverse)
    library(nycflights13)
    
    flights.df <- nycflights13::flights |>
      group_by(year, month, day) |>
      summarise(flights = n(),
                delayed = sum(dep_delay > 10, na.rm = T),
                .groups = "drop") |>
      mutate(pct_delayed = delayed/flights*100,
             date = as.Date(paste(year, month, day, sep = "-")))
    flights.df
    # A tibble: 365 × 7
        year month   day flights delayed pct_delayed date      
       <int> <int> <int>   <int>   <int>       <dbl> <date>    
     1  2013     1     1     842     195        23.2 2013-01-01
     2  2013     1     2     943     276        29.3 2013-01-02
     3  2013     1     3     914     234        25.6 2013-01-03
     4  2013     1     4     915     218        23.8 2013-01-04
     5  2013     1     5     720     129        17.9 2013-01-05
     6  2013     1     6     832     178        21.4 2013-01-06
     7  2013     1     7     933     149        16.0 2013-01-07
     8  2013     1     8     899     116        12.9 2013-01-08
     9  2013     1     9     902     102        11.3 2013-01-09
    10  2013     1    10     932     106        11.4 2013-01-10
    # ℹ 355 more rows
    

    The basic plot looks like this, with the date labels placed at the beginning of each month.

    ggplot(flights.df, aes(date, pct_delayed)) +
    geom_point() +
    scale_x_date(breaks = scales::date_breaks("1 month"),
    labels = scales::label_date("%b"))
    
    

    scatterplot

    Here's the same graph, but with the major and minor x grid flipped. Notice the explicit addition of minor axis ticks. You can specify any custom handling of the axis ticks.

    ggplot(flights.df, aes(date, pct_delayed)) +   
      geom_point() +   
      scale_x_date(breaks = flights.df$date[flights.df$day == "15"],                
                   minor_breaks = flights.df$date[flights.df$day == "1"],                
                   labels = scales::label_date("%b"),                
                   guide = guide_axis(minor.ticks = TRUE)) +   
      theme(axis.ticks.x = element_blank(),         
            axis.minor.ticks.x.bottom = element_line())
    

    scatterplot w/custom axis