Axe*_*man 17 r scoping dplyr lazyeval
我的目标是定义一些在dplyr动词中使用的函数,它们使用预定义的变量.这是因为我有一些这些函数带有一堆参数,其中许多参数都是相同的变量名.
我的理解:这很难(也许是不可能的)因为dplyr稍后会懒惰地评估用户指定的变量,但是任何默认参数都不在函数调用中,因此不可见dplyr.
考虑以下示例,我用它dplyr来计算变量是否已更改(在这种情况下相当无意义):
library(dplyr)
mtcars %>%
mutate(cyl_change = cyl != lag(cyl))
Run Code Online (Sandbox Code Playgroud)
现在,lag还支持备用排序,如下所示:
mtcars %>%
mutate(cyl_change = cyl != lag(cyl, order_by = gear))
Run Code Online (Sandbox Code Playgroud)
但是,如果我想创建自己的版本lag,总是按顺序排序gear呢?
天真的方法是这样的:
lag2 <- function(x, n = 1L, order_by = gear) lag(x, n = n, order_by = order_by)
mtcars %>%
mutate(cyl_change = cyl != lag2(cyl))
Run Code Online (Sandbox Code Playgroud)
但这显然会引发错误:
没有找到名为'gear'的对象
更现实的选择是这些,但它们也不起作用:
lag2 <- function(x, n = 1L) lag(x, n = n, order_by = ~gear)
lag2 <- function(x, n = 1L) lag(x, n = n, order_by = get(gear))
lag2 <- function(x, n = 1L) lag(x, n = n, order_by = getAnywhere(gear))
lag2 <- function(x, n = 1L) lag(x, n = n, order_by = lazyeval::lazy(gear))
Run Code Online (Sandbox Code Playgroud)
有没有办法在正在运行的data.frame中lag2正确查找?geardplyr
lag2不必提供的情况下打电话gear.lag2未被调用的数据集mtcars(但确实有gear一个变量).gear是函数的默认参数,因此如果需要它仍然可以更改,但这并不重要.edd*_*ddi 10
这里有两种方法data.table,但我不相信它们中的任何一种都能在dplyr目前使用.
在data.table,无论是内部j-expression(又名的第二参数[.data.table)被解析由所述data.table封装第一,而不是通过常规ř解析器.在某种程度上,您可以将其视为一个独立的语言解析器,它位于R的常规语言解析器中.这个解析器的作用是,它查找您使用的实际是data.table您正在操作的列的变量,以及无论它发现它把它放在环境中j-expression.
这意味着,你必须让这个解析器以某种方式知道gear将被使用,或者它根本不会成为环境的一部分.以下是实现这一目标的两个想法.
"简单"的方法是j-expression在你调用的地方实际使用列名lag2(除了一些monkeying lag2):
dt = as.data.table(mtcars)
lag2 = function(x) lag(x, order_by = get('gear', sys.frame(4)))
dt[, newvar := {gear; lag2(cyl)}]
# or
dt[, newvar := {.SD; lag2(cyl)}]
Run Code Online (Sandbox Code Playgroud)
这个解决方案有两个不受欢迎的属性 - 首先,我不确定它sys.frame(4)是多么脆弱- 你把这个东西放在一个函数或一个包中,我不知道会发生什么.你可以解决它并弄清楚正确的框架,但这是一种痛苦.第二 - 您必须在表达式中的任何位置提及您感兴趣的特定变量,或者在.SD任何地方再次使用,将所有这些变量转储到环境中.
我更喜欢的第二个选项是利用data.table解析器在变量查找之前评估eval表达式的事实,所以如果你在某个表达式中使用一个变量,那么你可以使用:eval
lag3 = quote(function(x) lag(x, order_by = gear))
dt[, newvar := eval(lag3)(cyl)]
Run Code Online (Sandbox Code Playgroud)
这不会受到其他解决方案的影响,而且必须输入额外的明显的缺点eval.
这个解决方案即将到来:
考虑一个稍微简单一点的玩具示例:
mtcars %>%
mutate(carb2 = lag(carb, order_by = gear))
Run Code Online (Sandbox Code Playgroud)
我们仍然使用lagand 它的order_by参数,但不再用它做任何进一步的计算。我们不再坚持使用 SE mutate,而是切换到 NSEmutate_并lag2构建一个函数调用作为字符向量。
lag2 <- function(x, n = 1, order_by = gear) {
x <- deparse(substitute(x))
order_by <- deparse(substitute(order_by))
paste0('dplyr::lag(x = ', x, ', n = ', n, ', order_by = ', order_by, ')')
}
mtcars %>%
mutate_(carb2 = lag2(carb))
Run Code Online (Sandbox Code Playgroud)
这给了我们与上面相同的结果。
原始玩具示例可以通过以下方式实现:
mtcars %>%
mutate_(cyl_change = paste('cyl !=', lag2(cyl)))
Run Code Online (Sandbox Code Playgroud)
mutate_。paste.gear应该来自哪里。gear向全局环境或在全局环境中赋值carb似乎没问题,但我的猜测是,在某些情况下可能会出现意外的错误。使用公式而不是字符向量会更安全,但这需要为其分配正确的环境才能工作,这对我来说仍然是一个很大的问号。| 归档时间: |
|
| 查看次数: |
1249 次 |
| 最近记录: |