【问题标题】:deparse(substitute()) within function using data.table as argument使用 data.table 作为参数的函数内的 deparse(substitute())
【发布时间】:2014-05-09 11:31:00
【问题描述】:

如果我想为错误或警告解析函数的参数,如果参数在函数内转换为 data.table,就会发生奇怪的事情:

e <- data.frame(x = 1:10)
### something strange is happening
foo <- function(u) {
  u <- data.table(u)
  warning(deparse(substitute(u)), " is not a data.table")
  u
}
foo(e)

##  foo(e)
##      x
##  1:  1
##  2:  2
##  3:  3
##  4:  4
##  5:  5
##  6:  6
##  7:  7
##  8:  8
##  9:  9
## 10: 10
## Warning message:
## In foo(e) :
##   structure(list(x = 1:10), .Names = "x", row.names = c(NA, -10L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x10026568>) is not a data.table

如果我在data.table 之前解析它一切正常:

### ok
foo1 <- function(u) {
  nu <- deparse(substitute(u))
  u <- data.table(u)
  warning(nu, " is not a data.table")
  u
}
## foo1(e)
##      x
##  1:  1
##  2:  2
##  3:  3
##  4:  4
##  5:  5
##  6:  6
##  7:  7
##  8:  8
##  9:  9
## 10: 10
## Warning message:
## In foo1(e) : e is not a data.table

顺便说一句,e 是否已经 data.table 没有区别。 我在分析一些代码时故意发现了它,其中deparse 非常耗时,因为e 很大。

这里发生了什么,我该如何处理 data.framedata.table 输入的此类函数?

nachti

【问题讨论】:

    标签: r data.table substitution


    【解决方案1】:

    这是因为substitute 在处理普通变量而不是 Promise 对象时表现不同。 Promise 对象是一个形式参数,并且有一个特殊的槽,其中包含生成它的表达式。换句话说,promise 对象是函数中的一个变量,它是该函数的参数列表的一部分。当您在函数中的 Promise 对象上使用 substitute 时,它将检索分配给该形式参数的函数调用中的表达式。来自?substitute

    通过检查解析树的每个组件来进行替换,如下所示:如果它不是 env 中的绑定符号,则它是不变的。如果它是一个promise对象,即函数的正式参数或使用delayedAssign()显式创建,promise的表达式槽将替换符号。如果是普通变量,则替换其值,除非 env 是 .GlobalEnv,在这种情况下符号保持不变。

    在你的情况下,你实际上用一个新的promise变量覆盖了原来的promise变量:

    u <- data.table(u)
    

    此时u 成为包含数据表的普通变量。当你substituteu 之后,substitute 只是返回数据表,deparse 处理回将生成它的 R 语言,这就是它慢的原因。

    这也解释了为什么您的第二个示例有效。你substitute,而变量仍然是一个承诺(即在你覆盖u之前)。这也是你第二个问题的答案。在覆盖你的承诺之前替换,或者不要覆盖你的承诺。

    有关更多详细信息,请参阅我在此处摘录的 R 语言定义(承诺)的 section 2.1.8

    Promise 对象是 R 的惰性求值机制的一部分。它们包含三个槽:一个值、一个表达式和一个环境。当一个函数被调用时,参数是匹配的,然后每个形式参数都绑定到一个 Promise。为该形式参数给出的表达式和一个指向调用该函数的环境的指针存储在 Promise 中。

    【讨论】:

    • @nachti,这不能回答你的问题吗?
    • @BrodieG:感谢您的回答。如上所述:如何处理data.framedata.table 输入的此类功能?我应该复制它(需要大量空间)吗?还是先对所有内容进行解析,然后再覆盖?
    • @nachti,后者,先deparse。此外,如果您想避免复制,您应该考虑使用setDT 而不是data.table。前者通过引用创建数据表。
    • 也许在第一个示例中使用delayedAssign("u", data.table(u)) 代替u&lt;-data.table(u)
    • @AaronMcDaid 我认为这没有帮助;你试过了吗?期望的输出可能是Warning: data.table(e) is not a data.table
    【解决方案2】:

    您也可以使用 sprintf 以及 is.data.table 来执行此操作。

    > e <- data.frame(x = 1:10)
    > foo <- function(u){
          nu <- deparse(substitute(u))
          if(!is.data.table(u)){
              warning(sprintf('%s is not a data table', nu))
              u
          } else {
              u
          }
      }
    > foo(e)
        x
    1   1
    2   2
    3   3
    4   4
    5   5
    6   6
    7   7
    8   8
    9   9
    10 10
    Warning message:
    In foo(e) : e is not a data table
    

    【讨论】:

    • 感谢您的回答! @Richard:好主意,但它仍然不是 data.table 并且我不能使用 := 例如。它应该在函数中使用,该函数接受data.framedata.table 作为输入,在data.table 语法中执行某些操作,如果输入为一个,则将其转换回data.frame。顺便说一句 u 很大 (~250 MB)
    猜你喜欢
    • 1970-01-01
    • 2013-09-01
    • 2021-12-02
    • 2022-01-04
    • 1970-01-01
    • 2019-05-21
    • 2017-04-26
    • 1970-01-01
    • 2021-11-04
    相关资源
    最近更新 更多