问题
I'm using the grid
and grid.Extra
packages to have multiple ggplots on one grid, however, I need to make 28 ggplots in total, each one with a different y variable. Obviously the loop to make and save them as a variable is simple enough, but I need to save each plot as a different object. Currently I just have 28 of the same plot code with only the axis changed, but I know that there's a better way.
My minimum code example:
dattn <- ggplot()+
geom_boxplot(
data = dat,
mapping = aes(x = site, y = tn)
)
My current loop heading (not sure if it makes any difference):
for (i in dat[, 10:12])
I need to switch the y value to tp
and tss
and save those ggplots as dattp
and datss
. The columns of the variables are 10, 11, and 12 in that order.
While doing research for this problem, I came across this question Dynamically naming variables in a loop to create 30 ggplots which is very similar to my problem, however it uses pipe functions and I have no idea how to use them whereas I have some idea of using the for loop. If anyone thinks that would work better I'd be grateful to be told so.
I have also tried the code paste("dat", i, sep="") <- ggplot...
specifically from the question Save ggplot objects in loop but that gives me an error message target of assignment expands to non-language object
and again uses pipe functions.
I'll update my question with any more necessary info as it's needed. Thanks in advance.
The reason I requested help using for loops is because I also need to subset my data based on location ("farm" in the data) and I figured once I could change the text of a loop I would be able to change how I subset the data and use nested loops.
Data using dput(head(dat))
:
structure(list(year = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = c("2018",
"2019"), class = "factor"), month = structure(c(4L, 5L, 5L, 6L,
7L, 7L), .Label = c("1", "2", "3", "4", "5", "6", "7", "9", "10",
"11", "12"), class = "factor"), day = c(24L, 18L, 30L, 25L, 6L,
19L), farm = structure(c(2L, 2L, 2L, 2L, 2L, 2L), .Label = c("ARR",
"Car", "CAR", "Mur", "Muz", "PBR", "Pre", "PRE", "Sch", "SCH",
"Sim", "SIM", "STU"), class = "factor"), treat = structure(c(1L,
1L, 1L, 1L, 1L, 1L), .Label = c("CC", "Con"), class = "factor"),
tss = c(1955, 3540, 4893.3, 410, 3357.5, 1836), tn = c(17,
32.8, 7.26, 5.91, 16.1, 16.7), tp = c(4.35, 10, 49.5, 3.57,
9.79, 11.1), dis = c(8178, 184232, 401364, 1113947, 10728,
21869), tss.1 = c(1.576347171, 64.30227415, 193.6414217,
45.03046056, 3.551344392, 3.958763937), tn.1 = c(0.013707367,
0.595795082, 0.28729829, 0.649097614, 0.017029529, 0.036008365
), tp.1 = c(0.003507473, 0.181644842, 1.958851976, 0.392094498,
0.010355223, 0.023933704), site = structure(c(1L, 1L, 1L,
1L, 1L, 1L), .Label = c("Car CC", "Mur CC", "Muz CC", "Pre CC",
"Sch CC", "Sim CC", "CAR CC", "ARR CC", "PBR CC", "PRE CC",
"STU CC", "Car Con", "Mur Con", "Muz Con", "Pre Con", "Sch Con",
"Sim Con", "SCH Con", "PRE Con", "ARR Con", "PBR Con", "SIM Con",
"STU Con"), class = "factor")), row.names = c(NA, 6L), class = "data.frame")
And some formatting code:
# Set variables as factors
cols <- c("year", "month")
dat[cols] <- lapply(dat[cols], as.factor)
# Set dat$site to combine farm and treat
dat$site <- paste(dat$farm, dat$treat)
# Sets the sites in order instead of aphabetically.
# unique() is needed else Error: duplicates
dat$site <- factor(dat$site, levels =unique(dat$site))
I'm not sure if that helps but it was what was suggested to me.
回答1:
An option is map
. Loop through the names of columns as a string and pass it on aes_string
library(tidyverse)
v1 <- c('tn', 'tp', 'tss')
out <- map(v1, ~
ggplot()+
geom_boxplot(
data = dat,
mapping = aes_string(x = "site", y =.x) #or
# mapping = aes(x, !! rlang::sym(.x))
))
回答2:
Or you can use .data[[]]
to extract desired column names
library(tidyverse)
# define plotting function
plot_gg <- function(dat, x_var, y_var) {
dattn <- ggplot() +
geom_boxplot(
data = dat,
mapping = aes(x = .data[[x_var]], y = .data[[y_var]])
) +
labs(x = x_var, y = y_var)
return(dattn)
}
# save plots in a list
plot_list <- c('tn', 'tp', 'tss') %>%
map(~ plot_gg(dat, 'site', .x))
plot_list
#> [[1]]
#>
#> [[2]]
#>
#> [[3]]
Created on 2019-08-05 by the reprex package (v0.3.0)
回答3:
Consider reshaping your wide data to long format and then plot with facet_wrap
or facet_grid
without any complex loops, mapping, or saving many plots for gridExtra::grid
. Below demonstrates with random, seeded data.
Data (assuming below structure mirrors OP's actual data)
set.seed(852019)
### DATA BUILD
random_df <- data.frame(
site = sample(c("sas", "stata", "spss", "python", "r", "julia"), 500, replace=TRUE),
var2 = NA,
var3 = NA,
var4 = NA,
var5 = NA,
var6 = NA,
var7 = NA,
var8 = NA,
var9 = NA,
tn = rnorm(500),
tss = rnorm(500),
tp = rnorm(500)
)
head(random_df)
# site var2 var3 var4 var5 var6 var7 var8 var9 tn tss tp
# 1 stata NA NA NA NA NA NA NA NA 2.0237416 -1.30919981 -1.71918905
# 2 r NA NA NA NA NA NA NA NA 0.6052126 -0.27231149 0.18739618
# 3 r NA NA NA NA NA NA NA NA 1.3270657 -0.70308896 0.04996251
# 4 sas NA NA NA NA NA NA NA NA -0.8690220 0.09934931 -0.12513907
# 5 julia NA NA NA NA NA NA NA NA -1.8871174 0.08761820 -0.45409606
# 6 sas NA NA NA NA NA NA NA NA 0.3205017 -0.61696052 0.32586570
Plot
# RESHAPE WIDE TO LONG
yvars <- c("tn", "tss", "tp")
long_df <- reshape(random_df, varying = yvars, v.names = "y_value",
times = yvars, timevar = "y_var",
idvar = c("site"), drop = c(2:9),
new.row.names = 1:1E4, direction = "long")
head(long_df)
# site y_var y_value
# 1 stata tn 2.0237416
# 2 r tn 0.6052126
# 3 r tn 1.3270657
# 4 sas tn -0.8690220
# 5 julia tn -1.8871174
# 6 sas tn 0.3205017
# BOXPLOT WITH FACET
ggplot() + geom_boxplot(data = long_df, mapping = aes(x = site, y = y_value)) +
facet_wrap(~y_var)
Should you want separate graphs, still consider the long format and use by
off the y_var indicator column to build a list of plots. Then plot with a gridExtra::grid.arrange
:
plot_list <- by(long_df, long_df$y_var, function(sub) {
ggplot() + geom_boxplot(data = sub, mapping = aes(x = site, y = y_value)) +
ggtitle(sub$y_var[[1]])
})
do.call(grid.arrange, args=list(grobs=plot_list, nrow = 1))
Using OP's Sample Data
# RESHAPE WIDE TO LONG
yvars <- c("tn.1", "tss.1", "tp.1")
long_df <- reshape(df, varying=yvars, v.names="y_value",
times = yvars, timevar = "y_var",
idvar = c("site"), drop=c(2:9),
new.row.names = 1:1E4, direction = "long")
long_df$y_var <- gsub(".1", "", long_df$y_var)
# GRID ARRANGE PLOT
plot_list <- by(long_df, long_df$y_var, function(sub) {
ggplot() + geom_boxplot(data = sub, mapping = aes(x = site, y = y_value)) +
ggtitle(sub$y_var[[1]])
})
do.call(grid.arrange, args=list(grobs=plot_list, nrow = 1))
来源:https://stackoverflow.com/questions/57360989/how-do-i-create-a-for-loop-in-r-that-assigns-ggplots-as-an-object-without-savi