waldo icon indicating copy to clipboard operation
waldo copied to clipboard

document how `waldo::compare` and `all.equal` differ for relative numeric comparison

Open jaganmn opened this issue 1 year ago • 3 comments

Perhaps this issue is known or has been reported before - in that case, apologies.

all.equal uses its first argument to compute the denominator of the relative difference. waldo::compare uses its second argument, leading to differences in behaviour as here:

> x <- c( 0.077172645966023900, -0.000266267947825388, -0.01796820303767930,
+        -0.000266267947825388,  0.026773667457362100,  0.00885332514392564,
+        -0.017968203037679300,  0.008853325143925640,  0.03937164346237700)
> y <- c(0.04, 0, 0, 0, 0.04, 0, 0, 0, 0.04)
> tol <- 0.02
> all.equal(x, y, tolerance = tol)
[1] "Mean relative difference: 0.5326904"
> all.equal(y, x, tolerance = tol)
[1] TRUE
> waldo::compare(x, y, tolerance = tol)
✔ No differences
> waldo::compare(y, x, tolerance = tol)
`old`: 0.040  0.000  0.000  0.000 0.040 0.000  0.000 0.000 0.040
`new`: 0.077 -0.000 -0.018 -0.000 0.027 0.009 -0.018 0.009 0.039
>

help("compare", package="waldo") and help("expect_equal", package="testthat") don't claim that they are drop-in replacements for all.equal(...) and stopifnot(all.equal(...)), but that seems to be a prevailing belief. The situation would be improved with clearer documentation about the correspondence of the arguments.

jaganmn avatar Nov 08 '24 18:11 jaganmn

A corollary is that testthat::expect_equal(x, y, tolerance = tol) can fail under edition 2 and pass under edition 3. In that case, with the arguments reversed, the test would pass under edition 2 and fail under edition 3.

jaganmn avatar Nov 08 '24 19:11 jaganmn

Hmm I thought of this as a difference in signatures since testthat::expect_equal() does object,expected whereas all.equal() does target,current which (to me) maps to actual,expected vs. expected,actual.

But if we look carefully at waldo::compare()'s signature directly, it gives the same impression as all.equal():

waldo::compare
function (x, y, ..., x_arg = "old", y_arg = "new"...)

old ~~ expected new ~~ actual

The terminology is admittedly all over the place as we can see :)

MichaelChirico avatar Nov 08 '24 19:11 MichaelChirico

Hmmmmm, I think one principle of waldo is that if compare(x, y) reports differences then so should compare(y, x). So that suggests we should maybe deliberately deviate from all.equal(). But I don't know enough about numerical comparison to do that myself.

Slightly simpler variation of @jaganmn's code:

x <- c(0.0772, 0.0004, -0.018, 0.0004, 0.0268, 0.0089, -0.018, 0.0089, 0.0394)
y <- c(0.04, 0, 0, 0, 0.04, 0, 0, 0, 0.04)
tol <- 0.02

all.equal(x, y, tolerance = tol)
#> [1] "Mean relative difference: 0.5333333"
all.equal(y, x, tolerance = tol)
#> [1] TRUE

waldo::compare(x, y, tolerance = tol)
#> ✔ No differences
waldo::compare(y, x, tolerance = tol)
#> `old`: 0.040 0.000  0.000 0.000 0.040 0.000  0.000 0.000 0.040
#> `new`: 0.077 0.000 -0.018 0.000 0.027 0.009 -0.018 0.009 0.039

Created on 2025-07-07 with reprex v2.1.1

hadley avatar Jul 07 '25 18:07 hadley