Search code examples
rggplot2scale

ggplot Integer Breaks on Facets


library(tidyverse)
df <- tibble(col1 = rep(c("A", "B"), 2),
             col2 = c(0.4, 0.7, 3, 9),
             col3 = c("I", "I", "II", "II"))
#> # A tibble: 4 x 3
#>   col1   col2 col3 
#>   <chr> <dbl> <chr>
#> 1 A       0.4 I    
#> 2 B       0.7 I    
#> 3 A       3   II   
#> 4 B       9   II 

ggplot(df, aes(col1, col2)) + 
  geom_col() + 
  facet_wrap(vars(col3), scales = "free")

integer 3

I want to create integer breaks for the ggplot above such that:

  1. There is an integer lower break at or below the lowest value value for each facet.
  2. There is an integer upper break at or above the highest value value for each facet.

For my first facet I the integer values for the axis would include 0 and 1. For the second facet II the integer values should include at the min 0 and the max integer would have to be at least 9, maybe 10 would look better, depending on the routine used for creating the breaks.

These attempts from this older stackoverflow question don't quite work.

# Attempt 1 
ggplot(df, aes(col1, col2)) + 
  geom_col() + 
  facet_wrap(vars(col3), scales = "free") +
  scale_y_continuous(
    breaks = function(x) unique(floor(pretty(seq(0, (max(x) + 1) * 1.1)))))

# Attempt 2
ggplot(df, aes(col1, col2)) + 
  geom_col() + 
  facet_wrap(vars(col3), scales = "free") +
  scale_y_continuous(breaks = scales::pretty_breaks(2))

# Attempt 3
ggplot(df, aes(col1, col2)) + 
  geom_col() + 
  facet_wrap(vars(col3), scales = "free") +
  scale_y_continuous(breaks = c(0, 1))

# Attempt 4
ggplot(df, aes(col1, col2)) + 
  geom_col() + 
  facet_wrap(vars(col3), scales = "free") +
  scale_y_continuous(
    breaks = function(x) seq(ceiling(x[1]), floor(x[2]), by = 1))

# Attempt 5
ggplot(df, aes(col1, col2)) + 
  geom_col() + 
  facet_wrap(vars(col3), scales = "free") +
  scale_y_continuous(
    breaks = 
      function(x, n = 5)  pretty(x, n)[round(pretty(x, n),1) %% 1 == 0])

Most attempts produce something like the following. Notice the missing 1 break on the first facet. And I'd want the second facet to have a break at 10. I don't want to manually set limits or breaks because in reality I have hundreds of facets. Hopefully one of the functions above can be modified to fit my requirements.

integer 4


Solution

  • To achieve the desired result you also have to adjust the limits of the y-axis. Try this:

    library(tidyverse)
    
    df <- tibble(col1 = rep(c("A", "B"), 2),
                 col2 = c(0.4, 0.7, 3, 9),
                 col3 = c("I", "I", "II", "II"))
    
    my_ceil <- function(x) {
      ceil <- ceiling(max(x))
      ifelse(ceil > 1 & ceil %% 2 == 1, ceil + 1, ceil)
    }
    
    my_breaks <- function(x) { 
      ceil <- my_ceil(max(x))
      unique(ceiling(pretty(seq(0, ceil))))
    } 
    
    my_limits <- function(x) { 
      ceil <- my_ceil(x[2])
      c(x[1], ceil)
    }
    
    ggplot(df, aes(col1, col2)) + 
      geom_col() + 
      facet_wrap(vars(col3), scales = "free")  +
      scale_y_continuous(breaks = my_breaks, limits = my_limits)
    

    Created on 2020-05-21 by the reprex package (v0.3.0)