7

I am trying to create 2 line plots.

But I noticed that using a for loop will generate two plots with y=mev2 (instead of a plot based on y=mev1 and another one based on y=mev2).

The code below shows the observation here.

mev1 <- c(1,3,7)
mev2 <- c(9,8,2)
Period <- c(1960, 1970, 1980)
df <- data.frame(Period, mev1, mev2)

library(ggplot2)
# Method 1: Creating plot1 and plot2 without using "for" loop (hard-code)
plot1 <- ggplot(data = df, aes(x=Period, y=unlist(as.list(df[2])))) + geom_line()
plot2 <- ggplot(data = df, aes(x=Period, y=unlist(as.list(df[3])))) + geom_line()

# Method 2: Creating plot1 and plot2 using "for" loop
for (i in 1:2) {
   y_var <- unlist(as.list(df[i+1]))
   assign(paste("plot", i, sep = ""), ggplot(data = df, aes(x=Period, y=y_var)) + geom_line())
}

Seems like this is due to some ggplot()'s way of working that I am not aware of.

Question:

  • If I want to use Method 2, how should I modify the logic?
  • People said that using assign() is not an "R-style", so I wonder what's an alternate way to do this? Say, using list?
1
  • The way to do it - transform wide to long and plot ggplot(reshape2::melt(df, "Period"), aes(Period, value, color = variable)) + geom_line() Commented Dec 13, 2019 at 10:07

4 Answers 4

3

One possible answer with no tidyverse command added is :

library(ggplot2)

y_var <- colnames(df)
for (i in 1:2) {
  assign(paste("plot", i, sep = ""),
         ggplot(data = df, aes_string(x=y_var[1], y=y_var[1 + i])) +
           geom_line())
}

plot1
plot2

You may use aes_string. I hope it helps.

EDIT 1

If you want to stock your plot in a list, you can use this :

Initialize your list :

n <- 2 # number of plots
list_plot <- vector(mode = "list", length = n)
names(list_plot) <- paste("plot", 1:n)

Fill it :

for (i in 1:2) {
  list_plot[[i]] <- ggplot(data = df, aes_string(x=y_var[1], y=y_var[1 + i])) +
           geom_line()
}

Display :

list_plot[[1]]
list_plot[[2]]
Sign up to request clarification or add additional context in comments.

1 Comment

It seems you're using functions from external packages, it should be better put those packages in your answer.
2

For lines in different "plots", you can simplify it with facet_wrap():

library(tidyverse)
df %>% 
gather(variable, value, -c(Period)) %>% # wide to long format
ggplot(aes(Period, value)) + geom_line() + facet_wrap(vars(variable))

enter image description here

You can also put it in a loop if necessary and store the results in a list:

# empty list
listed <- list()
# fill the list with the plots
for (i in c(2:3)){
     listed[[i-1]] <- df[,-i]  %>%
                      gather(variable, value, -c(Period)) %>% 
                      ggplot(aes(Period, value)) + geom_line()
                 }

# to get the plots
listed[[1]]
listed[[2]]

1 Comment

In your loop approach your y axis label is just "value" instead of "mev1" and "mev2". And the list elements are not named. For this reason I would go with Rémi Coulauds approach.
1

Why do you want 2 separate plots? ggplots way to do this would be to get data in long format and then plot.

library(tidyverse)

df %>%
  pivot_longer(cols = -Period) %>%
  ggplot() + aes(Period, value, color = name) + geom_line()

enter image description here

Comments

0

Here is an alternative approach using a function and lapply. I recognize that you asked how to solve this using a loop. Still, I think it might be useful to consider this approach.

library(ggplot2)
mev1 <- c(1,3,7)
mev2 <- c(9,8,2)
Period <- c(1960, 1970, 1980)
df <- data.frame(Period, mev1, mev2)

myplot <- function(yvar){
  plot <- ggplot(df, aes(Period, !!sym(yvar))) + geom_line()
  return(plot)
}

colnames <- c("mev1","mev2")
list <- lapply(colnames, myplot)
names(list) <- paste0("plot_", colnames)
# Alternativing naming: names(list) <- paste0("plot", 1:2)   

Using this approach you can easily apply your plot function to whatever columns you like. You can specify the columns by name, which may be preferrabe to specifying by position. Plots are saved in a list, and they are named afterwards using the names attribute. In my example I named the plots plot_mev1 and plot_mev2. But you can easily adjust to some other naming. E.g. write names(list) <- paste0("plot", 1:2) to get plot1 and plot2.

Note that I used !!sym() in the ggplot call. This is essentally an alternative to aes_string which was used in the answer of Rémi Coulaud. In this way ggplot understands even in the context of a function or in the context of a loop that "mev1" is a column of your dataset and not just a text string

Comments

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.