Search code examples
rggplot2borderfacet-grid

ggplot2 outside panel border when using facet


I would like to have a border around the outside of my faceted plot but not have the lines that separate the panels inside the plot. The issue is that panel.border draws a border around each panel in the facet with no option to just have a border around the entire plot. Alternatively can you set the inside dividing lines to 'white' but keep the outside border 'black'.

Here is my code:

mtcars
mtcars$manufacturer=rownames(mtcars)
ggplot(mtcars, aes(x=manufacturer, y=mpg,fill=factor(gear,levels=c("3","4","5"))))+
  geom_bar(stat="identity",position="dodge",colour="black")+
  facet_grid(~cyl,scales = "free_x",space = "free_x",) +
  theme(axis.text.x = element_text(angle = 45,size=12,colour="Black",vjust=1,hjust=1),
    strip.background = element_blank(),
    strip.placement = "inside",
    strip.text = element_text(size=15),
    legend.position=c(0.9,0.8),
    legend.title=element_blank(),
    legend.text=element_text(size=15),
    panel.spacing = unit(0.2, "lines"), 
    panel.background=element_rect(fill="white"),
    panel.border=element_rect(colour="black",size=1),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank())

Result: Faceted plot with inside borders

enter image description here

Desired output (edited in paint): Faceted plot without inside lines

enter image description here

My actual data plot that I want to remove the inside lines from looks like this:

Actual data plot with inside borders needing to be removed


Solution

  • Two options for consideration, both making use of a secondary axis to simulate the panel border on the right side. Use option 2 if you want to do away with the facet box outlines on top as well.

    Option 1:

    ggplot(df,
           aes(x = Month, y = Abundance, fill = Type)) +
      geom_col(position = "dodge", colour = "black") +
      scale_y_continuous(labels = function(x){paste(x, "-")},    # simulate tick marks for left axis
                         sec.axis = dup_axis(breaks = 0)) +      # add right axis
      scale_fill_grey() +
      facet_grid(~Season, scales = "free_x", space = "free_x") +
      theme_classic() +
      theme(axis.title.y.right = element_blank(),                # hide right axis title
            axis.text.y.right = element_blank(),                 # hide right axis labels
            axis.ticks.y = element_blank(),                      # hide left/right axis ticks
            axis.text.y = element_text(margin = margin(r = 0)),  # move left axis labels closer to axis
            panel.spacing = unit(0, "mm"),                       # remove spacing between facets
            strip.background = element_rect(size = 0.5))         # match default line size of theme_classic
    

    option 1

    (I'm leaving the legend in the default position as it's not critical here.)

    Option 2 is a modification of option 1, with facet outline removed & a horizontal line added to simulate the top border. Y-axis limits are set explicitly to match the height of this border:

    y.upper.limit <- diff(range(df$Abundance)) * 0.05 + max(df$Abundance)
    y.lower.limit <- 0 - diff(range(df$Abundance)) * 0.05
    
    ggplot(df,
           aes(x = Month, y = Abundance, fill = Type)) +
      geom_col(position = "dodge", colour = "black") +
      geom_hline(yintercept = y.upper.limit) +
      scale_y_continuous(labels = function(x){paste(x, "-")},    # 
                         sec.axis = dup_axis(breaks = 0),        # 
                         expand = c(0, 0)) +                     # no expansion from explicitly set range
      scale_fill_grey() +
      facet_grid(~Season, scales = "free_x", space = "free_x") +
      coord_cartesian(ylim = c(y.lower.limit, y.upper.limit)) +  # set explicit range
      theme_classic() +
      theme(axis.title.y.right = element_blank(),                # 
            axis.text.y.right = element_blank(),                 # 
            axis.ticks.y = element_blank(),                      # 
            axis.text.y = element_text(margin = margin(r = 0)),  # 
            panel.spacing = unit(0, "mm"),                       # 
            strip.background = element_blank())                  # hide facet outline
    

    option 2

    Sample data used:

    set.seed(10)
    df <- data.frame(
      Month = rep(c("Jun 14", "Aug 14", "Oct 14", "Dec 14", "Apr 15", "Jun 15"),
                 each = 3),
      Type = rep(c("Mangrove", "Mudflat", "Fringe"), 6),
      Season = rep(c("Dry1", rep("Wet1", 3), rep("Dry2", 2)), each = 3),
      Abundance = sample(50:600, 18)
    )
    
    df <- df %>%
      mutate(Month = factor(Month, levels = c("Jun 14", "Aug 14", "Oct 14", 
                                              "Dec 14", "Apr 15", "Jun 15")),
             Season = factor(Season, levels = c("Dry1", "Wet1", "Dry2")))
    

    (For the record, I don't think facet_grid / facet_wrap were intended for such use cases...)