了解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.