ggplot2's x-axis displays the starting date of next month instead of ending date of current month

Viewed 48

Given a dataframe df:

df <- structure(list(date = structure(c(19204, 19235, 19265, 19296), class = "Date"), 
    value = c(3.5, 3.6, 4.05, 4.13)), row.names = c(NA, -4L), class = "data.frame")

Out:

       date value
1 2022-07-31  3.50
2 2022-08-31  3.60
3 2022-09-30  4.05
4 2022-10-31  4.13

I want the x-axis to display the date values of date column in df, that is, July, August, September, October, the last day of these months, but I have tried several methods, all of which are displayed on the first day of the next month. How could we deal with this problem? Thanks.

ggplot(data=df, aes(x=date, y=value)) +
  geom_line(linetype='solid', alpha = 1, size = 1.5, color='blue') +
  geom_point(alpha = 1, size = 4, color='blue') +
  # scale_x_date(limits = as.Date(c("2022-07-31", "2022-10-31"), format = '%Y-%m-%d'), breaks = '1 month')
  # scale_x_continuous(labels=format(as.Date(df$date), "%Y-%m-%d"))
  # scale_x_datetime(breaks = df$date, date_labels = '%Y-%m-%d') 
  # scale_x_date(breaks = "1 month", minor_breaks = "1 month", date_labels = "%Y-%m-%d")
  scale_x_date(breaks = "1 month", minor_breaks = "1 month", date_labels = "%Y-%m-%d",
               limits = c(min(df$date), max = max(df$date)))

Out:

enter image description here

2 Answers

If you provide an explicit list of breaks, ggplot will "obey". So, you could use the following code:

ggplot(data=df, aes(x=date, y=value)) +
  geom_line(linetype='solid', alpha = 1, size = 1.5, color='blue') +
  geom_point(alpha = 1, size = 4, color='blue') + 
  scale_x_date(breaks = as.Date(c("2022-07-31", "2022-08-31", "2022-09-30", "2022-10-31")))

or even shorter:

ggplot(data=df, aes(x=date, y=value)) +
  geom_line(linetype='solid', alpha = 1, size = 1.5, color='blue') +
  geom_point(alpha = 1, size = 4, color='blue') + 
  scale_x_date(breaks = df$date)

yielding this plot:

enter image description here

The solution seems trivial, so I have to ask: Is this what you are after?

Edit

In case you have many data points, the same principle holds. Plot your data, but assign your breaks explicitly, like in the following code: (In addition, the labels are rotated by 45°)

## mock-up data
df2 <- structure(list(date = structure(seq(19204, 19296, 1), class = "Date"),
                     value = c(seq(3.5, 3.6, length.out = 31),
                               seq(3.6, 4.05, length.out = 31),
                               seq(4.05, 4.13, length.out = 31)) ), 
                 row.names = c(NA, -93L),
                 class = "data.frame")

## define my_breaks                
my_breaks = as.Date(c("2022-07-31", "2022-08-31", "2022-09-30", "2022-10-31"))

ggplot(data=df2, aes(x=date, y=value)) +
  geom_line(linetype='solid', alpha = 1, size = 1.5, color='blue') +
  geom_point(alpha = 1, size = 3, color='blue') + 
  scale_x_date(breaks = my_breaks) +                          ## breaks
  theme(axis.text.x = element_text(angle = 45, hjust = 1))    ## rotate

Which yields this plot:

enter image description here

You can change the date format in ggplot bloc: "%B-%y" will display August-22 "%B" only August

ggplot(data=df, aes(x=date, y=value)) +
  geom_line(linetype='solid', alpha = 1, size = 1.5, color='blue') +
  geom_point(alpha = 1, size = 4, color='blue') +
  # scale_x_date(limits = as.Date(c("2022-07-31", "2022-10-31"), format = '%Y-%m-%d'), breaks = '1 month')
  # scale_x_continuous(labels=format(as.Date(df$date), "%Y-%m-%d"))
  # scale_x_datetime(breaks = df$date, date_labels = '%Y-%m-%d') 
  # scale_x_date(breaks = "1 month", minor_breaks = "1 month", date_labels = "%Y-%m-%d")
  scale_x_date(breaks = "1 month", minor_breaks = "1 month", date_labels = "%B",
               limits = c(min(df$date), max = max(df$date)))
Related