了解R data.table中非标准评估的范围

了解R data.table中非标准评估的范围,r,scope,data.table,non-standard-evaluation,R,Scope,Data.table,Non Standard Evaluation,如何确保使用data.table的非标准计算1从父框架继承它所需的变量 根据我对动态范围的理解,我下面的代码应该可以工作,但实际上不行。我做错了什么 细节 我有一个要应用于单个data.table的许多函数的列表,这些函数返回布尔检查和消息(当检查为真时)。例如,假设我正在审核一个科目表 library(data.table) #----- Example data ----------------------------------------------------------- n <

如何确保使用
data.table
的非标准计算1从父框架继承它所需的变量

根据我对动态范围的理解,我下面的代码应该可以工作,但实际上不行。我做错了什么

细节 我有一个要应用于单个
data.table
的许多函数的列表,这些函数返回布尔检查和消息(当检查为真时)。例如,假设我正在审核一个科目表

library(data.table)
#----- Example data -----------------------------------------------------------
n <- 100
set.seed(123)
df <- data.table( acct_id      = paste0('ID',seq(n)),
                  acct_balance = round(pmax(rnorm(n,1000,5000),0)),
                  days_overdue = round(pmax(rnorm(n,20,20),0))
                  )
#----- Example list of rules to check (real case has more elements)------------
AuditRules <- list(
  list(
    msg_id = 1,
    msg_cat = 'Balance',
    cond_fn = function(d) d[, acct_balance > balance_limit ],
    msg_txt = 
      function(d) d[, paste('Account',acct_id,'balance is',
                            acct_balance - balance_limit, 
                            'over the limit.')]
  ),
  list(
    msg_id = 2,
    msg_cat = 'Overdue',
    cond_fn = function(d) d[, days_overdue > grace_period ],
    msg_txt = 
      function(d) d[, paste('Account',acct_id,'is overdue',
                            days_overdue-grace_period,
                            'days beyond grace period.')]
  )
)
with()
没有问题,尽管我读过(
)with
),通常人们应该避免在编程时使用它。这也不起作用:

auditTheData2 <- function(d, balance_limit = 1e4, grace_period=14){
  rbindlist(
    lapply(AuditRules, function(item){
          d[ item[['cond_fn']](d),
             .(msg_id, 
               msg_cat,
               msg_txt = item[['msg_txt']](.SD) )
             ]
    } )
  )
}
auditTheData2(df) # Same error
auditthedata 2这似乎有效:

ar <- list(
  list(
    cat = 'Balance',
    cond_expr = quote(acct_balance > balance_limit),
    msg_expr = quote(sprintf('Account %s balance is %s over the limit.',
      acct_id, 
      acct_balance - balance_limit))
  ),
  list(
    cat = 'Overdue',
    cond_expr = quote(days_overdue > grace_period),
    msg_expr = quote(sprintf('Account %s is overdue %s days beyond grace period.', 
      acct_id, 
      days_overdue-grace_period))
  )
)

audDT = rbindlist(rapply(ar, list, "call", how = "replace"), id="msg_id")

auditem = function(d, a, balance_limit = 1e4, grace_period = 14){
    a[, {
        cond    = cond_expr[[1]]
        msg     = msg_expr[[1]]
        .(txt = d[eval(cond), eval(msg)])
    }, by=.(msg_id, cat)]
}
我不确定这些变化中的哪一个造成了不同:

  • eval
    预定义表达式,而不是在函数内的
    j
    中组合它们
  • 使用规则表,有几个好处:
    • 由于每个条目都应该具有相同的结构,因此可以验证每个条目是否格式正确(没有缺少的组件)
    • msg\u id
      可以使用
      rbindlist
      自动编号,因此不必手动键入
    • by=
      可以代替
      lappy
      ,因为后者有一些奇怪的求值行为
我还将
paste
切换到
sprintf
,但我确信这并不重要


rappy
是必需的,因为data.table不支持作为列类型的调用/表达式(显然),但支持列表列。

rappy
是我的新表。我花了一段时间才完成这项工作。事实证明,我的子列表顺序不一致(尽管名称一致),这出乎意料地(1)导致了一个错误,(2)产生了一条关于
bmerge
的错误消息,这相当误导。再次感谢你,弗兰克。
Error in eval(jsub, SDenv, parent.frame()) : 
  object 'balance_limit' not found
auditTheData2 <- function(d, balance_limit = 1e4, grace_period=14){
  rbindlist(
    lapply(AuditRules, function(item){
          d[ item[['cond_fn']](d),
             .(msg_id, 
               msg_cat,
               msg_txt = item[['msg_txt']](.SD) )
             ]
    } )
  )
}
auditTheData2(df) # Same error
ar <- list(
  list(
    cat = 'Balance',
    cond_expr = quote(acct_balance > balance_limit),
    msg_expr = quote(sprintf('Account %s balance is %s over the limit.',
      acct_id, 
      acct_balance - balance_limit))
  ),
  list(
    cat = 'Overdue',
    cond_expr = quote(days_overdue > grace_period),
    msg_expr = quote(sprintf('Account %s is overdue %s days beyond grace period.', 
      acct_id, 
      days_overdue-grace_period))
  )
)

audDT = rbindlist(rapply(ar, list, "call", how = "replace"), id="msg_id")

auditem = function(d, a, balance_limit = 1e4, grace_period = 14){
    a[, {
        cond    = cond_expr[[1]]
        msg     = msg_expr[[1]]
        .(txt = d[eval(cond), eval(msg)])
    }, by=.(msg_id, cat)]
}
> head(auditem(df, audDT))
   msg_id     cat                                                 txt
1:      1 Balance        Account ID44 balance is 1845 over the limit.
2:      1 Balance        Account ID70 balance is 1250 over the limit.
3:      1 Balance        Account ID97 balance is 1937 over the limit.
4:      2 Overdue Account ID2 is overdue 11 days beyond grace period.
5:      2 Overdue  Account ID3 is overdue 1 days beyond grace period.
6:      2 Overdue  Account ID6 is overdue 5 days beyond grace period.