Использование dplyr :: if_else с purrr - PullRequest
2 голосов
/ 20 июня 2019

Я пытаюсь использовать purrr::map_dbl в списке, где мне нужно другое поведение, если элемент списка NULL.

x <- list(NULL, c(1, 2), c(3, 4))
purrr::map_dbl(x, function(y) { dplyr::if_else(is.null(y), 0, y[1] + y[2]) })

Это не работает должным образом, вместо этого выдается ошибка:

Ошибка: false должна быть длиной 1 (длина condition), а не 0

При отладке вызова if_else я вижу, что y[1] + y[2] оценивается как integer(0). Почему это не работает?

Следующие все работают, как я ожидал:

> purrr::map_dbl(x, function(y) { dplyr::if_else(is.null(y), 0, y[1]) })
[1] 0 1 3
> purrr::map_dbl(x, function(y) { dplyr::if_else(is.null(y), 0, y[2]) })
[1] 0 2 4
> purrr::map_dbl(x, ~ dplyr::if_else(is.null(.x), 0, .x[1]))
[1] 0 1 3
> purrr::map_dbl(x, function(y) { base::ifelse(is.null(y), 0, y[1] + y[2]) })
[1] 0 3 7
> purrr::map_dbl(x, function(y) { if (is.null(y)) 0 else y[1] + y[2] })
[1] 0 3 7

Чем отличается исходный звонок?

Ответы [ 2 ]

2 голосов
/ 20 июня 2019

Альтернативой является использование аргумента na.rm в sum, чтобы игнорировать значения NA или NULL при сложении значений вместе. Таким образом, мы можем пропустить логику if else:

purrr::map_dbl(x, sum, na.rm = TRUE) 
# [1] 0 3 7

Вот эквивалент Base R (как указано akrun):

sapply(x, sum, na.rm = TRUE)
1 голос
/ 20 июня 2019

Мы можем отладить, это легко с browser()

purrr::map_dbl(x, function(y) {
        browser()
        dplyr::if_else(is.null(y), 0, y[1] + y[2]) 
 })
Called from: .f(.x[[i]], ...)
Browse[1]> 
debug at #1: dplyr::if_else(is.null(y), 0, y[1] + y[2])
Browse[2]> 
Error: `false` must be length 1 (length of `condition`), not 0
Call `rlang::last_error()` to see a backtrace

Итак, здесь length является проблемой.

Согласно ?if_else требует, чтобы все аргументы имели одинаковую длину

Значения, которые следует использовать для ИСТИННЫХ и ЛОЖНЫХ значений условия. Они должны иметь одинаковую длину с условием или длину 1. Они также должны быть одного типа: if_else () проверяет, имеют ли они одинаковый тип и класс. Все остальные атрибуты взяты из true.


Чтобы углубиться в проблему, она все равно работает, если значение не NULL

v1 <- 1
if_else(v1==1, 0, v1[1] + v1[2])
#[1] 0

Но как только мы изменим его на NA или NULL, это станет проблемой, возможно из-за type

@ CBraun сделал интересное наблюдение

NULL[1] + NULL[2]
#integer(0)

возвращает длину 0,

if_else(is.na(v1), 0, integer(0))

Ошибка: false должна быть длиной 1 (длина condition), а не 0 Звоните rlang::last_error(), чтобы увидеть след

Однако,

NA + NA # [1] NA

имеет значение length 1, но все равно возвращает ошибку

v1 <- NA
if_else(is.na(v1), 0, v1[1] + v1[2])

Ошибка: false должен быть двойным вектором, а не целым вектором Звоните rlang::last_error(), чтобы увидеть след

Если мы используем правильный NA отправлено, он работает

v1 <- NA_real_
if_else(is.na(v1), 0, v1[1] + v1[2])
#[1] 0

Обратите внимание, что здесь проблема type. В целом, как указано в документации, length и type должны соответствовать if_else

Итог: когда значение NULL, поведение странное из-за того, что вывод + равен integer(0) длины 0


Это тот случай, когда мы можем использовать if/else вместо if_else

purrr::map_dbl(x, ~ if(is.null(.x)) 0 else sum(.x))
#[1] 0 3 7 

В этом отношении используйте sum вместо того, чтобы вызывать аргументы отдельно y[[1]], y[[2]], так как это вызывает дисбаланс в длине

purrr::map_dbl(x, ~ ifelse(is.null(.x), 0, sum(.x)))
#[1] 0 3 7

Обратите внимание, что ifelse также требует, чтобы длины были одинаковыми, хотя он работает здесь из-за повторного использования значений

Вектор такой же длины и атрибутов (включая измерения и «класс»), что и тестовые значения и значения данных из значений да или нет.

purrr::map_dbl(x, ~ ifelse(is.null(.x), 0, .x[[1]] + .x[[2]]))
#[1] 0 3 7

ПРИМЕЧАНИЕ. Все методы используются для проверки состояния ОП. Но, если целью является получение результата, есть и другие способы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...