Welcome to JiKe DevOps Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
421 views
in Technique[技术] by (71.8m points)

r - with nested loops, creating several one-page aggregates of ggplots

This follows a previous question was answered so helpfully by @Duck. (R ggpot: Arranging on one page several ggplots created with a loop / name each plot differenly). I was looking to put several ggplots created by a loop on one page. I made a model loop and @Duck provided a way to do just that. It worked so nicely, that I got an even bigger appetite and now want to nest the whole thing in another loop, creating several such pages (each present data from a different year). But it’s not working well.

Here is what @Duck suggested, for several plots on one page:

  library(dplyr)
  library(ggplot2)
  library(patchwork)
  #Create list
  List <- list()
  cylinder<-unique(mtcars$cyl)
  #Loop
  for (value in seq_along(cylinder)) {
    m<-mtcars%>%
      filter(cyl==cylinder[value])%>%
      group_by (gear)%>%
      summarise(number=n(), average=mean(mpg), se=sd(mpg))
    print(m) # reporting the numbers
    
    a<-m%>%
      mutate(gear=factor(gear, levels=unique(gear)))%>%
      ggplot()+
      geom_bar(aes(x=gear, y=average), stat = 'identity', fill ='red') +
      geom_errorbar( aes(x= gear, ymin=average-se, ymax=average+se), width=0.2, colour="black", alpha=1, size=1) +
      xlab("gears") + ylab("average mpg") +
      ggtitle (paste( "cyliner:", value ))+
      theme(axis.ticks.x=element_blank())  
    print(a)
    List[[value]]<-a}
  #Wrap plots
  wrap_plots(List,nrow = 1)
   

Nice! Now, for the sake of simplicity, say we just want three of the same. Here is how I tried to nest it.


  library(dplyr)
  library(ggplot2)
  library(patchwork)
repeats<-c(1,2,3)  # for the outer loop. 
  List <- list()
  cylinder<-unique(mtcars$cyl)

    #Loop 1:
  
  for (pic in seq_along(repeats)) {
    
    # loop 2:
  
  for (value in seq_along(cylinder)) 
# same code:
    m<-mtcars%>%
      filter(cyl==cylinder[value])%>%
      group_by (gear)%>%
      summarise(number=n(), average=mean(mpg), se=sd(mpg))
    print(m) # reporting the numbers
    
    a<-m%>%
      mutate(gear=factor(gear, levels=unique(gear)))%>%
      ggplot()+
      geom_bar(aes(x=gear, y=average), stat = 'identity', fill ='red') +
      geom_errorbar( aes(x= gear, ymin=average-se, ymax=average+se), width=0.2, colour="black", alpha=1, size=1) +
      xlab("gears") + ylab("average mpg") +
      ggtitle (paste( "cyliner:", value ))+
      theme(axis.ticks.x=element_blank())  
    print(a)
    List[[value]]<-a}
  #Wrap plots
  wrap_plots(List,nrow = 1)

    List[[value]]<-a }
  #Wrap plots
  wrap_plots(List,nrow = 1)

      print (List) #reporter
  List <- list()
  print(List) #reporter
  
  }  

Judge by how many times the lists is filled, then zeroed, the compilation goes through the loops correctly. But only 6 single plots are made (not 9), and no page with three wrapped plots. Suggestion?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

Please log in or register to answer this question.

1 Answer

0 votes
by (71.8m points)

This could be achieved like so:

  1. When using loops I prefer to put the plotting code in a separate function. Makes it easier to check, to debug and results (in my opinion) in cleaner and clearer code. Therefore I put the plotting code in a function make_plot.

  2. I did the same for your inner loop. I created a function

    inner_loop <- function(x) {
      # Create list
      List <- lapply(seq_along(cylinder), make_plot)
      # Wrap plots
      wrap_plots(List, nrow = 1)  
    }
    

which loops over the cylinders, makes a plot for each value and glues the plots together.

  1. After these inital steps we can easily loop over repeats, call function inner_loop and finally glue the resulting list of pages together:
repeats <- c(1,2,3)  # for the outer loop. 
pages <- lapply(repeats, inner_loop)
wrap_plots(pages, ncol = 1)  

Full reproducible code:

library(dplyr)
library(ggplot2)
library(patchwork)

cylinder <- unique(mtcars$cyl)

make_plot <- function(value) {
  m <- mtcars %>%
    filter(cyl == cylinder[value]) %>%
    group_by(gear) %>%
    summarise(number = n(), average = mean(mpg), se = sd(mpg))
  #print(m) # reporting the numbers
  
  m %>%
    mutate(gear = factor(gear, levels = unique(gear))) %>%
    ggplot() +
    geom_bar(aes(x = gear, y = average), stat = "identity", fill = "red") +
    geom_errorbar(aes(x = gear, ymin = average - se, ymax = average + se), width = 0.2, colour = "black", alpha = 1, size = 1) +
    xlab("gears") +
    ylab("average mpg") +
    ggtitle(paste("cylinder:", value)) +
    theme(axis.ticks.x = element_blank())
}

inner_loop <- function(x) {
  # Create list
  List <- lapply(seq_along(cylinder), make_plot)
  # Wrap plots
  wrap_plots(List, nrow = 1)  
}

# Outer loop
repeats <- c(1,2,3)  # for the outer loop. 
pages <- lapply(repeats, inner_loop)

wrap_plots(pages, ncol = 1)  

EDIT I adapted the example and the code a bit. Now the outer loop loops over the gears. The value of gear is passed as an parameter to the inner loop and to the plot function, where it can be used to filter the dataset accordingly. As a result you nine different plots. One row per gear and the columns for the cylinders.

library(dplyr)
library(ggplot2)
library(patchwork)

make_plot <- function(value, .gear) {
  m <- mtcars %>%
    filter(cyl == cylinder[value], gear %in% .gear) %>%
    group_by(gear) %>%
    summarise(number = n(), average = mean(mpg), se = sd(mpg))
  
  m %>%
    mutate(gear = factor(gear, levels = unique(gear))) %>%
    ggplot() +
    geom_bar(aes(x = gear, y = average), stat = "identity", fill = "red") +
    geom_errorbar(aes(x = gear, ymin = average - se, ymax = average + se), width = 0.2, colour = "black", alpha = 1, size = 1) +
    xlab("gears") +
    ylab("average mpg") +
    ggtitle(paste("cylinder:", value)) +
    theme(axis.ticks.x = element_blank())
}

inner_loop <- function(gear) {
  # Create list
  List <- lapply(seq_along(cylinder), make_plot, .gear = gear)
  # Wrap plots
  wrap_plots(List, nrow = 1)  
}

# Outer loop
cylinder <- unique(mtcars$cyl)
gears <- unique(mtcars$gear)
 # for the outer loop. 
pages <- lapply(gears, inner_loop)

wrap_plots(pages, ncol = 1) 


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to JiKe DevOps Community for programmer and developer-Open, Learning and Share

755k questions

754k answers

5 comments

53.3k users

...