1

I have the problem which I solved but I think my solution is to involved and unproductive when dealing with more variables. I want to write a function which combine three graphs :

  1. Numbers from standard normal distribution

  2. Numbers from standard uniform distribution

  3. Numbers from standard exponential distribution

The length of the samples can be specified. Also in output I want to have one additional input named plot_types. By default it's combining all graphs. But user can specify also exactly which plots should be included. For example if plot_types==c('Norm.plot','Unif.plot') R should only plot numbers from normal and uniform distribution omitting numbers from exponential distribution.

My work so far

I wrote code following :

library(ggplot2)
library(gridExtra)

visual=function(num,plot_types='all'){
  Norm.plot <- ggplot()+
    geom_line(aes(x=1:num,y=rnorm(num)))+
    labs(title='Norm plot',x=NULL,y=NULL)+ 
    theme(plot.title = element_text(hjust = 0.5))
  
  Unif.plot <- ggplot()+
    geom_line(aes(x=1:num,y=runif(num)))+
    labs(title='Unif plot',x=NULL,y=NULL)+ 
    theme(plot.title = element_text(hjust = 0.5))
  
  Exp.plot <- ggplot()+
    geom_line(aes(x=1:num,y=rexp(num)))+
    labs(title='Exp plot',x=NULL,y=NULL)+ 
    theme(plot.title = element_text(hjust = 0.5))
  
  if (plot_types=='all'){grid.arrange(Norm.plot,Exp.plot, Unif.plot,ncol=2)}
  else if (plot_types==c('Norm.plot','Unif.plot') || 
           plot_types==c('Unif.plot','Norm.plot')){grid.arrange(Norm.plot, Unif.plot)}
  else if (plot_types==c('Norm.plot','Exp.plot') ||
           plot_types==c('Exp.plot','Norm.plot')){grid.arrange(Norm.plot, Exp.plot)}
  else if (plot_types==c('Unif.plot','Exp.plot') || 
           plot_types==c('Exp.plot','Unif.plot')){grid.arrange(Exp.plot, Unif.plot)}
}

visual(50,plot_types = c('Norm.plot','Unif.plot'))

enter image description here

The code above has several problems. The first one is that it has a lot of loops so it's very unproductive. Also it would be very problematic when trying to extend it to higher numbers of variables.

Do you have any ideas how can I omit using so many loops ?

2 Answers 2

5

See if this suits your needs

visual.new <- function(num, plot_types = 'all') {
  # define a data frame for all the results
  data <- data.frame(x = seq(1, num),
                     y.norm = rnorm(num),
                     y.unif = runif(num),
                     y.exp = rexp(num))

  # define a base ggplot object
  gg <- ggplot(data, aes(x = x)) + 
    geom_line() +
    theme(plot.title = element_text(hjust = 0.5),
          axis.title = element_blank())

  # define a list of plots
  plot.list <- list(gg + aes(y = y.norm) + ggtitle("Norm plot"),
                    gg + aes(y = y.unif) + ggtitle("Unif plot"),
                    gg + aes(y = y.exp) + ggtitle("Exp plot"))

  # initial default: do not show any plot
  show.plot <- c(FALSE, FALSE, FALSE)

  # determine whether to show all / any plot based on plot_types value
  if('all' %in% plot_types) {
    show.plot <- c(TRUE, TRUE, TRUE)
  } else {
    if('Norm.plot' %in% plot_types) show.plot[1] <- TRUE
    if('Unif.plot' %in% plot_types) show.plot[2] <- TRUE
    if('Exp.plot' %in% plot_types) show.plot[3] <- TRUE
  }
  
  if(sum(show.plot) == 0) {
    message("Invalid plot_types parameter entered. No plot shown.")
  } else {
    cowplot::plot_grid(plotlist = plot.list[show.plot])
  }
}

Test

visual.new(50) # 3 plots
visual.new(30, c('Norm.plot', 'Exp.plot')) # 2 plots
visual.new(80, c('Exp.plot', "Unif.plot", "Norm.plot")) # 3 plots, in norm-unif-exp order
visual.new(25, 'Unif.plot') # 1 plot
visual.new(40, "something") # no plot, because plot_types value is invalid

(I used plot_grid from the cowplot package, rather than gridExtra's grid.arrange, because the former can accept a list of ggplot objects, which is useful here.)

Sign up to request clarification or add additional context in comments.

Comments

2

Pointing out to the fantastic solution of @Z.Lin you can also use wrap_plots() function from patchwork. I took the smart function from amazing @Z.Lin and added a slight change. Here the code:

#Code
visual.new <- function(num, plot_types = 'all') {
  # define a data frame for all the results
  data <- data.frame(x = seq(1, num),
                     y.norm = rnorm(num),
                     y.unif = runif(num),
                     y.exp = rexp(num))
  
  # define a base ggplot object
  gg <- ggplot(data, aes(x = x)) + 
    geom_line() +
    theme(plot.title = element_text(hjust = 0.5),
          axis.title = element_blank())
  
  # define a list of plots
  plot.list <- list(gg + aes(y = y.norm) + ggtitle("Norm plot"),
                    gg + aes(y = y.unif) + ggtitle("Unif plot"),
                    gg + aes(y = y.exp) + ggtitle("Exp plot"))
  
  # initial default: do not show any plot
  show.plot <- c(FALSE, FALSE, FALSE)
  
  # determine whether to show all / any plot based on plot_types value
  if('all' %in% plot_types) {
    show.plot <- c(TRUE, TRUE, TRUE)
  } else {
    if('Norm.plot' %in% plot_types) show.plot[1] <- TRUE
    if('Unif.plot' %in% plot_types) show.plot[2] <- TRUE
    if('Exp.plot' %in% plot_types) show.plot[3] <- TRUE
  }
  
  if(sum(show.plot) == 0) {
    message("Invalid plot_types parameter entered. No plot shown.")
  } else {
    patchwork::wrap_plots(plot.list[show.plot])
  }
}

Some test (Using fantastic code from @Z.Lin):

visual.new(80, c('Exp.plot', "Unif.plot", "Norm.plot"))

Output:

enter image description here

2 Comments

Why do you reckon that wrap_plots is better than plot_grid ? ;))
@John Hi John, I am just proposing as an option. wrap_plots() is like an improvement for plot_grid() and it has more advanced features. So the OP can have different options in case of any modification :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.