How to create a graph with multiple nested facets horizontally and vertically?

94 Views Asked by At

Below is a simplified version of my data. I want to plot the Means using vertical bars with ggplot() + geom_bar. There are a few things I want to do.

  • I want to separate the data where I have the data grouped vertically (=side by side/beside each other/in columns) according to Vfront (all "front" data on left, all "back" data on right).

  • I also want to separate the data where I have the data grouped horizontally (on top of each other/in rows) according to Manner (all "Stop" on top row, all "Fric" on bottom row).

  • Additionally, I want to have the different MedDors grouped per Target (in the NA, cor, incor, X order).

Target  Manner  Vfront  MedDor  Mean
p       Stop    front   NA      0.60
p       Stop    back    NA      0.59
p       Stop    front   cor     0.58
p       Stop    back    cor     0.56
p       Stop    front   incor   0.52
p       Stop    back    incor   0.52
p       Stop    front   X       0.24
p       Stop    back    X       0.52
b       Stop    front   NA      0.42
b       Stop    back    NA      0.39
b       Stop    front   cor     0.37
b       Stop    back    cor     0.42
b       Stop    front   incor   0.36
b       Stop    back    incor   0.33
b       Stop    front   X       0.14
b       Stop    back    X       0.39
f       Fric    front   NA      0.27
f       Fric    back    NA      0.23
f       Fric    front   cor     0.19
f       Fric    back    cor     0.40
f       Fric    front   incor   0.13
f       Fric    back    incor   0.32
f       Fric    front   X       0.08
f       Fric    back    X       0.13
v       Fric    front   NA      0.24
v       Fric    back    NA      0.18
v       Fric    front   cor     0.17
v       Fric    back    cor     0.11
v       Fric    front   incor   0.25
v       Fric    back    incor   0.16
v       Fric    front   X       0.09
v       Fric    back    X       0.16

So in total I should get something similar to the grouped data that I have below:

                         Front                              Back
pNA pcor pincor cX bNA bcor bincor bX pNA pcor pincor cX bNA bcor bincor bX
         p                    b                    p                    b 
fNA fcor fincor fX vNA vcor vincor vX fNA fcor fincor fX vNA vcor vincor vX
         f                    v                    f                    v

I can do some basic filtering of data, and factoring to get certain orders and I know facet_grid(~Vfront) gives me different columns according to Vfront values (based on my data) but the divisions I am asking about is beyond my current R abilities.

1

There are 1 best solutions below

5
M-- On BEST ANSWER

We can use ggh4x::facet_nested for nested facets like you want. If you want a certain order of facets, you need to create factor columns with levels that you desire; however, NA will always come last, so we need to convert NA to "NA".

library(ggplot2)
library(ggh4x)
library(dplyr)
library(tidyr)

df1 %>% 
  mutate(MedDor_f = factor(replace_na(MedDor, "NA") , 
                           levels = c("NA", "cor", "incor", "X")),
         Manner_f = factor(Manner, levels = c("Stop", "Fric")), 
         Vfront_f = factor(Vfront, levels = c("front", "back"))) %>% 
 ggplot(aes(x = Target, y = Mean)) +
  geom_bar(stat = "identity") +
  facet_nested(rows = vars(Manner_f), cols = vars(Vfront_f, MedDor_f))

Update:

If you want the x-axis to have a free scale (like what we get in facet_wrap), you need to add scales = "free_x", independent = "x" to the facet_nested function.

I have also looked at your previous question, and implemented that solution here (not sure whether you want that or not, but it looks nice).

df1 %>% 
  mutate(MedDor_f = factor(replace_na(MedDor, "NA") , 
                           levels = c("NA", "cor", "incor", "X")),
         Manner_f = factor(Manner, levels = c("Stop", "Fric")), 
         Vfront_f = factor(Vfront, levels = c("front", "back")), 
         Deviation = Mean - 0.5) %>% 
 ggplot(aes(x = Target, y = Deviation)) +
  geom_col(aes(fill = Deviation > 0), position = position_dodge()) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
  scale_fill_manual(values = c("TRUE" = "cadetblue3", "FALSE" = "coral3")) +
  theme_classic() +
  scale_y_continuous(limits = c(-0.5, 0.5),  
                     breaks = seq(-0.5, 0.5, by = 0.1),
                     labels = function(x) sprintf("%.1f", x + 0.5)) +
  theme(legend.position = "bottom", 
        axis.ticks.x = element_blank()) +
  facet_nested(rows = vars(Manner_f), cols = vars(Vfront_f, MedDor_f), 
               scales = "free_x", independent = "x")

Created on 2024-02-06 with reprex v2.0.2

If you don't want to use ggh4x, then you need to use grid, gtable, and/or patchwork or other similar libraries, get your individual plots and arrange them in a grid. I personally would spend some more time on figuring out why ggh4x is not working, maybe updating my R version, etc., before going down that route. Good luck!