rfor-loopggplot2ggtitle

How to name plots with the element name in list when using a for loop in R


I'm making some bar plots for individual subjects using a for loop in R. The graphs seem to be correct, but the problem is that I need to title the graphs using their subject ID (subid), which is what they are listed by in the list that I'm looping over. So I have a list of dataframes, and each element in the list is a different subid, but I haven't been able to get the subids into the title of the graphs.

Here's a simplified example of what I'm working with:

subid <- c(1, 1, 2, 2, 3, 3)
x <- c('scary', 'neutral', 'scary', 'neutral', 'scary', 'neutral')
y <- c(98, 60, 60, 20, 80, 30)
df <- data.frame(subid, x, y)
print(df)

Then I split the dataframe up like this:

df_list <- split(df, df$subid)

First, I tried using paste0():

for (subid in df_list) {
  plot <- ggplot(subid, aes(x, y)) + 
    geom_bar(position='dodge', stat='summary', fun='mean') + # make it show mean of y var
    ggtitle(paste0("Ratings by face for ", subid)) # title of graph
  print(plot)
}

But that just used the list of values (i.e. "Ratings by face for c(1,1)"), which definitely isn't what I want (and in my actual data it pulls the list of numbers from a different column in the dataframes, which is even worse).

Then I tried using names() with it:

for (subid in df_list) {
  plot <- ggplot(subid, aes(x, y)) + 
    geom_bar(position='dodge', stat='summary', fun='mean') + # make it show mean of y var
    ggtitle(paste0("Ratings by face for ", names(df_list))) # title of graph
  print(plot)
}

But this only uses the first item in the list, so all of the plots are titled "Ratings by face for 1", instead of changing based off of the subid.

The plots themselves are only pulling data for the subid in the iteration, which is right; I just can't get the titles to work. I've also tried a few other things like setting the title outside of the plot function, adding as.character(subid), but I haven't gotten anything to work, so any help would be very appreciated! I'm also pretty new to R, and especially when it comes to loops, so for all I know the answer is something obvious.


Solution

  • Iterate over names(df_list), and use each name to index into df_list:

    library(ggplot2)
    
    for (subid in names(df_list)) {
      plot <- ggplot(df_list[[subid]], aes(x, y)) + 
        geom_bar(stat='summary', fun='mean') + 
        ggtitle(paste0("Ratings by face for ", subid)) 
      print(plot)
    }
    

    Or using purrr::iwalk():

    library(purrr)
    
    iwalk(df_list, \(df, nm) {
      plot <- ggplot(df, aes(x, y)) + 
        geom_bar(stat='summary', fun='mean') +
        ggtitle(paste0("Ratings by face for ", nm)) 
      print(plot)
    })
    

    However, if you don't specifically need separate plots, you might consider faceting instead:

    ggplot(df, aes(x, y)) + 
      geom_bar(stat='summary', fun='mean') +
      facet_wrap(
        vars(subid), 
        nrow = 1,
        labeller = labeller(subid = \(x) paste("Ratings by face for", x))
      ) +
      theme(
        strip.text = element_text(size = 12, hjust = 0),
        strip.background = element_blank()
      )