rggplot2axisfacetfacet-wrap

different y axis breaks per facet


I am trying to create different bar graphs within a loop showing counts per species and regions, faceting by regions. The structure of my df it's something like:

# Sample data frame structure 
  max_counts <- data.frame(
    Region = c("Eco1", "Eco1", "Eco2", "Eco2"),
    Date = c(1, 2, 1, 2),
    max_count = c(10, 15, 8, 12),
    rounded_max_count = c(10, 15, 10, 15)
  )
  
  # Replicate the data frame for multiple species
  species_list <- c("Sp1", "Sp2", "Sp3")
  max_counts_list <- list()
  for (species in species_list) {
    max_counts_species <- max_counts
    max_counts_species$Species <- species
    max_counts_list[[length(max_counts_list) + 1]] <- max_counts_species
  }
  
  # Combine the replicated data frames into one
  max_counts_df <- do.call(rbind, max_counts_list)

I want the y axis of each facet to be its own and nota common y axis shared as in some regions there are many records and in others just a few. Since many attempts have given me bars reaching the top of the facet and I wanted some padding I created a column rounding the maximum value to the nearest multiple of 5. I need now the y max label on each region to be the rounded ymax value on that particular region. I have tried many things but the closes approach looks like this:

 # Create the plot
      plot <- ggplot(max_counts, aes(x = Date, y = max_count, fill = Region)) +
        geom_bar(stat = "identity") +
        scale_fill_manual(values = mycolors) +  
        facet_wrap(~ Region, ncol = 1, scales = "free_y") +  
        scale_x_continuous(breaks = seq(min(filtered_ecoregions$Date), max(filtered_ecoregions$Date), by = 1)) +
        scale_y_continuous(breaks = seq(0, max(max_counts$rounded_max_count), by = 5), expand = c(0, 0.01)) +
        theme_bw()

But it is not quite doing it... my attempts either create a common y axis for all facets or show the individual ones but the ymax label is not displayed.

Any advice would be welcome! Thanks


Solution

  • The issue is that only breaks= which fall inside the limits= of the scale will be displayed. Hence, for all panels where rounded_max_count is larger than max(max_count) your desired "maximum" value will not be displayed.

    Besides of that, if you want to set the limits= individually per panel I would suggest to have a look at the ggh4x package which via ggh4x::facetted_pos_scales allows to specify positional scales (including the limits=) individually per panel.

    library(ggplot2)
    library(ggh4x)
    
    # Create a list of y scales per panel
    scale_y <- max_counts |>
      split(~Region) |>
      lapply(\(x) {
        scale_y_continuous(
          breaks = seq(0, max(x$rounded_max_count), by = 5), expand = c(0, 0.01),
          limits = c(0, max(x$rounded_max_count))
        )
      })
    
    ggplot(max_counts, aes(x = Date, y = max_count, fill = Region)) +
      geom_bar(stat = "identity") +
      #scale_fill_manual(values = mycolors) +
      #scale_x_continuous(breaks = seq(min(filtered_ecoregions$Date), max(filtered_ecoregions$Date), by = 1)) +
      scale_x_continuous(breaks = scales::breaks_width(1)) +
      facet_wrap(~Region, ncol = 1, scales = "free_y") +
      ggh4x::facetted_pos_scales(
        y = scale_y
      ) +
      theme_bw()