Generating Names Iteratively in R for Storing Plots

Generating names iteratively in R for storing plots

If you want a "vector" of plot objects, the easiest way is probably to store them in a list. Use paste() to create a name for your plot and then add it to the list:

# Create a list to hold the plot objects.
pltList <- list()

for( i in 2:15 ){

# Get data, perform analysis, ect.

# Create plot name.
pltName <- paste( 'a', i, sep = '' )

# Store a plot in the list using the name as an index.
# Note that the plotting function used must return an *object*.
# Functions from the `graphics` package, such as `plot`, do not return objects.
pltList[[ pltName ]] <- some_plotting_function()

}

If you didn't want to store the plots in a list and literally wanted to create a new object that had the name contained in pltName, then you could use assign():

# Use assign to create a new object in the Global Environment
# that gets it's name from the value of pltName and it's contents
# from the results of plot()
assign( pltName, plot(), envir = .GlobalEnv )

rePost: Generating names iteratively in R for storing plots (2009)

In the original question you referenced and my answer to it, plot() was used as an abstract placeholder for a plotting function that returned an object and not a literal call to the R function plot. Most functions that return graphics objects are based on the 'grid' package such as xyplot from the lattice package or qplot from ggplot2.

Sorry for the confusion, I should have made this point clear in my answer, but I did not as the asker of the question was already aware of it.

Base graphics functions such as hist and plot render directly to output and do not return any objects that can be used to recreate the plot at a later time which is why you are ending up with a list of length zero.

In order to use this approach you will have to trade usage of the hist function for something that returns an object. Using ggplot2, you could do something like the following in your loop:

# Don't call your data variable 'data' or ggplot will confuse it with the
# `data` function and suffer an error.
h <- qplot(x = plot_data)

pltName <- paste('a', formatC(i, width=2, flag='0'), sep='')
pltList[[ pltName ]] <- h

I have edited my answer to the previous question to make it clear that the use of plot() in my example is not an actual call to the R function of the same name.

Create different plots based on file names / condition in R

Okay. To start with, here's some dummy data that matches your description

# vector with file names, and list of data frames for each file
file_list <- paste0('loc', 1:10)
flist <- lapply(1:10, function(dummy) data.frame(x=runif(6), y3=runif(6), y4=runif(6)))

# file groups to plot
g1 <- "loc5"
g2 <- c("loc1","loc4","loc10")

Here's how I would solve the problem

# first, add a column to each data frame with the file name
for(i in seq_along(flist)) flist[[i]]$file <- file_list[i]

# now a function that extracts data for a given group to a single data.frame
# and plots x vs a given y variable
library(ggplot2)

plot_group <- function(g, yvar) {
plot_data <- do.call(rbind, flist[file_list %in% g])

ggplot(plot_data, aes_string(x='x', y=yvar, color='file')) +
geom_point() + theme_classic()
}

plot_group(g2, 'y4') gives you:

Sample Image

Saving plots made in a for loop

As the error message states ggplot expects a data frame as input while you provide x_i which is a numeric vector. To fix that just make the vector a column in a data frame. To store all the plots create a list and on each iteration add the ggplot object to the list. you can then use the list as input to your lattice.

library(ggplot2)
plots <- list()
for(i in 1:4) {

x_i = data.frame("V1" = rnorm(n = 10^(1+i)))

plots[[i]] <- ggplot(data = x_i, aes(x = V1)) +
geom_histogram(aes(y = ..density..), binwidth = .05, col = "blue") +
stat_function(fun = dnorm, args = list(mean = 0, sd = 1), col = "Red")

}

For-Loop: getting object names into plot titles and file names

You can use main= argument of plot.igraph:

list_of_graphs <- c("V_turn1", "V_turn2", "V_turn3", "V_turn4")
list_of_titles <- c("This is Turn 1","This is Turn 2","This is Turn 3","This is Turn 4")
lapply(seq_along(list_of_graphs),
function(i) plot(get(list_of_graphs[i]),
layout=layout.old,
main=list_of_titles[i]))

Of course if you have a certain pattern for the titles , you can use list_of_graphes to create list_of_titles. For example , here I remove the first 2 letters from graph name to create graph title:

list_of_titles <- paste('This is',gsub('V_','',list_of_graphs))

How to loop through columns in R to create plots?

you can also use the tidyverse. Loop through a vector of grouping variable names with map. On every iteration, you can evaluate !!sym(variable) the variable name to group_by. Alternatively, we can use across(all_of()), wihch can take strings directly as column names. The rest of the code is pretty much the same you used.

library(dplyr)
library(purrr)

groups <- c('age', 'gender', 'income')

## with !!(sym(.x))

map(groups, ~
df %>% group_by(!!sym(.x)) %>%
summarise(n = n()) %>%
mutate(prop = n/sum(n)) %>%
ggplot(aes(y = prop, x = i)) +
geom_col()
)

