1

I have an issue and I did not find anything on internet about it. I would be glad to get some hints.

I have a dataset, where the x-axis is discrete, but I would like to connect the points to each other (which I can do). My issue is when I add the faceting option. I cannot link the points to each other anymore. I found an alternative, BUT it does not look great.

This is a simple reproducible example:

# Create a simple dataset
dat <- data.frame(Days = c(rep("Monday", 6), rep("Tuesday", 6), rep("Wednesday", 6)),
                  Shift = rep(c("Morning", "Evening", "Night"), 6),
                  Group = c(rep("Group1", 3), rep("Group2", 3), rep("Group1", 3), rep("Group2", 3), rep("Group1", 3), rep("Group2", 3)),
                  Amount = c(6, 5, 6, 2, 3, 1, 5, 4, 6, 1, 2, 2, 5, 4, 7, 3, 1, 2)) 
# Change into factor to have the order I need
dat$Days <- factor(dat$Days, levels = c("Monday", "Tuesday", "Wednesday"))
dat$Shift <- factor(dat$Shift, levels = c("Morning", "Evening", "Night"))

# The plot I am stuck with
# The last dot of Monday night should be linked to Tuesday morning
ggplot(data = dat, aes(x = Shift, y = Amount)) + geom_point() +
    geom_line(aes(group = Group)) + facet_grid(.~Days) +
    theme(panel.spacing = unit(0, "lines"))

enter image description here

# This is the alternative I found, but clearly not great
ggplot(data = dat, aes(x = interaction(Days, Shift, sep = "\n", lex.order = T), y = Amount)) + geom_point(aes(color = Group)) +
    geom_line(aes(group = Group)) +
    geom_vline(xintercept = c(3, 6, 9),
               color = "darkred", linetype = 2)

enter image description here

Robin.N.
  • 99
  • 8
  • 1
    related https://stackoverflow.com/questions/42151880/ggplot-drawing-multiple-lines-across-facets/42171082#comment77259469_42171082 and https://stackoverflow.com/questions/31690007/ggplot-drawing-line-between-points-across-facets – tjebo Apr 05 '23 at 16:01
  • 1
    and here a quasi duplicate https://stackoverflow.com/questions/66553823/how-can-i-draw-geom-line-across-facets-or-grid?noredirect=1 – tjebo Apr 05 '23 at 16:04
  • 1
    From this thread, a wise comment from user @gregorthomas: "_Facets are separate graphs. Seems like you don't want facets..._" – tjebo Apr 05 '23 at 16:05
  • @tjebo thank you for posting those threads! I did not find them! Yes, I agree that facets are for separate graphs, but in my case I wanted to split them by days of the week... Thus, I was confused how to achieve it. – Robin.N. Apr 06 '23 at 05:24
  • 1
    I understand now that you're basically looking for nested axis labels? Another approach would be https://stackoverflow.com/questions/20571306/multi-row-x-axis-labels-in-ggplot-line-chart?noredirect=1&lq=1 or https://stackoverflow.com/questions/44616530/axis-labels-on-two-lines-with-nested-x-variables-year-below-months?noredirect=1&lq=1 – tjebo Apr 08 '23 at 11:29
  • 1
    @tjebo WOW! This is perfect! This is a way easier option and clean one! I did not know the term "nested axis labels"... Thus, it is solving my issue the best! Thank you! – Robin.N. Apr 11 '23 at 06:54

2 Answers2

4

I don't think there's a simple way to connect data between facets using standard ggplot2 syntax. I've seen approaches that augment it afterwards by getting into the innards, e.g. using ggplot_build or the grid package.

Your 2nd approach of "faking the facets" seems simpler to me. It can look cleaner with more tweaking. For instance:

ggplot(data = dat, aes(x = interaction(Days, Shift, sep = "\n", lex.order = T),
                       y = Amount)) + 
  geom_point(aes(color = Group)) +
  geom_vline(xintercept = c(3, 6, 9),
             color = "darkred", linetype = 2) +
  annotate("rect", xmin = c(0.5,3.5,6.5), xmax = c(3.5,6.5,9.5),
           ymin = 6.4, ymax = 7, fill = "gray80", color = "gray90") +
  annotate("text", x = c(2,5,8), y = 7, vjust = 1.5, 
           label = c("Monday", "Tuesday","Wednesday")) +
  geom_vline(xintercept = c(0.5,3.5,6.5), color = "white", size = 2) +
  geom_line(aes(group = Group)) +

  scale_x_discrete(labels = dat$Shift, name = "Shift") +
  coord_cartesian(ylim = c(0.5, 7), expand = FALSE) 

enter image description here

A downside of my particular approach here is that the facet label rectangles need to be sized in relation to your output resolution -- ie if you resize the output you may want to adjust the ymin of the "rect" annotation, for instance.

Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • Upon review, I think this counts as a pretty close dupe of the answer here. I won't take it personally if people vote to close. :-) https://stackoverflow.com/questions/66553823/how-can-i-draw-geom-line-across-facets-or-grid?noredirect=1 – Jon Spring Apr 05 '23 at 16:37
  • Thank you! Sorry @Jon Spring I did not find the thread you posted... I will work with both! – Robin.N. Apr 06 '23 at 05:22
2

Another option would be to use patchwork to create three separate plots but set limits via coord_cartesian so that only one day is displayed per plot. Unfortunately I haven't found a way to set limits via coord_cartesian directly as can be done via the limits argument of the scale, i.e. we have to pass a numeric range. As a consequence we need some manual work to manually compute the numeric limits.

library(ggplot2)
library(patchwork)

plot_fun <- function(limits) {
  day <- unique(limits$Days)
  breaks = as.character(limits$x)
  limits <- as.numeric(unique(dat$x))[levels(dat$x) %in% limits$x]
  
  theme_adjust <- if (!day %in% c("Monday")) {
    theme(axis.text.y = element_blank(),
          axis.title.y = element_blank(),
          axis.line.y = element_blank(),
          axis.ticks.length.y = unit(0, "pt")
        )
  }
  
  labs <- if (!day == "Tuesday") labs(x = NULL) else labs(x = "Shift")
  
  ggplot(data = dat, aes(x = x, y = Amount)) +
    geom_point() +
    geom_line(aes(group = Group)) +
    facet_wrap(~as.character(day)) +
    scale_x_discrete(breaks = breaks, labels = ~ gsub("^(.*)\\..*$", "\\1", .x), expand = c(0, .5)) +
    coord_cartesian(xlim = range(limits)) +
    theme_adjust +
    theme(plot.margin = margin()) +
    labs
}

dat$x <- interaction(dat$Shift, dat$Days)

xlims <- dat[!duplicated(dat$x), c("Days", "x")]
xlims <- split(xlims, xlims$Days)

p <- lapply(xlims, plot_fun) 

p |> 
  wrap_plots() &
  theme(panel.spacing.x = unit(0, "pt"))

enter image description here

stefan
  • 90,330
  • 6
  • 25
  • 51
  • Thank you! I never used the patchwork package, but it looks quite interesting! I will dig deeper into it :) – Robin.N. Apr 06 '23 at 05:23