Стандартная оценка Dplyr с использованием вектора из нескольких строк с функцией mutate
Я пытаюсь предоставить вектор, который содержит несколько имен столбцов для mutate()
позвонить с помощью dplyr
пакет. Воспроизводимый пример ниже:
stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
jill = c(1,2,NA,3,4,NA,5,6,NA,7),
jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
one_name <- c('jack')
# jack jill jane
# 1 1 1
# NA 2 2
# 2 NA 3
# NA 3 4
# 3 4 5
# NA NA 6
# 4 5 NA
# NA 6 NA
# 5 NA NA
# NA 7 NA
Я могу понять, как использовать версии "одной переменной", но не знаю, как расширить это на несколько переменных?
# the below works as expected, and is an example of the output I desire
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
# using the one_name variable works if I evaluate it and then convert to
# a name before unquoting it
stackdf %>% rowwise %>% mutate(test = anyNA(!!as.name(eval(one_name))))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 FALSE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA FALSE
10 NA 7 NA TRUE
Как я могу расширить вышеуказанный подход, чтобы я мог использовать two_names
вектор? С помощью as.name
принимает только один объект, поэтому он не работает.
Этот вопрос здесь похож: передайте вектор имен переменных в range() в dplyr. Это решение "работает" в том, что я могу использовать следующий код:
two_names2 <- quos(c(jack, jill))
stackdf %>% rowwise %>% mutate(test = anyNA(!!!two_names2))
Но это побеждает цель, если я должен напечатать c(jack, jill)
напрямую, а не используя two_names
переменная. Есть ли похожая процедура, где я могу использовать two_names
напрямую? Этот ответ Как передать именованный вектор в dplyr::select с помощью кавычек? использования rlang::syms
но хотя это работает для выбора переменных (т.е. stackdf %>% select(!!! rlang::syms(two_names))
кажется, не работает для предоставления аргументов при мутировании (то есть stackdf %>% rowwise %>% mutate(test = anyNA(!!! rlang::syms(two_names)))
, Этот ответ похож, но не работает: Как оценить построенную строку с нестандартной оценкой, используя dplyr?
2 ответа
Есть несколько ключей к решению этого вопроса:
- Доступ к строкам в символьном векторе и использование их с
dplyr
- Форматирование аргументов, предоставляемых функции, используемой с
mutate
здесьanyNA
Цель здесь состоит в том, чтобы повторить этот вызов, но используя именованную переменную two_names
вместо ввода вручную c(jack,jill)
,
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
1. Использование динамических переменных с dplyr
С помощью
quo
/quos
: Не принимает строки в качестве входных данных. Решение с использованием этого метода будет:two_names2 <- quos(c(jack, jill)) stackdf %>% rowwise %>% mutate(test = anyNA(!!! two_names2))
Обратите внимание, что
quo
принимает один аргумент, и, следовательно, без кавычек, используя!!
и для нескольких аргументов вы можете использоватьquos
а также!!!
соответственно. Это не желательно, потому что я не используюtwo_names
и вместо этого нужно напечатать столбцы, которые я хочу использовать.С помощью
as.name
или жеrlang::sym
/rlang::syms
:as.name
а такжеsym
принять только один вход, однакоsyms
примет несколько и вернет список символических объектов в качестве вывода.> two_names [1] "jack" "jill" > as.name(two_names) jack > syms(two_names) [[1]] jack [[2]] jill
Обратите внимание, что
as.name
игнорирует все после первого элемента. Тем не мение,syms
здесь, похоже, работает должным образом, так что теперь мы должны использовать это вmutate
вызов.
2. Использование динамических переменных внутри mutate
с помощью anyNA
или другие переменные
С помощью
syms
а такжеanyNA
напрямую не дает правильного результата.> stackdf %>% rowwise %>% mutate(test = anyNA(!!! syms(two_names))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 FALSE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA FALSE 10 NA 7 NA TRUE
Осмотр
test
показывает, что это учитывает только первый элемент и игнорирует второй элемент. Однако, если я использую другую функцию, например,sum
или жеpaste0
Ясно, что оба элемента используются:> stackdf %>% rowwise %>% mutate(test = sum(!!! syms(two_names), na.rm = TRUE)) jack jill jane test <dbl> <dbl> <dbl> <dbl> 1 1 1 1 2 2 NA 2 2 2 3 2 NA 3 2 4 NA 3 4 3 5 3 4 5 7 6 NA NA 6 0 7 4 5 NA 9 8 NA 6 NA 6 9 5 NA NA 5 10 NA 7 NA 7
Причина этого становится ясной, когда вы посмотрите на аргументы
anyNA
противsum
,функция (x, recursive = FALSE).Primitive ("anyNA")
function (..., na.rm = FALSE).Primitive ("sum")
anyNA
ожидает один объектx
, в то время какsum
может взять переменный список объектов(...)
,Просто поставка
c()
исправляет эту проблему (см. ответ от alistaire).> stackdf %>% rowwise %>% mutate(test = anyNA(c(!!! syms(two_names)))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 TRUE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA TRUE 10 NA 7 NA TRUE
В качестве альтернативы... в образовательных целях можно использовать комбинацию
sapply
,any
, а такжеanyNA
для получения правильного результата. Здесь мы используемlist
так что результаты предоставляются в виде одного объекта списка.# this produces an error an error because the elements of !!! # are being passed to the arguments of sapply (X =, FUN = ) > stackdf %>% rowwise %>% mutate(test = any(sapply(!!! syms(two_names), anyNA))) Error in mutate_impl(.data, dots) : Evaluation error: object 'jill' of mode 'function' was not found.
Поставляя
list
устраняет эту проблему, потому что он связывает все результаты в один объект.# the below table is the familiar incorrect result that uses only the `jack` > stackdf %>% rowwise %>% mutate(test = any(sapply(X=as.list(!!! syms(two_names)), FUN=anyNA))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 FALSE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA FALSE 10 NA 7 NA TRUE # this produces the correct answer > stackdf %>% rowwise %>% mutate(test = any(X = sapply(list(!!! syms(two_names)), FUN = anyNA))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 TRUE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA TRUE 10 NA 7 NA TRUE
Понимание того, почему эти двое работают по-разному, имеет смысл, когда их поведение сравнивают!
> as.list(two_names) [[1]] [1] "jack" [[2]] [1] "jill" > list(two_names) [[1]] [1] "jack" "jill"
Ты можешь использовать rlang::syms
(который экспортируется версией dplyr для разработки; поочередно вызывайте ее напрямую) для приведения строк к предложениям, поэтому
library(dplyr)
stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
jill = c(1,2,NA,3,4,NA,5,6,NA,7),
jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
stackdf %>% rowwise %>% mutate(test = anyNA(c(!!!syms(two_names))))
#> Source: local data frame [10 x 4]
#> Groups: <by row>
#>
#> # A tibble: 10 x 4
#> jack jill jane test
#> <dbl> <dbl> <dbl> <lgl>
#> 1 1. 1. 1. FALSE
#> 2 NA 2. 2. TRUE
#> 3 2. NA 3. TRUE
#> 4 NA 3. 4. TRUE
#> 5 3. 4. 5. FALSE
#> 6 NA NA 6. TRUE
#> 7 4. 5. NA FALSE
#> 8 NA 6. NA TRUE
#> 9 5. NA NA TRUE
#> 10 NA 7. NA TRUE
Альтернативно, используя маленькую базу R вместо аккуратного eval:
stackdf %>% mutate(test = rowSums(is.na(.[two_names])) > 0)
#> jack jill jane test
#> 1 1 1 1 FALSE
#> 2 NA 2 2 TRUE
#> 3 2 NA 3 TRUE
#> 4 NA 3 4 TRUE
#> 5 3 4 5 FALSE
#> 6 NA NA 6 TRUE
#> 7 4 5 NA FALSE
#> 8 NA 6 NA TRUE
#> 9 5 NA NA TRUE
#> 10 NA 7 NA TRUE
... что, вероятно, будет намного быстрее, как итерация rowwise
марки n
звонки вместо одного векторизованного.