Alberson Miranda
Alberson Miranda

Reputation: 1430

Show plot according to argument with patchwork package

I'm trying to write a function with patchwork package in which plots are displayed according to the arguments passed in the function. I tried the approach below, setting objects not passed in the function as NULL. However, it only works if the first object is different from NULL. Any thoughts?

# 1. PACKAGES

library(tidyverse)
library(patchwork)

# 2. DATA

data = starwars

# 3. FUNCTION

plot_people = function (homeworld = c("Tatooine", "Naboo", "Alderaan")) {

  p1 = if (is.element("Tatooine", homeworld)) {

    data %>%
    filter(homeworld == "Tatooine") %>%
    ggplot(aes(x = mass, y = height,
               label = ifelse(species == "Human", name, NA))) +
    geom_point() +
    geom_label()

  } else {

    NULL
  }

  p2 = if (is.element("Naboo", homeworld)) {

    data %>%
      filter(homeworld == "Naboo") %>%
      ggplot(aes(x = mass, y = height,
                 label = ifelse(species == "Human", name, NA))) +
      geom_point() +
      geom_label()

  } else {

    NULL
  }

  p3 = if (is.element("Alderaan", homeworld)) {

    data %>%
      filter(homeworld == "Alderaan") %>%
      ggplot(aes(x = mass, y = height,
                 label = ifelse(species == "Human", name, NA))) +
      geom_point() +
      geom_label()

  } else {

    NULL
  }

  # how to write this line in order to plot only objects in homeworld argument?
  p1 + p2 + p3

}

# 4. RESULTS

plot_people(homeworld = c("Naboo", "Tatooine"))

plot_people(homeworld = c("Naboo", "Alderaan"))
#> NULL

Created on 2020-06-07 by the reprex package (v0.3.0)

Upvotes: 1

Views: 260

Answers (1)

alistaire
alistaire

Reputation: 43344

Since you need to run the same code for each element of homeworld, you can iterate across it with purrr::map (or lapply, if you prefer). This returns a list with an element for each iteration, here containing a plot (like p1, p2, etc.). This list can be reduced (or Reduced) to iteratively combine each element with +:

library(tidyverse)
library(patchwork)

plot_people = function (homeworld = c("Tatooine", "Naboo", "Alderaan")) {

    plots <- map(homeworld, function(hw){
        starwars %>%
            filter(homeworld == hw) %>%
            ggplot(aes(x = mass, y = height,
                       label = ifelse(species == "Human", name, NA))) +
            geom_point() +
            geom_label()
    })

    reduce(plots, `+`)
}

plot_people(homeworld = c("Naboo", "Tatooine"))

plot_people(homeworld = c("Naboo", "Alderaan"))

Alternatively, instead of reduce(plots, `+`), you could write wrap_plots(plots), using the wrap_plots() function from patchwork which accepts a list of plots. The results are the same.

More generally, you should consider facetting before resorting to patchwork:

library(tidyverse)

plot_people = function (homeworld = c("Tatooine", "Naboo", "Alderaan")) {
    starwars %>%
        filter(homeworld %in% !!homeworld) %>%
        ggplot(aes(x = mass, y = height,
                   label = ifelse(species == "Human", name, NA))) +
        geom_point() +
        geom_label() + 
        facet_wrap(~homeworld)
}

plot_people(homeworld = c("Naboo", "Tatooine"))

plot_people(homeworld = c("Naboo", "Alderaan"))

Note you get nice panel strip labels identifying which planet is which for free with this approach.

Upvotes: 1

Related Questions