通过管道传递的数据框名称在R中如何获取

14

我希望能够通过管道打印传递的数据框的名称。这可行吗?我可以做到。

printname <- function(df){
    print(paste(substitute(df)))
}
printname(mtcars)
#[1] "mtcars"

然而,当使用 magrittr 管道传递此函数时,它返回 "."。

mtcars %>% printname
# [1] "."

如果在记录的生产过程中,自定义错误消息的函数出现问题,那么这将非常有帮助 -- 如果日志中唯一的东西是 ".",很难知道哪里出了问题。

只返回原始调用可能已经足够了,其中包括 mtcars %>% 部分。


如何在不知道数据框名称的情况下启动管道?使用 get 函数吗?这里缺少一些上下文。 - alistaire
当你编写代码时,你知道名称,但是通过管道调用的函数不知道名称,因此它们无法引发有信息的错误。你希望运行时断言引发包含导致错误的数据框名称的错误。 - Ryan Knight
好的。现在想起来了,加上行号会更好。 - alistaire
2个回答

14

这是第一次尝试,有点儿像hack,但似乎可以起作用。

find_chain_parts <- function() {
    i <- 1
    while(!("chain_parts" %in% ls(envir=parent.frame(i))) && i < sys.nframe()) {
          i <- i+1
      }
    parent.frame(i)
}

printfirstname <- function(df){
    ee <- find_chain_parts()
    print(deparse(ee$lhs))
}

mtcars %>% printfirstname
# [1] "mtcars"

pipe 函数创建一个环境,以跟踪链的部分。我试图在当前执行环境中向上查找此变量,然后使用那里存储的 lhs 信息来查找管道开头的符号。这项功能尚未经过充分测试。


1
这个之前运行得很好,但是在R 4.0.3和dplyr 1.0.2中,在while()的所有步骤中现在都返回NULL。 - Tom
2
这个技巧使用了 magrittr 1.5 的内部实现细节,不再适用于 magrittr 2.0。 - Lionel Henry
@LionelHenry 那么在2.0中有替代方案吗?或者是否有更官方的支持来从内部导航链条? - MrFlick
你可以使用 magrittr::pipe_nested() 作为管道,它使用与 R 4.1 的 |> 管道相同的重写方法。因此,在评估之前,foo %>% f() 被重写为 f(foo),并且名称可以被捕获。但我仍然建议避免依赖于这种模式的任何操作。 - Lionel Henry

2

正如Tom & Lionel Henry在MrFlick的回答中所评论的那样,接受的答案在更多的magrittr 2下已经不再适用。

因此,一个新的答案放弃了deparse(substitute()),而是使用sys.calls()。我从Artem Sokolov的回答here中得到了这个方法。我不会假装完全理解发生了什么,但它对我有效:

x_expression <- function(x) {
  getAST <- function(ee) purrr::map_if(as.list(ee), is.call, getAST)

  sc <- sys.calls()
  ASTs <- purrr::map( as.list(sc), getAST ) %>%
    purrr::keep( ~identical(.[[1]], quote(`%>%`)) )  # Match first element to %>%

  if( length(ASTs) == 0 ) return( enexpr(x) )        # Not in a pipe
  dplyr::last( ASTs )[[2]]    # Second element is the left-hand side
}

这将为管道和非管道符号提供所需的输出:

x_expression(mtcars)
# mtcars

mtcars %>% x_expression()
# mtcars

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接