Search code examples
rfunctionggplot2environmentscoping

Accessing multiple matched objects in a function without explicitly stating the names in the function call


I would like to develop a simple function that would enable me to save graphs of specific characteristics. For example, I'm running some analysis producing a set of histograms:

# Data  and Libs
data(mtcars); require(ggplot2)

# Graphs

## A
grph_a <- ggplot(data = mtcars) +
  geom_histogram(aes(mpg)) +
  ggtitle("MPG")

## B
grph_b <- ggplot(data = mtcars) +
  geom_histogram(aes(cyl)) +
  ggtitle("CYL")

Instead of writing ggsave command for each of those graphs I would like to do it via function. It makes sense as I will be repeating the same steps for a number of graphs across various similar projects. I would like for the function to do one thing:

  • For all the graphs that have a specific string in name run ggsave with a set parameters and save them to provide path.

Ideally, I would like for the function call to look like that

ExportGraphs(graphNamePhrase = "grph_", filesPath = "Somewhere/GaphsAndStuff/)

I don't want to be specifying more stuff.

Function

My function looks like that:

ExportGraphs <- function(graphNamePhrase = "grph_",
                         filesPath, objects = ls()) {

  # Check if required packages are available
  req_pkgs <- c("ggplot2","grid")
  ## Check if the package is loaded and load if needed
  for (i in 1:length(req_pkgs)) {
    pkg <- req_pkgs[i]
    if (length(grep(pkg, search())) == 0) {
      lapply(pkg, require, character.only = TRUE)
    }
  }

  # Create list of objects
  save_grphs <- grep(pattern = graphNamePhrase, x = objects,
                     ignore.case = TRUE, value = TRUE)

  # Create save loop
  for (i in 1:length(save_grphs)) {

    # Create file path
    fle_path <- paste0(filesPath, save_grphs[i], ".png")

    # Save file
    ggsave(filename = fle_path, plot = save_grphs[i],
           width = 7, height = 7, units = 'cm', scale = 2, dpi = 600)
  }

}

Problems

Obviously, the code:

  save_grphs <- grep(pattern = graphNamePhrase, x = objects,
                     ignore.case = TRUE, value = TRUE)

won't work as what is passed via the objects = ls() will be a string. My question is how can I get around it. Is there a way to use get on the parent frame from which the function is called? Not the easiest solution but I could search objects via string. Or can I run ls with grep in the function call and pass all matching objects?


Comments Follow-up

mget

I tried the solution with mget:

ExportGraphs <- function(graphNamePhrase = "grph_",
                         filesPath, objects = ls()) {

  # Check if required packages are available
  req_pkgs <- c("ggplot2","grid")
  ## Check if the package is loaded and load if needed
  for (i in 1:length(req_pkgs)) {
    pkg <- req_pkgs[i]
    if (length(grep(pkg, search())) == 0) {
      lapply(pkg, require, character.only = TRUE)
    }
  }

  # Create list of objects
  save_grphs <- grep(pattern = graphNamePhrase, x = objects,
                     ignore.case = TRUE, value = TRUE)
  save_grphs <- mget(objects[save_grphs])

  # Create save loop
  for (i in 1:length(save_grphs)) {

    # Create file path
    fle_path <- paste0(filesPath, save_grphs[i], ".png")

    # Save file
    ggsave(filename = fle_path, plot = save_grphs[[i]],
           width = 7, height = 7, units = 'cm', scale = 2, dpi = 600)
  }

}

But it seems that I would have to adjust the loop as subscription appears to be out of bounds:

Error in save_grphs[[i]] : subscript out of bounds
Called from: inherits(plot, "ggplot")

Solution

  • This works for me. There are many places to further optimize the function:

    ExportGraphs <- function(graphNamePhrase = "grph_",
                             filesPath, objects = ls()) {
    
      # Check if required packages are available
      req_pkgs <- c("ggplot2","grid")
      ## Check if the package is loaded and load if needed
      for (i in 1:length(req_pkgs)) {
        pkg <- req_pkgs[i]
        if (length(grep(pkg, search())) == 0) {
          lapply(pkg, require, character.only = TRUE)
        }
      }
    
      # Create list of objects
      index <- grep(pattern = graphNamePhrase, x = objects,
                         ignore.case = TRUE)
      save_grphs <- mget(objects[index])
      # Create save loop
      for (i in 1:length(save_grphs)) {
    
        # Create file path
        fle_path <- paste0(filesPath, objects[index][i], ".png")
    
        # Save file
        ggsave(filename = fle_path, plot = save_grphs[[i]],
               width = 7, height = 7, units = 'cm', scale = 2, dpi = 600)
      }
    
    }