Эквивалент next в purrr::map_df

#r #tidyverse #purrr

#r #tidyverse #purrr

Вопрос:

Я ищу эквивалент next в циклах для вызова purrr::map_df.

map_df прекрасно работает с фреймами данных, которые равны НУЛЮ (как в примере ниже), поэтому он работает, когда я задаю Result <- NULL в своем примере ниже.

Может ли кто-нибудь предложить общее решение для моей иллюстрации ниже, которое не требовало бы от меня настройки Result <- NULL , а сразу переходило бы к «далее».

 library(tidyverse)
set.seed(1000)

df <- data.frame(x = rnorm(100), y = rnorm(100), z = rep(LETTERS, 100))

Map_Func <- function(df) {

  Sum_Num <- suppressWarnings(sqrt(sum(df$y)))

  if( Sum_Num == "NaN" ) {

    Result <- NULL
    # I would like to have an equivalent to "next" here... 

    } else {

  Result <- df %>% filter(y == max(y)) %>% mutate(Result = x*y)

}

Result

}

Test <- split(df, df$z) %>% map_df(~Map_Func(.))
 

В приведенном выше коде, что я могу использовать вместо Result <- NULL уродливого оператора if (т. Е. Я хочу просто проверить условие и эффективно выполнить «next»).

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

1. group_split из какого пакета? этого нет в стандартном dplyr

2. group_split Функция dplyr включена, только только в самой новой версии. dplyr.tidyverse.org/reference/group_split.html Но вы можете захотеть изменить пример, поскольку большинство людей, вероятно, не имеют последней версии и, следовательно, не могут протестировать этот код. Не обязательно, просто к вашему сведению

3. @IceCreamToucan спасибо — я скорректировал код, чтобы он не зависел от использования последней терминологии dplyr.

4. В функции, которую использует map, вы должны что-то вернуть. Вместо того, чтобы делать это предложение else, вы могли return(NULL) бы использовать предложение if, которое было бы эквивалентно next .

5. К вашему сведению, есть is.nan функция

Ответ №1:

Для выхода из функции вы можете использовать return(<output>) команду. Это немедленно завершает работу функции с определенным вами выводом. Следующий вывод дает тот же результат, который вы получали с вашим примером кода.

 library(tidyverse)
set.seed(1000)

df <- data.frame(x = rnorm(100), y = rnorm(100), z = rep(LETTERS, 100))

Map_Func <- function(df) {

  Sum_Num <- suppressWarnings(sqrt(sum(df$y)))

  if( Sum_Num == "NaN" ) {

    return(NULL)

  } 

  Result <- df %>% filter(y == max(y)) %>% mutate(Result = x*y)
}

Test <- split(df, df$z) %>% map_df(~Map_Func(.))
 

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

1. это именно то, что я искал, спасибо. Действительно классный способ добавления остановок в map_df, поскольку он учитывает нули.

Ответ №2:

Логически решение не сильно отличается от OP, но пытается сохранить его в чистоте, используя отдельные функции. custom_check функция заключается в проверке условия для каждой группы. Используя map_if , мы применяем функцию Map_Func_true только при custom_check возврате TRUE или иначе применяем Map_Func_false , который возвращает NULL и, наконец, связывает строки.

 library(tidyverse)

Map_Func_true <- function(df) {
  df %>% filter(y == max(y)) %>% mutate(Result = x*y)
}

Map_Func_false <- function(df) { return(NULL) }

custom_check <- function(df) {
    !is.nan(suppressWarnings(sqrt(sum(df$y))))
}


df %>%
  group_split(z) %>%
  map_if(., custom_check, Map_Func_true, .else = Map_Func_false) %>%
  bind_rows()


# A tibble: 26 x 4
#       x     y z     Result
#   <dbl> <dbl> <fct>  <dbl>
# 1  1.24  2.00 A       2.47
# 2  1.24  2.00 A       2.47
# 3  1.24  2.00 C       2.47
# 4  1.24  2.00 C       2.47
# 5  1.24  2.00 E       2.47
# 6  1.24  2.00 E       2.47
# 7  1.24  2.00 G       2.47
# 8  1.24  2.00 G       2.47
# 9  1.24  2.00 I       2.47
#10  1.24  2.00 I       2.47
# … with 16 more rows
 

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

1. Ваше решение слишком специфично (функция — просто иллюстрация). Идея заключается в том, чтобы заставить вызов map_df остановиться и перейти к следующему. решение @adam обеспечивает более чистый способ сделать это.

2. Ваше решение интересно в общих чертах — никогда не думайте об объединении map_if таким образом…

Ответ №3:

Вот еще один способ взглянуть на это с помощью purrr::safely

 Map_Func <- function(df) {

  Sum_Num <- suppressWarnings(sqrt(sum(df$y)))

  df %>% filter(y == max(y)) %>% mutate(Result = x*y)

}

Test <- split(df, df$z) %>% 
  map(safely(~Map_Func(.))) %>% 
  transpose() %>% 
  pluck("result") %>% # use 'error' here to get the error log
  bind_rows()
 

Таким образом, функция становится чище, и вы также получаете хороший журнал ошибок