Как использовать purrr::pmap для вызова определяемой пользователем функции в R

#r #list #user-defined-functions #purrr

Вопрос:

Я хочу вызвать функцию wrap_vr с помощью map или pmap purrr библиотеки.

Во-первых, я не понимаю, почему я должен использовать df$v1 и df$v2 передавать переменные в функцию. Почему нет v1 и v2 только?

Во-вторых, в чем моя ошибка, когда я пытался использовать pmap ?

 library(tidyverse)

df <- tibble(cy = c('a', 'a', 'b', 'b'),
             date = c(1,2,1,2),
             v1 = c(1,2,3,1),
             v2 = c(5,3,2,1))

wrap_vr <- function(df, vr, tit, ylab){
  ggplot(data = df, aes(date, all_of(vr)))  
    geom_line(color = "steelblue", size = 1)  
    labs(title =  tit,
         y = ylab, x = "")  
    facet_wrap(~ cy)
}

wrap_vr(df, df$v1, "title_1", "ylabel_1")


wrap_vr(df, df$v2, "title_2", "ylabel_2")


list_1 <- list(df, list(df$v1, df$v2), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))

# This gives an error
pmap(list_1, ~wrap_vr(.x))
#> Error: Element 2 of `.l` must have length 1 or 4, not 2
Created on 2021-07-06 by the reprex package (v2.0.0)
 

Комментарии:

1. Я полагаю, что проблема с purrr заключается в том, что ваш набор данных читается как список. Попробуй list(list(df), ... вместо list(df, ... этого . Для передачи переменных в функцию на основе ggplot2 вы можете оказаться в месте, где было бы полезно прочитать о программировании с помощью tidyverse/ggplot2 и аккуратной оценки. Я чаще всего использую строки при передаче переменных в функции на основе ggplot2 и поэтому использую .data местоимение. Нравится aes(x = data, y = .data[[vr]]) , а затем передайте строки wrap_var(df, "v1", "t1", "y1") .

2. Спасибо. Использование .data местоимения привело к этому, но превращение df в list(df) не произошло.

3. @sbac, ваш df является константой для всех итераций, поэтому его следует передавать за пределы списка.

4. @sbac Да, я пропустил один шаг. Затем сделайте pmap(list_1, wrap_vr) , чтобы передать вещи из списка в функцию (нет .x ). Я согласен с другими, что если df будет исправлен, вы сможете сделать его частью своей функции.

5. Рори правильно это показал. Однако также рекомендуется, чтобы другие имена столбцов не использовались в качестве констант.

Ответ №1:

Изменение нескольких вещей в вашем коде устраняет эту проблему. Во-первых, ваш набор данных не должен читаться как список, поэтому вы можете его удалить list_1 .

 list_1 <- list(list(df$v1, df$v2), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))
 

Оттуда вы можете сформулировать свой вызов pmap следующим образом, чтобы получить нужные вам результаты:

 pmap(list_1, ~wrap_vr(df, ..1, ..2, ..3))
 

Ответ №2:

Помимо двух перечисленных проблем есть еще одна проблема, вы передаете столбцы даты и cy как постоянные, предполагая, что они всегда будут там.
Тем не менее, я предлагаю использовать эти столбцы в качестве имен по умолчанию.

так

  • ваша первая проблема может быть решена с помощью .data[[vars]]
  • вашу вторую проблему можно решить, удалив df из списка
  • кроме того, предлагается дополнительно изменить вашу пользовательскую функцию, используя еще два аргумента, хотя и со значениями по умолчанию.
  • Также предлагается использовать аргумент df в последнем со значением по умолчанию
  • Таким образом, в вашей функции вам придется передать ей три аргумента, другие будут использовать значения по умолчанию.

ДЕМОНСТРАЦИЯ

 library(tidyverse)

df <- tibble(cy = c('a', 'a', 'b', 'b'),
             date = c(1,2,1,2),
             v1 = c(1,2,3,1),
             v2 = c(5,3,2,1))

wrap_vr <- function( vr, tit, ylab, c1 = 'date', c2 = 'cy', df = df){
  df %>% ggplot(aes(.data[[c1]], .data[[vr]]))  
    geom_line(color = "steelblue", size = 1)  
    labs(title =  tit,
         y = ylab, x = "")  
    facet_wrap(~ .data[[c2]])
}

wrap_vr( 'v1', "title_1", "ylabel_1")
 

 wrap_vr( 'v2', "title_2", "ylabel_2")
 

 list_1 <- list(list('v1', 'v2'), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))

pmap(list_1, ~wrap_vr(..1, ..2, ..3))
#> [[1]]
 

 #> 
#> [[2]]
 

Создано 2021-07-06 пакетом reprex (v2.0.0)

Ответ №3:

Использование pmap в этом случае приведет к печати пустых списков в консоли (или внутри уценки). Поскольку wrap_vr() вызывается побочными эффектами (показывает графику) и ничего не возвращает, лучше использовать pwalk() такую функцию:

Что касается записи v1 вместо df$v1 того, чтобы нам нужно будет изменить a wrap_vr() , чтобы учесть тот факт, что v1 его нужно хранить внутри списка в виде выражения (чтобы избежать ошибки «объект v1 не найден»).

 library(tidyverse)
library(rlang)

df <- tibble(cy = c('a', 'a', 'b', 'b'),
             date = c(1,2,1,2),
             v1 = c(1,2,3,1),
             v2 = c(5,3,2,1))

wrap_vr <- function(df, vr, tit, ylab){
  print(
  ggplot(data = df, aes(date, all_of(vr)))  
    geom_line(color = "steelblue", size = 1)  
    labs(title =  tit,
         y = ylab, x = "")  
    facet_wrap(~ cy))
}

list_1 <- list(list(df, df), list(df$v1, df$v2), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))

pwalk(list_1, wrap_vr)
 

Вариант 2

 #to avoid calling df twice inside the list
list_2 <- list(list(df$v1, df$v2), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))

pwalk(list_2, wrap_vr, df = df)
 

вариант 3

 #or quoting the column names

#because column names will go inside a list, we'll need a mechanism to avoid evaluation.

wrap_vr_expr <- function(df, vr, tit, ylab){
  print(
    ggplot(data = df, aes(date, eval_tidy(vr)))  
      geom_line(color = "steelblue", size = 1)  
      labs(title =  tit,
           y = ylab, x = "")  
      facet_wrap(~ cy))
}

list_3 <- list(list(df, df), list(expr(v1), expr(v2)), list("title_1", "title_2"), list("ylabel_1", "ylabel_2"))

pwalk(list_3, wrap_vr_expr)