Показать сюжет по аргументу с пакетом пэчворк

Я пытаюсь написать функцию с patchworkпакет, в котором графики отображаются в соответствии с аргументами, переданными в функцию. Я попробовал описанный ниже подход, установив для объектов, не переданных в функцию, значение NULL. Однако это работает только в том случае, если первый объект отличается от NULL. Какие-нибудь мысли?

# 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

Создано 07.06.2020 с помощью пакета REPEX (v0.3.0)

1 ответ

Решение

Поскольку вам нужно запустить один и тот же код для каждого элемента homeworld, вы можете перебирать его с помощью purrr::map (или lapply, если хочешь). Это возвращает список с элементом для каждой итерации, здесь содержащий график (например,p1, p2, так далее.). Этот список может быть reduce d (или Reduceг) итеративно комбинировать каждый элемент с +:

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"))

В качестве альтернативы вместо reduce(plots, `+`)вы могли бы написать wrap_plots(plots), используя wrap_plots()функция из лоскутного шитья, которая принимает список участков. Результаты такие же.

В более общем плане, прежде чем прибегать к пэчворку, вам следует подумать о фацетировании:

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"))

Обратите внимание, что при таком подходе вы получаете красивые метки в виде полос на панели, определяющие, какая планета какая планета.

Другие вопросы по тегам