Ggplot bar graphs with date x variable and specified limits bug

Ggplot bar graphs with date x variable and specified limits specified seem to lose the min/max value if it is the same as the limits. Does anyone know why this is? Or is there something I've mistaken? Thanks!

library(tidyverse)

plot_data <- storms %>%
  group_by(year) %>%
  summarise(average_wind = round(mean(wind), 2)) %>% 
  mutate(year = lubridate::ymd(glue::glue("{year}-01-01", tz = "Pacific/Auckland")))

x_var_vctr <- plot_data$year
x_breaks <- pretty(x_var_vctr, n = 8)
x_limits <- c(min(x_breaks), max(x_breaks))

# this works
ggplot(plot_data) +
  geom_col(aes(year, average_wind)) + 
  scale_x_date(breaks = x_breaks, labels = scales::date_format("%Y"))

# this doesn't work
ggplot(plot_data) +
  geom_col(aes(year, average_wind)) + 
  scale_x_date(limits = x_limits, breaks = x_breaks, labels = scales::date_format("%Y"))

Hi,

This is because the geom_col have a "width". If you make a geom_point it would work just fine, however in case of geom_col you have to consider that left side of the first bar and right side of the last bar turn out to be outside of the limits when you apply scale_x_date(limits = ...). Hence it outside of the limits it assign to NA, the ggplot doesn't know how to draw them and just drop with warning.

Instead use coord_cartesian(xlim = x_limits). Please check examples in documentation ?coord_cartesian to understand the difference between scale_x_(limits = ...)and coord_cartesian(xlim = ...).

library(tidyverse)

plot_data <- storms %>%
  group_by(year) %>%
  summarise(average_wind = round(mean(wind), 2)) %>% 
  mutate(year = lubridate::ymd(glue::glue("{year}-01-01", tz = "Pacific/Auckland")))

x_var_vctr <- plot_data$year
x_breaks <- pretty(x_var_vctr, n = 8)
x_limits <- c(min(x_breaks), max(x_breaks))

# This works
p1 <- ggplot(plot_data) +
  geom_col(aes(year, average_wind)) + 
  scale_x_date(breaks = x_breaks, labels = scales::date_format("%Y"))

# This doesn't work
p2 <- ggplot(plot_data) +
  geom_col(aes(year, average_wind)) + 
  scale_x_date(limits = x_limits, breaks = x_breaks, labels = scales::date_format("%Y"))

# This works
p3 <- ggplot(plot_data) +
  geom_point(aes(year, average_wind)) + 
  scale_x_date(limits = x_limits, breaks = x_breaks, labels = scales::date_format("%Y"))

# This is your limits in numeric format
as.numeric(x_limits)
#> [1]  1826 16436

# This is how the first bar defined in p1. Note xmin
ggplot_build(p1)$data[[1]] %>%
  head(n = 1)
#>      x     y PANEL group flipped_aes ymin  ymax    xmin    xmax colour   fill
#> 1 1826 50.87     1    -1       FALSE    0 50.87 1661.75 1990.25     NA grey35
#>   size linetype alpha
#> 1  0.5        1    NA

# This is how the first bar defined in p2
# Note, that xmin became NA because 1661.75 outside of 1826 ~ 16436 range.
ggplot_build(p2)$data[[1]] %>%
  head(n = 1)
#>      x     y PANEL group flipped_aes ymin  ymax xmin    xmax colour   fill size
#> 1 1826 50.87     1    -1       FALSE    0 50.87   NA 1990.25     NA grey35  0.5
#>   linetype alpha
#> 1        1    NA

# Use coord_cartesian instead
p4 <- ggplot(plot_data) +
  geom_col(aes(year, average_wind), ) + 
  coord_cartesian(xlim = x_limits)+
  scale_x_date(breaks = x_breaks, labels = scales::date_format("%Y"))
1 Like

That is super helpful! I've learned a lot from this post. Thanks so much @Dobrokhotov1989

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.