rggplot2x-axisgeom-col

x-axis tick are not centered under column


I am plotting some precipitation data by month using geom_col and the ticks and labels don't display correctly under the bars and I can't figure out why. The problem suddenly appeared today, I didn't have this problem last week and I didn't change the code especially.

Here is what I have currently

Here is is my code and a sample of the data.

    json %>% 
  filter(Station == "26_2_1_150_744") %>%
  filter(Date >= "2025-03-01 00:00:00" & Date <= "2025-06-15") %>%
  ggplot(aes(x = Date, y = measure)) + 
  geom_col(fill = "dodgerblue3") +
  scale_colour_manual(name = "") + 
  ylim(0, 300) + 
  labs(x = "", y = "") + 
  annotate("text", x = as.POSIXct("2025-06-05 12:00:00"), y = as.numeric(min_data + 15), label = "Ongoing", size = 9) + 
  annotate("text", x = as.POSIXct(Inf), y = Inf, label = "Station", vjust = 2, hjust = 1.08, size = 13) +
  theme_bw() + 
  theme(axis.text.x = element_text(size = 35), 
        axis.text.y = element_text(size = 35))


> dput(sample)
structure(list(measureDate = c("14/03/2025 12:00", "14/04/2025 12:00", 
"15/05/2025 12:00", "10/06/2025 12:00"), measure = c(250.7, 186, 
97.1, 5.7), Station = c("26_2_1_150_744", "26_2_1_150_744", "26_2_1_150_744", 
"26_2_1_150_744"), Date = structure(c(1741953600, 1744632000, 
1747310400, 1749556800), tzone = "UTC", class = c("POSIXct", 
"POSIXt")), Date2 = structure(c(1741953600, 1744632000, 1747310400, 
1749556800), tzone = "UTC", class = c("POSIXct", "POSIXt")), 
    Date3 = structure(c(20161, 20192, 20223, 20249), class = "Date"), 
    Month = c("mars", "avril", "mai", "juin")), row.names = c(NA, 
-4L), class = "data.frame")

Solution

  • The reason why the x axis labels are not directly under the bars is that the x axis labels are placed at the start of each month. Your measurements are not made at the start of each month, but on various different days (the 14th, 15th and 10th of the month).

    If you want the bars to be at the same place as the labels, you could simply find the floor_date of each measurement so that the bars are also placed at the first of each month:

    library(ggplot2)
    
    ggplot(sample, aes(x = lubridate::floor_date(Date3, "month"), y = measure)) +
      geom_col(fill = "dodgerblue3") +
      ylim(0, 300) + 
      labs(x = NULL, y = NULL) + 
      annotate("text", x = as.Date("2025-06-01"), 
               y = as.numeric(min(sample$measure) + 15), label = "Ongoing", 
               size = 9) + 
      annotate("text", x = as.Date(Inf), y = Inf, label = "Station", 
               vjust = 2, hjust = 1.08, size = 13) +
      scale_x_date(breaks = seq(as.Date("2025-03-01"), as.Date("2025-06-01"), 
                                by = "month"), labels = sample$Month) +
      theme_bw(35)  
    

    enter image description here

    Perhaps a less complex way to do it would be to use the months as a categorical x axis:

    ggplot(sample, aes(x = forcats::fct_inorder(Month), y = measure)) +
      geom_col(fill = "dodgerblue3") +
      ylim(0, 300) + 
      labs(x = NULL, y = NULL) + 
      annotate("text", x = "juin", y = min(sample$measure) + 15, 
               label = "Ongoing", size = 9) + 
      annotate("text", x = I(Inf), y = I(Inf), label = "Station", 
               vjust = 2, hjust = 1.08, size = 13) +
      theme_bw(35) 
    

    enter image description here