воскресенье, 9 августа 2015 г.

dplyr. Non-standard evaluation (перевод)



Перевод

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

Комментариев нет:

Отправить комментарий