-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnse.Rmd
127 lines (99 loc) · 4.39 KB
/
nse.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
---
title: "Non-standard evaluation"
output: html_document
date: '2015-06-15'
---
### Перевод
### https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html
```{r, echo=FALSE, warning=FALSE, message=FALSE}
setwd("D:/GITHUB/dplyr_doc_ru")
library(dplyr)
```
Dplyr использует нестандартное вычисление (non-standard evaluation, NSE) во всех наиболее важных глаголах для одной таблицы: `filter()`, `mutate()`, `summarise()`, `arrange()`, `select()` и `group_by()`. NSE является важным не только для сохранения вашей типизации, но также для бекенда базы данных, где оно делает возможным трансляцию вашего кода из R в SQL. Тем не менее, хотя NSE хорошо подходит для интерактивного использования, с ним трудно программировать. Это виньетка описывает, как вы можете отказаться от NSE в dplyr, и вместо этого полагаться только на SE (вместе с небольшим использованием экранирования).
NSE "за кулисами" опеспечивается пакетом [lazyeval](https://github.com/hadley/lazyeval). Цель заключается в обеспечении подхода к NSE, который вы можете изучить однажды, а затем применять во многих местах (dplyr является первым из моих пакетов, использующим этот подход, но со временем я буду внедрять его везде). Вы можете почитать виньетки lazyeval, если вы хотите узнать больше об основах, или если вы хотели бы использовать этот подход в своих пакетах.
## Основы стандартного вычисления
Каждая функция в dplyr, использующая NSE, также имеет версию, использующую SE. Схема наименований следующая: название SE-версии - это название NSE-версии с _ в конце. Например, SE-версия `summarise()` - это `summarise_()`, `arrange()` - `arrange_()`. Эти функции работают подобно своим "двоюродным братьем" с NSE, но входящие данные должны быть "экранированы":
```{r, eval=FALSE}
# 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)"`.
Лучше всего использовать формулу, потому что формула фиксирует как выражение для вычисления, так и окружение, в котором оно должно вычисляться. Это важно, когда выражение является комбинацией переменных в таблице данных и объектов в локальном окружении:
```{r, eval=FALSE}
constant1 <- function(n) ~n
summarise_(mtcars, constant1(4))
#> n
#> 1 4
```
```{r, eval=FALSE}
# Использование чего-то другого кроме формулы приведет к ошибке,
# потому что неизвестно, в каком окружении искать
constant2 <- function(n) quote(n)
summarise_(mtcars, constant2(4))
#> Error in eval(expr, envir, enclos): binding not found: 'n'
```
## Установка имен переменных
Если вы также хотите, чтобы выходных переменных менялись, необходимо передать список экранированных объектов аргументу .dots:
```{r, eval=FALSE}
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()`:
```{r, eval=FALSE}
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 - это вызов функции](http://adv-r.had.co.nz/Functions.html#all-calls), вы можете использовать тот же принцип для изменения функций:
```{r, eval=FALSE}
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`:
```{r, eval=FALSE}
interp(~ x + y, .values = list(x = 10))
#> ~10 + y
# Вы также можете вставлять переменные, определенные в текущем
# окружении, но это немного рискованно, потому что оно может легко
# измениться без вашего ведома
y <- 10
interp(~ x + y, .values = environment())
#> ~x + 10
```