向量化相等性测试

7

我会很惊讶如果这不是一个重复问题,但我没有找到解决方法。

我了解使用 == 测试浮点数相等性的限制。应该使用 all.equal

0.1 + 0.2 == 0.3
# FALSE
all.equal(0.1 + 0.2, 0.3)
# TRUE

但是 == 具有矢量化的优势:
set.seed(1)
Df <- data.frame(x = sample(seq(-1, 1, by = 0.1), size = 100, replace = TRUE),
                 y = 0.1)
Df[Df$x > 0 & Df$x < 0.2,]
## x   y
## 44 0.1 0.1
## 45 0.1 0.1

# yet
sum(Df$x == Df$y)
# [1] 0

我可以自己编写一个(糟糕的)函数:

我可以自己编写一个(糟糕的)函数:

All.Equal <- function(x, y){
  stopifnot(length(x) == length(y))
  out <- logical(length(x))
  for (i in seq_along(x)){
    out[i] <- isTRUE(all.equal(x[i], y[i]))
  }
  out
}

sum(All.Equal(Df$x, Df$y))

虽然给出了正确答案,但仍有很长的路要走。

microbenchmark::microbenchmark(All.Equal(Df$x, Df$y), Df$x == Df$y)
Unit: microseconds
                  expr      min        lq        mean     median        uq        max neval cld
 All.Equal(Df$x, Df$y) 9954.986 10298.127 20382.24436 10511.5360 10798.841 915182.911   100   b
          Df$x == Df$y   16.857    19.265    29.06261    30.8535    38.529     45.151   100  a 

另一个选择可能是:

All.equal.abs <- function(x,y){
  tol <- .Machine$double.eps ^ 0.5
  abs(x - y) < tol
}

有一个现有的函数可以执行与==相似的操作。

这个任务需要使用什么函数?


最接近的我能想到的代码是 with(Df, mapply(function(a, b) isTRUE(all.equal(a, b)), x, y)) ,但这可能不比你已经做的更好。你可以尝试使用 .mapply() (裸骨的 mapply()) 以获得轻微的速度提升。 - Rich Scriven
4
在数值计算中,abs(x-y)<tol 是非常标准的写法,绝对比使用 ==all.equal 更好。 - fishtank
2个回答

3

Vectorize()这个选项速度较慢。正如@fishtank在评论中建议的那样,最好的解决方案是检查绝对差是否小于某个容差值,即下面的is_equal_tol()

set.seed(123)
a <- sample(1:10, size = 50, replace = T)
b <- sample(a)

is_equal_tol <- function(x, y, tol = .Machine$double.eps ^ 0.5) {
  abs(x - y) < tol
}

is_equal_vec <- Vectorize(all.equal, c("target", "current"))

is_equal_eq <- function(x, y) x == y

microbenchmark::microbenchmark(is_equal_eq(a, b),
                               is_equal_tol(a, b), 
                               isTRUE(is_equal_vec(a, b)),
                               times = 1000L)

Unit: nanoseconds
                       expr     min      lq        mean  median      uq      max neval
          is_equal_eq(a, b)       0     856    1545.797    1284    2139    14113  1000
         is_equal_tol(a, b)    1711    2567    4991.377    4278    6843    27370  1000
 isTRUE(is_equal_vec(a, b)) 2858445 3008552 3258916.503 3082964 3204204 46130260  1000

1
你没有展示出你的ab的生成方式,但是根据你纳秒级别的结果,我认为你应该将它们变成更长的向量。 - Gregor Thomas
@Gregor 没错。我编辑了示例以提供完整的示例。谢谢! - Johan Larsson

0

无法进行基准测试,但对all.equal函数进行向量化处理可能有效:

All.equal <- Vectorize(all.equal, c("target", "current"))
sum(All.equal(Df$x, Df$y)==T)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接