Non-standard evaluation
2015-06-15
Перевод
https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html
Dplyr использует нестандартное вычисление (non-standard evaluation, NSE) во всех наиболее важных глаголах для одной таблицы:filter()
, mutate()
, summarise()
, arrange()
, select()
и group_by()
. NSE является важным не только для сохранения вашей типизации, но также для бекенда базы данных, где оно делает возможным трансляцию вашего кода из R в SQL. Тем не менее, хотя NSE хорошо подходит для интерактивного использования, с ним трудно программировать. Это виньетка описывает, как вы можете отказаться от NSE в dplyr, и вместо этого полагаться только на SE (вместе с небольшим использованием экранирования).NSE “за кулисами” опеспечивается пакетом lazyeval. Цель заключается в обеспечении подхода к NSE, который вы можете изучить однажды, а затем применять во многих местах (dplyr является первым из моих пакетов, использующим этот подход, но со временем я буду внедрять его везде). Вы можете почитать виньетки lazyeval, если вы хотите узнать больше об основах, или если вы хотели бы использовать этот подход в своих пакетах.
Основы стандартного вычисления
Каждая функция в dplyr, использующая NSE, также имеет версию, использующую SE. Схема наименований следующая: название SE-версии - это название NSE-версии с _ в конце. Например, SE-версияsummarise()
- это summarise_()
, arrange()
- arrange_()
. Эти функции работают подобно своим “двоюродным братьем” с NSE, но входящие данные должны быть “экранированы”:# NSE-версия:
summarise(mtcars, mean(mpg))
#> mean(mpg)
#> 1 20.09062
# SE-версия:
summarise_(mtcars, ~mean(mpg))
#> mean(mpg)
#> 1 20.09062
summarise_(mtcars, quote(mean(mpg)))
#> mean(mpg)
#> 1 20.09062
summarise_(mtcars, "mean(mpg)")
#> mean(mpg)
#> 1 20.09062
Dplyr понимает три способа экранирования:- с использованием формулы,
~ mean(mpg)
. - с
quote()
,quote(mean(mpg))
. - как строка:
"mean(mpg)"
.
constant1 <- function(n) ~n
summarise_(mtcars, constant1(4))
#> n
#> 1 4
# Использование чего-то другого кроме формулы приведет к ошибке,
# потому что неизвестно, в каком окружении искать
constant2 <- function(n) quote(n)
summarise_(mtcars, constant2(4))
#> Error in eval(expr, envir, enclos): binding not found: 'n'
Установка имен переменных
Если вы также хотите, чтобы выходных переменных менялись, необходимо передать список экранированных объектов аргументу .dots:n <- 10
dots <- list(~mean(mpg), ~n)
summarise_(mtcars, .dots = dots)
#> mean(mpg) n
#> 1 20.09062 10
summarise_(mtcars, .dots = setNames(dots, c("mean", "count")))
#> mean count
#> 1 20.09062 10
Комбинирование констант и переменных
Что делать, если вам нужно комбинировать константы и переменные? Используйте удобную функциюlazyeval::interp()
:library(lazyeval)
# Interp работает с формулами, quote() и строками (формулы лучше всего)
interp(~ x + y, x = 10)
#> ~10 + y
interp(quote(x + y), x = 10)
#> 10 + y
interp("x + y", x = 10)
#> [1] "10 + y"
# Используйте as.name, если имеется строка с именами переменных
interp(~ mean(var), var = as.name("mpg"))
#> ~mean(mpg)
# или используйте непосредственно имя в quote()
interp(~ mean(var), var = quote(mpg))
#> ~mean(mpg)
Поскольку любое действия в R - это вызов функции, вы можете использовать тот же принцип для изменения функций:interp(~ f(a, b), f = quote(mean))
#> ~mean(a, b)
interp(~ f(a, b), f = as.name("+"))
#> ~a + b
interp(~ f(a, b), f = quote(`if`))
#> ~if (a) b
Если у вас уже есть список значений, используйте .values
:interp(~ x + y, .values = list(x = 10))
#> ~10 + y
# Вы также можете вставлять переменные, определенные в текущем
# окружении, но это немного рискованно, потому что оно может легко
# измениться без вашего ведома
y <- 10
interp(~ x + y, .values = environment())
#> ~x + 10
Комментариев нет:
Отправить комментарий