用于替换quote()d表达式中所有变量的通用函数(如果它们存在于环境中)

时间:2013-10-11 22:55:04

标签: r

遇到类似问题,我正在寻找比此问题提供的更通用的解决方案:How do I substitute symbols in a language object?

我有一个未评估的表达式foo <- quote(bar + baz),它是其他表达式中的变量:qux <- quote(bla + foo)

我们可以查看:

exists("foo", envir=.GlobalEnv) & class(foo)=="call"
[1] TRUE
exists("qux", envir=.GlobalEnv) & class(qux)=="call"
[1] TRUE

现在我想编写一个广义函数,将(解析?)qux分解为组件表达式,替换 .GlobalEnv 中存在的函数和属于call类及其值:

replaceFUN <- function(x) {
  # do something
}

运行replaceFUN(qux)应返回:

bla + (bar + baz)  

实际问题的背景:

我正在建立一个量化交易规则回测试引擎。我的目标是延迟对quote()d表达式的评估,例如在定义之后的规则和指标计算。

require(data.table)
require(TTR) # for the `SMA` function

DT <- data.table(Instrument=rep("SPX",3),Date=1:3, Close=c(1050, 1052, 1051))

# define parameters
nSMA <-2
t <- 2

# define indicators
time.filter <- quote( Date==t )
moving.average <- quote( SMA(Close, nSMA) )    
buy <- quote( Close > moving.average & time.filter )


AddColumn <- function(x, colname) {
  DT[,eval(substitute(colname)):=eval(x, envir=.SD)]
}

AddColumn(time.filter, "filter")

    Instrument Date Close filter
 1:        SPX    1  1050  FALSE
 2:        SPX    2  1052   TRUE
 3:        SPX    3  1051  FALSE

AddColumn(moving.average, "MA")

   Instrument Date Close filter     MA
1:        SPX    1  1050  FALSE     NA
2:        SPX    2  1052   TRUE 1051.0
3:        SPX    3  1051  FALSE 1051.5

AddColumn(buy, "Buy")

Error in Close > moving.average & time.filter : 
operations are possible only for numeric, logical or complex types

这显然会引发错误,因为AddColumn函数缺少解析嵌套moving.averagetime.filter变量的机制(以及用户定义和嵌套的任何其他变量)。 buy中的规则嵌套是为了提高可读性而确实是一种语法糖。

2 个答案:

答案 0 :(得分:4)

我刚才解决了一个非常类似的问题。查看[.data.table的{​​{3}}并查看其中的deconstruct_and_evalconstruct功能。它应该给你足够的信息继续。

答案 1 :(得分:3)

[.data.table source code自定义此功能 产生了所需的通用解决方案:

deconstruct_and_eval = function(expr, envir = parent.frame(), enclos = parent.frame()) {

  if (!mode(expr) %in% c("call", "expression")) 
    return(expr)

  if (length(expr) == 1) {
    if (is.call(expr[[1]])) return (deconstruct_and_eval(expr[[1]]))
    else return(expr)
  }

  if (expr[[1]] == quote(eval) && length(expr) < 3) {
    return(deconstruct_and_eval(eval(expr[[2]], envir, enclos), envir, enclos))
  }

  lapply(expr, function(m) {
    if (is.call(m)) {
      if (m[[1]] == quote(eval)) eval(m[[2]], envir, enclos)
      else deconstruct_and_eval(m, envir, enclos)
    } else {
# begin edit 
      if(exists(as.character(m),envir=.GlobalEnv)) {
        if(!is.function(eval(m)))
           eval(m)
           else
             m
      } else
# end edit
        m
    }
  })
}

使用原始问题变量运行函数会产生:

deconstruct_and_eval(qux)
[[1]]
`+`

[[2]]
bla

[[3]]
bar + baz

然后可以使用source code中的construct函数重建此解构列表:

construct(deconstruct_and_eval(qux))
bla + (bar + baz)

应用于实际问题:

deconstruct_and_eval(buy)
[[1]]
`&`

[[2]]
[[2]][[1]]
`>`

[[2]][[2]]
Close

[[2]][[3]]
SMA(Close, nSMA)


[[3]]
Date == t

construct(deconstruct_and_eval(buy))
Close > SMA(Close, nSMA) & Date == t