【问题标题】:Is there a way to short-circuit a vectorized operand (combination of & and &&)?有没有办法短路矢量化操作数(& 和 && 的组合)?
【发布时间】:2018-05-15 17:57:21
【问题描述】:

在 R 中,有操作数 & 和 &&(或者,| 和 ||),其中 & 是矢量化的,而 && 不是。另一个区别是 & 总是计算所有参数,而 && 短路(有时称为惰性计算),这意味着 F && fun(x) 不会调用 fun(x)。

我正在寻找的是结合这两者的方法,例如我可以调用的方法

input <- data.frame(valid=c(T,T,T,F,F), value=c('1','2','3','huh',14), stringsAsFactors = F)
# A function to check evenness, but who prints an alert if the value is more then 10
fun <- function(x) {
  if(any(as.numeric(x)>10))
    cat(as.numeric(x)[as.numeric(x)>10], '')
  return(as.numeric(x) %% 2==0)
}
cat("Numbers over 10 (unexpected):\n")
pass <- input$valid & fun(input$value)
cat("\nAnd in total we have",sum(pass),"even numbers") 

在这里,我收到警告,因为 'huh' 不能转换为数字,即使执行函数永远不需要 'huh'。

我想要的是类似这样的行为:

pass2 <- rep(FALSE, nrow(input))
cat("Numbers over 10 (unexpected):\n")
for(n in 1:nrow(input)) {
  if(input$valid[n]) pass2[n] <- fun(input$value[n])
}
cat("\nAnd in total we have",sum(pass2),"even (valid) numbers")

在这个例子中,很容易适应乐趣,或者围绕它来写,但在我的日常工作中,我经常发现有更困难的条件的用例,以及我不想每次都适应的各种功能。

有什么方法可以做我想做的事,还是我真的需要返回到非向量化函数和/或 for 循环?


我自己尝试了一些方法,但没有成功: 映射:

mapply(`&&`, input$valid, fun(input$value))

但仍会评估乐趣。 有趣的是,如果您比较以下内容,则 && 在必要时会忽略返回的值:

mapply(`&&`, c(F,F), c(T, print('Huh?')))
mapply(`&&`, c(T,T), c(T, print('Huh?')))
mapply(`&`, c(F,F), c(T, print('Huh?')))

但在所有情况下,打印都会被评估,我猜是 mapply 强制评估。

我也试过这个:

`%&%` <- function(a,b) {
  res <- rep(FALSE, times=length(a))
  res[is.na(a)|a] <- a[is.na(a)|a] & b[is.na(a)|a]
}
input$valid %&% fun(input$value)

认为如果 a 不为假,我只会使用 b 的值。 但看起来这里发生的事情几乎相同:首先评估 b ,然后才进行子集化...... (是的,我知道我也应该检查长度,我正在尝试这个,因为长度检查可能会强制评估)

【问题讨论】:

  • 您可以考虑在您的问题中包含reproducible example。这会让其他人更容易帮助你。

标签: r vectorization


【解决方案1】:

您可以做一个新函数的构造函数,将NA 处理为FALSE

bool_noNA <- function(fun) {
  function(x, valid) {
    if (missing(valid)) valid <- !is.na(x)
    res  <- logical(length(x))
    res[valid] <- fun(x[valid])
    res
  }
}

使用示例:

is_odd <- function(x) x %% 2 == 1    
is_odd(c(3:5, NA))

is_odd_noNA <- bool_noNA(is_odd)
is_odd_noNA(c(3:5, NA))
is_odd_noNA(c(3:5, NA), valid = c(T, F, F, F))
is_odd_noNA(c(3:5, NA), valid = c(F, T, F, F))

【讨论】:

  • 谢谢,我认为您的回答有帮助!我的问题并不是专门针对摆脱 NA 的(并且我已经更新了我的问题),但我认为不传递 fun(x) 而是仅评估 fun(x[Conditional]) 的想法可行。我会尝试看看是否可以提出更通用的解决方案。
【解决方案2】:

根据 F. Privé 的回答,我找到了一个更通用的解决方案:

LazyAnd <- function(a,b, fun, ...) {
  a[is.na(a)|a] <- a[is.na(a)|a] & fun(b[is.na(a)|a], ...)
  return(a)
}

缺点是它不再是中缀运算符,这可能会使事情变得更加混乱。但我认为在不评估 fun(x) 的所有结果的情况下调用 fun(x) 是不可能的,即使那是我想要的。因为我想到的是,否则函数(如 cumsum)的定义会很差,因为 cumsum(c(1:100)[10:20]) 当然给出的结果与 cumsum(c(1:100))[10 :20]

最后,如果人们想重用我的代码,这里是惰性的、向量化的或同样的:

LazyOr <- function(a,b, fun, ...) {
  a[is.na(a)|!a] <- a[is.na(a)|!a] & fun(b[is.na(a)|!a], ...)
  return(a)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 2016-01-10
    相关资源
    最近更新 更多