Question

GGplot: extend axis/facet lines into the panel/axis labels

Edit: I added a reprex below. My goal is to add the red lines on this faceted chart.

enter image description here

So I have this chart showing regression output, with variables grouped by category, I have used facet_grid with 'free' scales and removing panel.spacing to get this grouped plot and I think it looks great! However, I want to expand the segment lines within the plot out to meet up with the panel. Does anyone have any ideas?

I've tried:

  • playing with theme elements (nothing seems to modify that)
  • annotating a line segment
  • expanding the axis itself
  • modifying the facet arguments
  • probably a handful of other things I can't remember now.

Any advice would be much appreciated!

Edit: Reprex

library(tidyverse)

sample_df <- 
  tribble(~grp, ~Term, ~estimate,
          'Other', 'Population Density', 2,
          'Age', '15-30', 1,
          'Age', '30-45', 0,
          'Age', '45-65', -2,
          'Age', '65+', -4,
          'Sex', 'Male', 1.5,
          'Sex', 'Other', -.5,
          'Education', 'High School', -2,
          'Education', 'College', 4,
          'Education', 'Graduate', 2
  )

sample_df %>%
  ggplot(aes(
    y = fct_rev(as_factor(Term)), x = estimate, 
    group = fct_rev(grp),
  )) + 
  geom_bar(stat = 'identity', color = 'grey64') + 
  labs(
    x = 'Coefficient Estimate',
    y = NULL,
  ) + 
  facet_grid(grp~., scales='free', space='free_y', switch='y') + 
  scale_x_continuous(breaks = scales::breaks_width(5), minor_breaks = scales::breaks_width(1)) + 
  theme(
    strip.placement = 'outside',
    strip.clip='off',
    panel.spacing = unit(0, 'in'), 
    panel.border = element_rect(color='gray33', fill=NA, linetype=1)
  )
 2  41  2
1 Jan 1970

Solution

 0

Here's a hacky solution:

  1. Plot your data on a numerical scale (instead of discrete factor scale).
  2. Adapt your axis that it shows factor levels nevertheless - we need ggh4x::facetted_pos_scales to adapt each facetted axis separately.
  3. As you are now operating on a numerical scale, you can add segments between the bars.
  4. Make sure to add clip = 'off' to allow for the segments to protrude to the axis area.

N.B. Needed to use coord_flip, as I could not convince R to use horizontal bars with a numerical scale.

library(tidyverse)
library(ggh4x)

sample_df <- 
  tribble(~grp, ~Term, ~estimate,
          'Other', 'Population Density', 2,
          'Age', '15-30', 1,
          'Age', '30-45', 0,
          'Age', '45-65', -2,
          'Age', '65+', -4,
          'Sex', 'Male', 1.5,
          'Sex', 'Other', -.5,
          'Education', 'High School', -2,
          'Education', 'College', 4,
          'Education', 'Graduate', 2
  ) %>% 
  mutate(Term = fct_rev(as_factor(Term)))

lines_df <- sample_df %>% 
  group_by(grp) %>% 
  summarize(min_x = min(as.numeric(Term)) - .75,
            max_x = max(as.numeric(Term)) + .75,
            brks = list(as.numeric(Term)),
            lbls = list(as.character(Term))) 

x_scls <- lines_df %>% 
  group_by(grp) %>% 
  group_map( ~ scale_x_continuous(
    breaks = .x$brks[[1L]],
    labels = .x$lbls[[1L]],
    limits = c(.x$min_x, .x$max_x),
    expand = expansion()
  ))

ggplot(sample_df) +
  geom_col(
    aes(x = as.numeric(Term),
        y = estimate)
  ) +
  geom_segment(
    data = lines_df %>% slice(-1L),
    aes(x = max_x, xend = max_x, y = -5.9, yend = 4.5),
    color = "gray33"
  ) +
  facet_grid(grp ~ ., 
             scales = "free",
             space = "free_y", 
             switch = "y") +
  facetted_pos_scales(x = x_scls,
                      y = scale_y_continuous(breaks = -2:2 * 2)) +
  labs(y = "Coefficient Estimate", x = NULL) +
  coord_flip(ylim = c(-4.5, 4.5), clip = "off", expand = FALSE) +
  theme(
    strip.placement = "outside",
    strip.clip = "off",
    panel.spacing = unit(0, "in")
  )

A facetted bar chart with lines between the facets

2024-07-25
thothal