Cameron So
Cameron So

Reputation: 149

Store multiple plots into a list using a function

I am trying to store multiple plots produced by ggplot2 into a list.

I am attempting to use the list function suggested in a previous thread, however I am having difficulty creating my own function to meet my needs.

First, I split a dataframe based on a factor into a list with the following code:

heatlist.germ <- split(heatlist.germ, f=as.factor(heatlist.germ$plot))

Afterwhich, I attempt to create a list function that I can later use lapply with.

plot_data_fcn <- function (heatlist.germ) {
  ggplot(heatlist.germ[[i]], aes(x=posX, y=posY, fill=germ_bin)) + 
    geom_tile(aes(fill=germ_bin)) + 
    geom_text(aes(label=germ_bin)) +
    scale_fill_gradient(low = "gray90", high="darkolivegreen4") +
    ggtitle(plot) +
    scale_x_continuous("Position X", breaks=seq(1,30)) +
    scale_y_continuous("Position Y (REVERSED)", breaks=seq(1,20))
  }

heatlist.test <- lapply(heatlist.germ[[i]], plot_data_fcn)

Two main things I am trying to accomplish:

  1. Store the 12 ggplots (hence 12 factors of plot) in a list.
  2. Create a title called "Plot [i] Germination".

Any help would be appreciated.

Upvotes: 2

Views: 1113

Answers (1)

r2evans
r2evans

Reputation: 161155

I don't have your data, so I'll simplify the plotting mechanism.

The first problem is that you should not use your [[i]] referencing in your function. Just have your function deal with data as-is, it really doesn't know that its argument is (in another environment) an element with a list. It knows just the object itself.

# a simple plot function
myfunc <- function(x) ggplot(x, aes_string(names(x)[1], names(x)[2])) + geom_point()
# a list of frames, nothing fancy here
datalist <- replicate(3, mtcars, simplify = FALSE)
# just call it ...
myplots <- lapply(datalist, myfunc)
class(myplots[[1]])
# [1] "gg"     "ggplot"

When myfunc is called, its argument x is just a data.frame, the function has no idea that x is the first (or second or third) frame in a list of frames.

If you want to include the nth frame with an index indicating which element it is, this is in my view "zipping" data together, so I suggest Map. (You can also use purrr::imap or related tidyverse functions.)

myfunc2 <- function(x, title = "") ggplot(x, aes_string(names(x)[1], names(x)[2])) + geom_point() + labs(title = title)
myplots <- Map(myfunc2, datalist, sprintf("Plot number %s", seq_along(datalist)))
class(myplots[[1]])
# [1] "gg"     "ggplot"

To understand how Map relates to lapply, then understand that lapply(datalist, myfunc) is "unrolled" to something like:

myfunc(datalist[[1]])
myfunc(datalist[[2]])
myfunc(datalist[[3]])

With Map, however, it takes one function that must accept one or more arguments in each call. With that, Map accepts as many lists (or vectors) as the function accepts arguments. The two functions are synonomously

lapply(datalist, myfunc) # data first,     function second
Map(myfunc, datalist)    # function first, data second

and a more complicated call unrolls like thus:

titles <- sprintf("Plot number %d", seq_along(datalist)) # "Plot number 1", ...
Map(myfunc2, datalist, titles)
# equivalent to
myfunc2(datalist[[1]], titles[[1]])
myfunc2(datalist[[2]], titles[[2]])
myfunc2(datalist[[3]], titles[[3]])

It doesn't really matter if each of the arguments is a true list (as in datalist) or a vector (as in titles), as long as they are the same length (or length 1).

Upvotes: 5

Related Questions