rggplot2anova

Plotting Three Levels of Nested Data Using ggplot and R


I recently ran a few 2x3 Mixed ANOVAs for a neuroscience project with age and valence as predictors of correlation values. These analyses were ran three times, once for each ROI, or brain region. I can create point line plots that visualize the data for each ROI pretty much without issue:

#Dataframe creation and data import
df <- data.frame(matrix(NA, nrow = 6, 
                        ncol = 1, 
                        dimnames = list(1:6, "Age")))
df$Age <-c(rep("Child", 3), rep("Adult", 3))
df$Valence <- c(rep(c("Negative", "Mixed", "Positive"),2))
df$Cor <- c(0.319, 0.303, 0.278, 0.200, 0.230, 0.216)
df$ci <- c(0.0648, 0.0548, 0.0557, 0.0631, 0.0542, 0.0649)

#Plotting
ggplot(df, aes(x = Age, y = Cor, group = Valence, color = Valence)) + 
  stat_summary(geom="pointrange", position=position_dodge(width = .2), fun.data = NULL) +
  stat_summary(geom="line", position=position_dodge(width = .2), fun.data = NULL) +
  geom_errorbar(aes(ymin = Cor - ci, ymax = Cor + ci), size= 0.3, width=0.2, position=position_dodge(width = .2)) +
  scale_fill_hue(name="Valence Type", breaks=c("Negative", "Mixed", "Positive"), labels=c("Negative", "Mixed", "Positive")) +
  scale_x_discrete(name = "Age Groups",  
                   labels = c("Child", "Adult")) +
  scale_y_continuous(limits=c(0.10,0.40)) +
  ggtitle("Representational Similarity By Age and Valence") + 
  labs(y="Mean Correlation Value") + 
  theme_bw() +
  theme(title = element_text(face="bold", size=14)) +
  theme(axis.text.x = element_text(size=14, color = "Black")) +
  theme(axis.text.y = element_text(size=14, color = "Black"))

However, what I'd love to do is consolidate the three separate plots into one so that the results of all three ROIs are represented in the same image; something like the image below, where the results of each ROI appear side by side of one another: MS Paint Rendering of my Scripting Inadequacies

I've had no luck in finding a scripting solution to this problem, though. I thought maybe I could try nesting X axis values, since that's what I'm doing conceptually and that of course didn't work. I tried combining the ROI and Age factors, such that I then had "Child, AMY", "Adult, AMY", "Child, NAcc", etc., but using a point line format, the lines extended across ROIs, which is not ideal. Point Line is my preference in visualization due to the research in biases present when reading bar plots, but if someone has a good solution to this using bar plots, I wouldn't fight it.

I'm including a summary dataframe and my boilerplate ggplot script below.

#Dataframe creation and data import
df <- data.frame(matrix(NA, nrow = 18, 
                            ncol = 1, 
                            dimnames = list(1:18, "Age")))
df$Age <-c(rep("Child", 9), rep("Adult", 9))
df$Valence <- c(rep(c(rep("Negative",3), rep("Mixed",3), rep("Positive",3)),2))
df$ROI <- rep(c("AMY", "NAcc", "vmPFC"), 6)
df$Cor <- c(0.229, 0.268, 0.319, 0.236, 0.260, 0.303, 0.213, 0.236, 0.278, 0.188, 0.205, 0.200, 0.221, 0.226, 0.230, 0.208, 0.210, 0.216)
df$ci <- c(0.0474, 0.0675, 0.0648, 0.0445, 0.0456, 0.0548, 0.0531, 0.0518, 0.0557, 0.0485, 0.0727, 0.0631, 0.0336, 0.0541, 0.0542, 0.0430, 0.0604, 0.0649)

#Plotting
ggplot(df, aes(x = Age, y = Cor, group = Valence, color = Valence)) + 
  stat_summary(geom="pointrange", position=position_dodge(width = .2), fun.data = NULL) +
  stat_summary(geom="line", position=position_dodge(width = .2), fun.data = NULL) +
  geom_errorbar(aes(ymin = Cor - ci, ymax = Cor + ci), size= 0.3, width=0.2, position=position_dodge(width = .2)) +
  scale_fill_hue(name="Valence Type", breaks=c("Negative", "Mixed", "Positive"), labels=c("Negative", "Mixed", "Positive")) +
  scale_x_discrete(name = "Age Groups",  
                   labels = c("Child", "Adult")) +
  scale_y_continuous(limits=c(0.10,0.40)) +
  ggtitle("Representational Similarity By Age and Valence") + 
  labs(y="Mean Correlation Value") + 
  theme_bw() +
  theme(title = element_text(face="bold", size=14)) +
  theme(axis.text.x = element_text(size=14, color = "Black")) +
  theme(axis.text.y = element_text(size=14, color = "Black"))

Solution

  • You can try this (you have to wrap the plots and adjust some details for strips). The data used is your last version of df (nested):

    library(tidyverse)
    #Data
    df <- data.frame(matrix(NA, nrow = 18, 
                                ncol = 1, 
                                dimnames = list(1:18, "Age")))
    df$Age <-c(rep("Child", 9), rep("Adult", 9))
    df$Valence <- c(rep(c(rep("Negative",3), rep("Mixed",3), rep("Positive",3)),2))
    df$ROI <- rep(c("AMY", "NAcc", "vmPFC"), 6)
    df$Cor <- c(0.229, 0.268, 0.319, 0.236, 0.260, 0.303, 0.213, 0.236, 0.278, 0.188, 0.205, 0.200, 0.221, 0.226, 0.230, 0.208, 0.210, 0.216)
    df$ci <- c(0.0474, 0.0675, 0.0648, 0.0445, 0.0456, 0.0548, 0.0531, 0.0518, 0.0557, 0.0485, 0.0727, 0.0631, 0.0336, 0.0541, 0.0542, 0.0430, 0.0604, 0.0649)
    #Plot
    ggplot(df, aes(x = Age, y = Cor, group = Valence, color = Valence)) + 
      stat_summary(geom="pointrange", position=position_dodge(width = .2), fun.data = NULL) +
      stat_summary(geom="line", position=position_dodge(width = .2), fun.data = NULL) +
      geom_errorbar(aes(ymin = Cor - ci, ymax = Cor + ci), size= 0.3, width=0.2, position=position_dodge(width = .2)) +
      scale_fill_hue(name="Valence Type", breaks=c("Negative", "Mixed", "Positive"), labels=c("Negative", "Mixed", "Positive")) +
      scale_x_discrete(name = "",  
                       labels = c("Child", "Adult")) +
      scale_y_continuous(limits=c(0.10,0.40)) +
      facet_wrap(.~ROI,scales='free',strip.position = "bottom")+
      ggtitle("Representational Similarity By Age and Valence") + 
      labs(y="Mean Correlation Value",x="") + 
      theme_bw() +
      theme(title = element_text(face="bold", size=14)) +
      theme(axis.text.x = element_text(size=14, color = "Black")) +
      theme(axis.text.y = element_text(size=14, color = "Black")) +
      theme(panel.spacing    = unit(0, "points"),
            strip.background = element_blank(),
            strip.placement  = "outside",
            strip.text = element_text(size=12,face = 'bold'))
    

    Output:

    enter image description here