#r #function #dplyr #tidyselect
#r #функция #dplyr #tidyselect
Вопрос:
Я пытаюсь написать функцию, которая будет переносить dplyr::coalesce()
и будет использовать объект данных и имена столбцов для объединения. До сих пор мои попытки не увенчались успехом.
Пример данных
library(dplyr)
df <-
data.frame(col_a = c("bob", NA, "bob", NA, "bob"),
col_b = c(NA, "danny", NA, NA, NA),
col_c = c("paul", NA, NA, "paul", NA))
## col_a col_b col_c
## 1 bob <NA> paul
## 2 <NA> danny <NA>
## 3 bob <NA> <NA>
## 4 <NA> <NA> paul
## 5 bob <NA> <NA>
Использование заглушки при написании пользовательской функции
coalesce_plus_1 <- function(data, vars) {
data %>%
mutate(coalesced_col = coalesce(!!! rlang::syms(tidyselect::vars_select(names(.), vars))))
}
coalesce_plus_2 <- function(data, vars) {
data %>%
mutate(coalesced_col = coalesce(!!! rlang::syms(vars)))
}
coalesce_plus_3 <- function(data, vars) {
data %>%
mutate(coalesced_col = coalesce({{ vars }}))
}
Результаты…
coalesce_plus_1()
df %>%
coalesce_plus_1(data = ., vars = c(col_a, col_b, col_c))
Ошибка: объект ‘col_a’ не найден.
Однако:
df %>%
coalesce_plus_1(data = ., vars = all_of(starts_with("col")))
## col_a col_b col_c coalesced_col
## 1 <NA> <NA> paul paul
## 2 <NA> danny <NA> danny
## 3 bob <NA> <NA> bob
## 4 <NA> <NA> paul paul
## 5 bob <NA> <NA> bob
coalesce_plus_2()
df %>%
coalesce_plus_2(data = ., vars = c(col_a, col_b, col_c))
Ошибка в lapply(.x, .f, …) : объект ‘col_a’ не найден
А также
df %>%
coalesce_plus_2(data = ., vars = all_of(starts_with("col")))
Ошибка:
starts_with()
должно использоваться в функции выбора.
я вижу https://tidyselect.r-lib.org/reference/faq-selection-context.html .
Запуститеrlang::last_error()
, чтобы увидеть, где произошла ошибка.
coalesce_plus_3()
df %>%
coalesce_plus_3(data = ., vars = c(col_a, col_b, col_c))
Ошибка: проблема с
mutate()
вводомcoalesced_col
. ввод x
coalesced_col
не может быть переработан до размера 5. i Вводcoalesced_col
coalesce(c(col_a, col_b, col_c))
. i вводcoalesced_col
должен быть
размером 5 или 1, а не 15.
А также
df %>%
coalesce_plus_3(data = ., vars = all_of(starts_with("col")))
Ошибка: проблема с
mutate()
вводомcoalesced_col
.
xstarts_with()
должен использоваться в функции выбора.
я вижу https://tidyselect.r-lib.org/reference/faq-selection-context.html .
я ввожуcoalesced_col
coalesce(all_of(starts_with("col")))
.
Итог
Как я могу написать пользовательскую функцию coalesce()
, которая будет использовать объект данных и конкретные имена столбцов для объединения, позволяя использовать как конкретные имена, например, c(col_a, col_b, col_c)
так и вспомогательные функции, например, starts_with("col")
в аргументе функции vars
?
Комментарии:
1. Вы видели
tidyselect
виньетку о реализации интерфейсов tidyselect?2. очень полезно, спасибо!
Ответ №1:
Это простая реализация, которая будет возвращать только выбранные столбцы, но может быть довольно легко расширена, чтобы сохранить все столбцы (я бы bind_cols
вернул их в конце …).
Это просто, потому что мы полагаемся на select
то, что он сделает всю работу за нас, как было предложено в начале реализации виньетки tidyselect
# edited to keep all columns
coalesce_df = function(data, ...) {
data %>%
select(...) %>%
transmute(result = invoke(coalesce, .)) %>%
bind_cols(data, .)
}
df %>%
coalesce_df(everything())
# col_a col_b col_c result
# 1 bob <NA> paul bob
# 2 <NA> danny <NA> danny
# 3 bob <NA> <NA> bob
# 4 <NA> <NA> paul paul
# 5 bob <NA> <NA> bob
df %>% coalesce_df(col_a, col_b)
# col_a col_b col_c result
# 1 bob <NA> paul bob
# 2 <NA> danny <NA> danny
# 3 bob <NA> <NA> bob
# 4 <NA> <NA> paul <NA>
# 5 bob <NA> <NA> bob
Комментарии:
1. Я понял ваш намек
bind_cols
и сделал следующее в функции:cols <- data %>% select(...) %>% names
, затем канал сmutate(result = invoke(coalesce, .))
, как вы предложили, и в конце каналаbind_cols(., select(data, -all_of(cols)))
.2. Я просто отредактировал, чтобы использовать
transmute
вместоmutate
, что делает хранение всех столбцов немного более чистым — сохраняет их в том же порядке и добавляетresult
столбец в конце.
Ответ №2:
На самом деле, ваша первая функция работает, просто напишите vars
как символ. Посмотрите:
df %>% coalesce_plus_1(data = ., vars = c("col_a","col_b","col_c"))
Вот еще один хороший вариант:
library(dplyr)
df <- data.frame(col_a = c("bob", NA, "bob", NA, "bob"),
col_b = c(NA, "danny", NA, NA, NA),
col_c = c("paul", NA, NA, "paul", NA))
coalesce_plus <- function(data,vars){
x <- as.list(select(data,vars))
data.frame(data, coalesced_col=coalesce(!!!x))
}
df %>% coalesce_plus(data = ., vars = c("col_a","col_b","col_c"))