0

I would like to create multiple plots using for loop setup. However my code does not work. Could anyone give me some guidance on this?

for i in 1:4 {
paste0("p_carb_",i) <- ggplot(mtcars%>% filter(carb==4), aes(x = wt, y = mpg, color = disp)) 
+  geom_point()
}
3
  • 2
    "Does not work" isn't very precise, but I suspect you are a victim of lazy evaluation. The solution is simple: use one of the apply family of functions (which force evaluation) rather than a for loop, which does not. More detail here. Also, paste0("p_carb_",i) creates a character string, which is not valid as the target of an assignment. Commented Jan 25, 2022 at 15:42
  • Is it a way to make paste0("p_carb_",i) a usable target to store the plots? Commented Jan 25, 2022 at 15:44
  • 2
    Objects are usually stored in lists. So initialize a list prior to a loop, e.g. plotlist <- list(). Then inside the loop set plotlist(i) = ggplot... But agree with Limey, better to use lapply. Moreover, the ggplot function on the left hand side contains no i terms, so all iterations will return the same result. Commented Jan 25, 2022 at 15:48

1 Answer 1

3

Perhaps this?

library(ggplot2)
library(dplyr)
ggs <- lapply(sort(unique(mtcars$carb)), function(crb) {
  ggplot(filter(mtcars, carb == crb), aes(x = wt, y = mpg, color = disp)) +
    geom_point()
})

This produces six plots, which when the first two are viewed side-by-side (calling ggs[[1]] and then ggs[[2]]), we see

enter image description here

An alternative might be to facet the data, as in

ggplot(mtcars, aes(x = wt, y = mpg, color = disp)) +
  facet_wrap(~ carb) +
  geom_point()

enter image description here

But the literal translation of your paste(..) <- ... code into something syntactically correct, we'd use an anti-pattern in R: assign:

for (crb in sort(unique(mtcars$carb))) {
  gg <- ggplot(filter(mtcars, carb == crb), aes(x = wt, y = mpg, color = disp)) +
    geom_point()
  assign(paste0("carb_", crb), gg)
}

Again, this is not the preferred/best-practices way of doing things. It is generally considered much better to keep like-things in a list for uniform/consistent processing of them.


Multiple IDs ... two ways:

  1. Nested lapply:

    carbs <- sort(unique(mtcars$carb))
    ggs <- lapply(carbs, function(crb) {
      gears <- subset(mtcars, carb == crb)$gear
      lapply(gears, function(gr) {
        ggplot(dplyr::filter(mtcars, carb == crb, gear == gr), aes(x = wt, y = mpg, color = disp)) +
          geom_point()
      })
    })
    

    Where ggs[[1]] is a list of lists. ggs[[1]][[1]] will be one plot.

  2. split list, one-deep:

    carbsgears <- split(mtcars, mtcars[,c("carb", "gear")], drop = TRUE)
    ggs <- lapply(carbsgears, function(dat) {
      ggplot(dat, aes(x = wt, y = mpg, color = disp)) + geom_point()
    })
    

    Here, ggs is a list only one-deep. The names are just concatenated strings of the two fields, so since we have mtcars$carb with values c(1,2,3,4,6,8) and mtcars$gear with values c(3,4,5), removing combinations without data we have names:

    names(ggs)
    #  [1] "1.3" "2.3" "3.3" "4.3" "1.4" "2.4" "4.4" "2.5" "4.5" "6.5" "8.5"
    

    where "1.3" is carb == 1 and gear == 3. When column names have dots in them, this might become ambiguous.

Sign up to request clarification or add additional context in comments.

12 Comments

Your answer opens a whole new world to me :D
Perhaps stick with faceting (again): ggplot(mtcars, aes(x = wt, y = mpg, color = disp)) + facet_grid(vs ~ carb) + geom_point() (where you might instead choose facet_grid(ID ~ ID2)).
You can also nest calls to lapply. That would handle OP's use case above. The choice between many separate plots and a single faceted plot is multi-dimensional. There is no absolute answer. The better option will change on a case-by-case basis and will depend on the data, the message you want to convey, the audience ... and personal preference.
Not to steal your thunder @Limey, but ... Stataq, see my edit, particularly with the split option.
@r2evans: I'm always happy when someone volunteers to do my work for me. Especially when it's someone as knowledgeable as yourself.
|

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.