Чтобы добавить нюанса, эти вещи не обязательно так сложны в базовом R.
Важно помнить, что использовать eval.parent()
, когда это необходимо для оценки заменяемых аргументов в правильной среде, если вы используете eval.parent()
правильно, выражение во вложенных вызовах найдет свое применение. Если вы этого не сделаете, вы можете открыть для себя ад окружающей среды :).
Базовый ящик для инструментов, который я использую, состоит из quote()
, substitute()
, bquote()
, as.call()
и do.call()
(последний полезен при использовании с substitute()
Не вдаваясь в подробности, вот как решить в базе R случаи, представленные @Artem и @Tung, без какой-либо аккуратной оценки, а затем последний пример, не использующий quo
/ enquo
, но все же извлекающий выгоду из объединения и удаления кавычек (!!!
и !!
)
Мы увидим, что объединение и удаление кавычек делают код более приятным (но требуют функций для его поддержки!), И что в настоящих случаях использование quosures не улучшает ситуацию кардинально (но все же, возможно, улучшает).
решение случая Артема с базой R
f0 <- function( myExpr ) {
eval(substitute(myExpr), list(a=2, b=3))
}
g0 <- function( myExpr ) {
val <- eval.parent(substitute(f0(myExpr)))
val
}
f0(a+b)
#> [1] 5
g0(a+b)
#> [1] 5
решение 1-го случая Тунга с базой R
my_summarise0 <- function(df, group_var, select_var) {
group_var <- substitute(group_var)
select_var <- substitute(select_var)
# create new name
mean_name <- paste0("mean_", as.character(select_var))
eval.parent(substitute(
df %>%
select(select_var, group_var) %>%
group_by(group_var) %>%
summarise(mean_name := mean(select_var))))
}
library(dplyr)
set.seed(1234)
d = data.frame(x = c(1, 1, 2, 2, 3),
y = rnorm(5),
z = runif(5))
my_summarise0(d, x, z)
#> # A tibble: 3 x 2
#> x mean_z
#> <dbl> <dbl>
#> 1 1 0.619
#> 2 2 0.603
#> 3 3 0.292
решение 2-го случая Тунга с базой R
grouping_vars <- c(quote(x), quote(y))
eval(as.call(c(quote(group_by), quote(d), grouping_vars))) %>%
summarise(mean_z = mean(z))
#> # A tibble: 5 x 3
#> # Groups: x [3]
#> x y mean_z
#> <dbl> <dbl> <dbl>
#> 1 1 -1.21 0.694
#> 2 1 0.277 0.545
#> 3 2 -2.35 0.923
#> 4 2 1.08 0.283
#> 5 3 0.429 0.292
в функции:
my_summarise02 <- function(df, select_var, ...) {
group_var <- eval(substitute(alist(...)))
select_var <- substitute(select_var)
# create new name
mean_name <- paste0("mean_", as.character(select_var))
df %>%
{eval(as.call(c(quote(select),quote(.), select_var, group_var)))} %>%
{eval(as.call(c(quote(group_by),quote(.), group_var)))} %>%
{eval(bquote(summarise(.,.(mean_name) := mean(.(select_var)))))}
}
my_summarise02(d, z, x, y)
#> # A tibble: 5 x 3
#> # Groups: x [3]
#> x y mean_z
#> <dbl> <dbl> <dbl>
#> 1 1 -1.21 0.694
#> 2 1 0.277 0.545
#> 3 2 -2.35 0.923
#> 4 2 1.08 0.283
#> 5 3 0.429 0.292
решение 2-го случая Танга с базой R, но с использованием !!
и !!!
grouping_vars <- c(quote(x), quote(y))
d %>%
group_by(!!!grouping_vars) %>%
summarise(mean_z = mean(z))
#> # A tibble: 5 x 3
#> # Groups: x [3]
#> x y mean_z
#> <dbl> <dbl> <dbl>
#> 1 1 -1.21 0.694
#> 2 1 0.277 0.545
#> 3 2 -2.35 0.923
#> 4 2 1.08 0.283
#> 5 3 0.429 0.292
в функции:
my_summarise03 <- function(df, select_var, ...) {
group_var <- eval(substitute(alist(...)))
select_var <- substitute(select_var)
# create new name
mean_name <- paste0("mean_", as.character(select_var))
df %>%
select(!!select_var, !!!group_var) %>%
group_by(!!!group_var) %>%
summarise(.,!!mean_name := mean(!!select_var))
}
my_summarise03(d, z, x, y)
#> # A tibble: 5 x 3
#> # Groups: x [3]
#> x y mean_z
#> <dbl> <dbl> <dbl>
#> 1 1 -1.21 0.694
#> 2 1 0.277 0.545
#> 3 2 -2.35 0.923
#> 4 2 1.08 0.283
#> 5 3 0.429 0.292
person
Moody_Mudskipper
schedule
04.10.2019
dplyr::
ppl просто позволял нам передавать имена переменных в виде символьных строк, как в старых подчеркнутых вариантах, таких какmutate_()
. imo, еще лучшим вариантом было бы иметь аргумент типаcolnames_as_strings=TRUE
дляmutate()
и др ..., который упростил бы использование dplyr как в интерактивном режиме, так и в программном обеспечении. А пока добро пожаловать в _5 _ / _ 6_ ад ... - person lefft   schedule 07.04.2018enquo()
действительно имеет смысл только в том случае, если вы глубоко привержены возможности передавать имена столбцов без кавычек (мне непонятно, почему это важно, но да ладно). может быть, есть какая-то фундаментальная причина, которая требует понимания внутреннего устройства dplyr, чтобы понять ... - person lefft   schedule 07.04.2018base::subset()
!) - person lefft   schedule 07.04.2018group_by()
,select()
иmutate_at()/summarize_at()
. Когда имена столбцов неизвестны (или не могут быть известны) заранее, может быть сложно написать хорошие функции разделения-применения-объединения в dplyr. Иногда даже легче использоватьbase::tapply()
, именно потому, что вы можете указать группирующие столбцы как символьные строки, которые вы передаете в качестве параметра ... В конкретном случае, показанном OP, было бы, конечно, ужасно, если бы"m"
означалоmydata$m
(или всякий раз, когда имя столбца используется в правой части=
внутри функции таблицы dplyr). - person lefft   schedule 07.04.2018dplyr::
и использую его каждый день - я просто хочу, чтобы он был как можно лучше!) - person lefft   schedule 07.04.2018group_by(data, !! var)
. Честно говоря, я не вижу трудностей. Это простая, понятная, последовательная, но мощная абстракция. Таким образом, это диаметрально противоположно тому, что предлагаютtapply
и т. Д. - person Konrad Rudolph   schedule 07.04.2018