## with across(all_of())

map(groups, ~
df %>% group_by(across(all_of(.x))) %>%
summarise(n = n()) %>%
mutate(prop = n/sum(n)) %>%
ggplot(aes(y = prop, x = i)) +
geom_col()
)

If you want to use a for loop:

groups <- c('age', 'gender', 'income')

for (i in groups){
df %>% group_by(!!sym(i)) %>%
summarise(n = n()) %>%
mutate(prop = n/sum(n)) %>%
ggplot(aes(y = prop, x = i)) +
geom_col()
}

Storing ggplot objects in a list from within loop in R

In addition to the other excellent answer, here’s a solution that uses “normal”-looking evaluation rather than eval. Since for loops have no separate variable scope (i.e. they are performed in the current environment) we need to use local to wrap the for block; in addition, we need to make i a local variable — which we can do by re-assigning it to its own name1:

myplots <- vector('list', ncol(data2))

for (i in seq_along(data2)) {
message(i)
myplots[[i]] <- local({
i <- i
p1 <- ggplot(data2, aes(x = data2[[i]])) +
geom_histogram(fill = "lightgreen") +
xlab(colnames(data2)[i])
print(p1)
})
}

However, an altogether cleaner way is to forego the for loop entirely and use list functions to build the result. This works in several possible ways. The following is the easiest in my opinion:

plot_data_column = function (data, column) {
ggplot(data, aes_string(x = column)) +
geom_histogram(fill = "lightgreen") +
xlab(column)
}

myplots <- lapply(colnames(data2), plot_data_column, data = data2)

This has several advantages: it’s simpler, and it won’t clutter the environment (with the loop variable i).


1 This might seem confusing: why does i <- i have any effect at all? — Because by performing the assignment we create a new, local variable with the same name as the variable in the outer scope. We could equally have used a different name, e.g. local_i <- i.

How to build a list of two strings by iteratively combining variable names stored in a data frame so that there is one from each column

Here's a base solution that is general (scales up to more columns). Calling your data dd:

# Omit missing values - data isn't really rectangular
# and rows seem to have no meaning
# so a list is an appropriate structure
dd_list = lapply(dd, na.omit)

# generate all pairs of "columns" (now list items)
col_pairs = combn(seq_along(dd_list), 2)

# for each pair, use `expand.grid` to generate all combinations
# since the wanted result is a list of vectors, not a data frame
# we strip the names and convert to matrix
result = apply(col_pairs, MARGIN = 2, FUN = function(x) {
as.matrix(unname(do.call(expand.grid, args = dd_list[x])))
})

# bind the matrices together - this seems like a nice result to work with
result = do.call(rbind, result)
result
# [,1] [,2]
# [1,] "Combined_t2|t1_lag8" "mean_RT_200_all"
# [2,] "Combined_abmag_t2_lag8_minus_lag3" "mean_RT_200_all"
# [3,] "Combined_abmag_t2_1.0_minus_lag3" "mean_RT_200_all"
# [4,] "Combined_abwidth" "mean_RT_200_all"
# [5,] "Combined_abdepth" "mean_RT_200_all"
# [6,] "Combined_lag3vslag8_residuals" "mean_RT_200_all"
# [7,] "Combined_lag3vslag8_stdrdized_residuals" "mean_RT_200_all"
# [8,] "Combined_t2|t1_lag8" "mean_RT_1000_all"
# ...


# but if you really want a list we can `split` the matrix into
# individual rows:
split(result, 1:nrow(result))
# $`1`
# [1] "Combined_t2|t1_lag8" "mean_RT_200_all"
#
# $`2`
# [1] "Combined_abmag_t2_lag8_minus_lag3" "mean_RT_200_all"
#
# $`3`
# [1] "Combined_abmag_t2_1.0_minus_lag3" "mean_RT_200_all"
# ...

The above is fancy (and scalable) way to do it - it's pretty much equivalent to this quick and dirty method:

result = rbind(
expand.grid(x = na.omit(dd$AB), y = na.omit(dd$PRP)),
expand.grid(x = na.omit(dd$AB), y = na.omit(dd$DUAL)),
expand.grid(x = na.omit(dd$PRP), y = na.omit(dd$DUAL))
)

split(as.matrix(unname(result)), 1:nrow(result))

Add plot to list using a for loop

This is the solution I came up with. I created a name for the graph based off the column name and used this to add to the list.

# Plot the data and add to list
bplot_list <- list()
for (data in dataList){
chemElement <- colnames(data[5])
p <-
data %>%
ggplot(aes_string(x='Month', y=data[,5])) +
geom_boxplot() +
theme_classic() +
labs(y= chemElement) +
scale_x_discrete()
bplot_list[[chemElement]] <- p

}



Related Topics



Leave a reply



Submit