17

这个问题有关

我想构建一个自定义管道%W>%,可以使一个操作的警告静音

library(magrittr)
data.frame(a= c(1,-1)) %W>% mutate(a=sqrt(a)) %>% cos

将相当于:

w <- options()$warn
data.frame(a= c(1,-1)) %T>% {options(warn=-1)} %>%
  mutate(a=sqrt(a))    %T>% {options(warn=w)}  %>%
  cos

这两个尝试不起作用:

`%W>%` <- function(lhs,rhs){
  w <- options()$warn
  on.exit(options(warn=w))
  options(warn=-1)
  lhs %>% rhs
}

`%W>%` <- function(lhs,rhs){
  lhs <- quo(lhs)
  rhs <- quo(rhs)
  w <- options()$warn
  on.exit(options(warn=w))
  options(warn=-1)
  (!!lhs) %>% (!!rhs)
}

我怎样才能把rlang它变成有效的东西?

4

4 回答 4

8

我想我会像这样处理它,通过调整 magrittr 管道以包含这个新选项。这种方式应该非常健壮。

首先,我们需要在 magrittr 的函数中插入一个新选项is_pipe,通过该选项确定某个函数是否为管道。我们需要它来识别%W>%

new_is_pipe = function (pipe)
{
  identical(pipe, quote(`%>%`)) || identical(pipe, quote(`%T>%`)) ||
    identical(pipe, quote(`%W>%`)) ||
    identical(pipe, quote(`%<>%`)) || identical(pipe, quote(`%$%`))
}
assignInNamespace("is_pipe", new_is_pipe, ns="magrittr", pos="package:magrittr")
`%W>%` = magrittr::`%>%`

我们还需要一个新的辅助函数来检查正在处理的管道是否是%W>%

is_W = function(pipe) identical(pipe, quote(`%W>%`))
environment(is_W) = asNamespace('magrittr')

最后,我们需要放置一个新的分支magrittr:::wrap_function来检查这是否是一个%W>%管道。如果是这样,它会将options(warn = -1)on.exit(options(warn = w)插入到函数调用的主体中。

new_wrap_function = function (body, pipe, env)
{
  w <- options()$warn
  if (magrittr:::is_tee(pipe)) {
    body <- call("{", body, quote(.))
  }
  else if (magrittr:::is_dollar(pipe)) {
    body <- substitute(with(., b), list(b = body))
  }
  else if (is_W(pipe)) {
    body <- as.call(c(as.name("{"), expression(options(warn=-1)), parse(text=paste0('on.exit(options(warn=', w, '))')), body))
  }
  eval(call("function", as.pairlist(alist(. = )), body), env, env)
}
assignInNamespace("wrap_function", new_wrap_function, ns="magrittr", pos="package:magrittr")

测试这个工作:

data.frame(a= c(1,-1)) %W>% mutate(a=sqrt(a)) %>% cos
#           a
# 1 0.5403023
# 2       NaN

相比...

data.frame(a= c(1,-1)) %>% mutate(a=sqrt(a)) %>% cos
#           a
# 1 0.5403023
# 2       NaN
# Warning message:
# In sqrt(a) : NaNs produced
于 2017-11-28T17:55:35.667 回答
3

我不确定这个解决方案是否完美,但这是一个开始:

`%W>%` <- function(lhs, rhs) {
  call <- substitute(`%>%`(lhs, rhs))
  eval(withr::with_options(c("warn" = -1), eval(call)), parent.frame())
}

这似乎适用于以下 2 个示例:

> data.frame(a= c(1,-1)) %W>% mutate(a=sqrt(a)) %>% cos
          a
1 0.5403023
2       NaN
> c(1,-1) %W>% sqrt()
[1]   1 NaN
于 2017-11-27T16:23:27.877 回答
3

也许是这样的rlang

library(rlang)
library(magrittr)

`%W>%` <- function(lhs, rhs){
  w <- options()$warn
  on.exit(options(warn=w))
  options(warn=-1)
  lhs_quo = quo_name(enquo(lhs))
  rhs_quo = quo_name(enquo(rhs))
  pipe = paste(lhs_quo, "%>%", rhs_quo)
  return(eval_tidy(parse_quosure(pipe)))
}

data.frame(a= c(1,-1)) %W>% mutate(a=sqrt(a)) %>% cos

结果:

          a
1 0.5403023
2       NaN

笔记:

  • 您需要enquo代替,quo因为您引用的是提供lhsand的代码,而rhs不是文字lhsand rhs

  • 在评估之前,我无法弄清楚如何将lhs_quo/lhs输入rhs_quo(这是 a quosure),我也不能先评估(抛出一个错误,说not found in )rhs_quoamutate(a=sqrt(a))

  • 我想出的解决方法变成字符串lhsrhs用 粘贴它们"%>%",将字符串解析为quosure,然后最后 tidy 评估quosure.

于 2017-11-27T19:33:51.893 回答
2

回来有点经验,我只是错过了一个eval.parentsubstitute组合,不需要rlang

`%W>%` <- function(lhs,rhs){
  # `options()` changes options but returns value BEFORE change
  opts <- options(warn = -1) 
  on.exit(options(warn=opts$warn))
  eval.parent(substitute(lhs %>% rhs))
}

data.frame(a= c(1,-1)) %W>% mutate(a=sqrt(a)) %>% cos
#           a
# 1 0.5403023
# 2       NaN
于 2019-04-13T18:36:24.413 回答