Skip to content

Commit 6734e24

Browse files
authored
Drop warnings, add optional probabilistic comparisons (#69)
* drop coercion and boolean warnings, add support for probabilistic comparisons * add errors scales so that the mapping (comparisons) works even when probabilistic comparisons are enabled * do not use probabilistic comparisons with vctrs * update tests * bump version, update NEWS * move scales to separate file, document functions * restore ! behavior
1 parent 4805a3f commit 6734e24

14 files changed

Lines changed: 271 additions & 92 deletions

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: errors
22
Type: Package
33
Title: Uncertainty Propagation for R Vectors
4-
Version: 0.4.4.2
4+
Version: 0.4.4.3
55
Authors@R: c(
66
person("Iñaki", "Ucar", email="iucar@fedoraproject.org",
77
role=c("aut", "cph", "cre"), comment=c(ORCID="0000-0001-6403-5550")),
@@ -20,6 +20,6 @@ LazyData: true
2020
Depends: R (>= 3.0.0)
2121
Suggests: dplyr (>= 1.0.0), vctrs (>= 0.5.0), pillar, ggplot2 (>= 3.5.0),
2222
testthat, vdiffr, knitr, rmarkdown
23-
RoxygenNote: 7.3.2
23+
RoxygenNote: 7.3.3
2424
Roxygen: list(old_usage = TRUE)
2525
VignetteBuilder: knitr

NAMESPACE

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ export(errors)
6767
export(errors_max)
6868
export(errors_min)
6969
export(geom_errors)
70+
export(scale_alpha_errors)
71+
export(scale_color_errors)
72+
export(scale_colour_errors)
73+
export(scale_fill_errors)
74+
export(scale_linewidth_errors)
75+
export(scale_radius_errors)
76+
export(scale_size_area_errors)
77+
export(scale_size_errors)
78+
export(scale_x_errors)
79+
export(scale_y_errors)
7080
export(set_correl)
7181
export(set_covar)
7282
export(set_errors)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
- Fix `print()` method, which now correctly returns the object invisibly (#64).
44
- Fix `all.equal.errors()` registration (@MichaelChirico #67 addressing #66).
5+
- Operations with errors and numeric vectors no longer warn about coercion;
6+
comparisons no longer warn about errors being dropped (#69 addressing #68).
7+
Also, probabilistic comparisons are now available via a dedicated option,
8+
see `?Ops.errors` for details.
59

610
# errors 0.4.4
711

R/geom_errors.R

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,3 @@ MakeGeomErrors <- function() ggplot2::ggproto(
147147
grob
148148
}
149149
)
150-
151-
# registered in .onLoad()
152-
scale_type.errors <- function(x) "continuous"

R/init.R

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
.onLoad <- function(libname, pkgname) {
2-
types <- c("bool", "coercion", "matmult")
3-
types <- paste0("errors.warn.", types)
4-
options(as.list(setNames(rep.int(TRUE, length(types)), types)))
52
register_all_s3_methods()
63
}

R/ops.R

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,78 @@
11
#' @rdname groupGeneric.errors
22
#'
33
#' @details \subsection{\code{Ops}}{
4-
#' Boolean operators drop the uncertainty (showing a warning once) and operate on the
5-
#' numeric values. The rest of the operators propagate the uncertainty as expected from
4+
#' Boolean operators drop the uncertainty and operate on the numeric values
5+
#' unless the option \code{errors.compare.probabilistic} is set to \code{TRUE}.
6+
#' In such case the comparison operators return a numeric value between 0 and 1,
7+
#' representing the probability that the comparison is true, assuming normal
8+
#' distribution of the errors.
9+
#' The rest of the operators propagate the uncertainty as expected from
610
#' the first-order Taylor series method. Any numeric operand is automatically
7-
#' coerced to \code{errors} (showing a warning once) with no uncertainty.}
11+
#' coerced to \code{errors} with no uncertainty.}
812
#'
913
#' @examples
10-
#' y <- set_errors(4:6, 0.2)
14+
#' y <- set_errors(1:3 + 0.1, c(0, 0.1, 0.2))
1115
#' x / sqrt(y) + y * sin(x)
1216
#'
1317
#' # numeric values are automatically coerced to errors
1418
#' x^2
1519
#'
1620
#' # boolean operators drop uncertainty
1721
#' y > x
22+
#' # unless probabilistic comparisons are enabled
23+
#' options(errors.compare.probabilistic = TRUE)
24+
#' y > x
25+
#' options(errors.compare.probabilistic = NULL)
1826
#'
1927
#' @export
2028
Ops.errors <- function(e1, e2) {
21-
if (.Generic %in% c("&", "|", "!", "==", "!=", "<", ">", "<=", ">=")) {
22-
warn_once_bool(.Generic)
29+
if (.Generic == "!")
30+
return(NextMethod())
31+
32+
cmp <- .Generic %in% c("==", "!=", "<", ">", "<=", ">=") # comparison-type
33+
pm <- .Generic %in% c("+", "-") # addition-type
34+
prd <- .Generic %in% c("*", "/", "%/%", "%%") # product-type
35+
pw <- .Generic %in% c("**", "^") # power-type
36+
37+
if (!any(cmp, pm, prd, pw))
38+
stop(paste("operation", .Generic, "not allowed"))
39+
40+
if (cmp && !getOption("errors.compare.probabilistic", default=FALSE))
2341
return(NextMethod())
24-
}
2542

2643
if (!missing(e2)) {
27-
coercion <- cond2int(!inherits(e1, "errors"), !inherits(e2, "errors"))
28-
if (coercion) {
29-
warn_once_coercion("Ops")
30-
switch(coercion, e1<-set_errors(e1), e2<-set_errors(e2))
31-
}
44+
if (!inherits(e1, "errors")) e1 <- set_errors(e1)
45+
if (!inherits(e2, "errors")) e2 <- set_errors(e2)
3246
}
3347

3448
deriv <- switch(
3549
.Generic,
50+
51+
# comparison-type
52+
"==" = return(NextMethod() & ((!.e(e1) & !.e(e2)) | .c(e1, e2) == 1)),
53+
"!=" = return(NextMethod() | (( .e(e1) | .e(e2)) & .c(e1, e2) != 1)),
54+
"<" = , "<=" = return(zstd(e2 - e1, .Generic)),
55+
">" = , ">=" = return(zstd(e1 - e2, .Generic)),
56+
57+
# addition-type
3658
"+" = , "-" =
3759
if (missing(e2)) {
3860
e2 <- NA
3961
list(do.call(.Generic, list(1)), NA)
4062
}
4163
else list(1, do.call(.Generic, list(1))),
64+
65+
# product-type
4266
"*" = list(.v(e2), .v(e1)),
4367
"/" = list(1 / .v(e2), -.v(e1) / .v(e2)^2),
4468
"%/%" = return(round(e1 / e2)),
4569
"%%" = return(e1 - round(e1 / e2) * e2),
46-
"^" = list(.v(e1)^(.v(e2)-1) * .v(e2), .v(e1)^.v(e2) * log(abs(.v(e1))))
70+
71+
# power-type
72+
"**" = , "^" =
73+
list(.v(e1)^(.v(e2)-1) * .v(e2), .v(e1)^.v(e2) * log(abs(.v(e1))))
4774
)
75+
4876
propagate(unclass(NextMethod()), e1, e2, deriv[[1]], deriv[[2]])
4977
}
5078

@@ -61,10 +89,6 @@ Ops.errors <- function(e1, e2) {
6189
#'
6290
#' #' @export
6391
#' `%*%.errors` = function(x, y) {
64-
#' warn_once(
65-
#' "matrix multiplication not supported for 'errors' objects, uncertainty dropped",
66-
#' fun = .Generic,
67-
#' type = "matmult"
68-
#' )
92+
#' warning("matrix multiplication not supported for 'errors' objects, uncertainty dropped")
6993
#' base::`%*%`(unclass(x), unclass(y))
7094
#' }

R/scale_errors.R

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#' Continuous scales for \code{errors} objects
2+
#'
3+
#' Default scales for the \code{errors} class.
4+
#'
5+
#' @param ... arguments passed on to the corresponding continuous scale
6+
#' (see the manual page for each \code{scale_{type}} for details).
7+
#'
8+
#' @name scale_errors
9+
#' @aliases NULL
10+
NULL
11+
12+
#' @rdname scale_errors
13+
#' @export
14+
scale_x_errors <- function(...) {
15+
make_scale_errors(ggplot2::scale_x_continuous(...))
16+
}
17+
18+
#' @rdname scale_errors
19+
#' @export
20+
scale_y_errors <- function(...) {
21+
make_scale_errors(ggplot2::scale_y_continuous(...))
22+
}
23+
24+
#' @rdname scale_errors
25+
#' @export
26+
scale_colour_errors <- function(...) {
27+
make_scale_errors(ggplot2::scale_colour_continuous(...))
28+
}
29+
30+
#' @rdname scale_errors
31+
#' @export
32+
scale_color_errors <- scale_colour_errors
33+
34+
#' @rdname scale_errors
35+
#' @export
36+
scale_fill_errors <- function(...) {
37+
make_scale_errors(ggplot2::scale_fill_continuous(...))
38+
}
39+
40+
#' @rdname scale_errors
41+
#' @export
42+
scale_alpha_errors <- function(...) {
43+
make_scale_errors(ggplot2::scale_alpha(...))
44+
}
45+
46+
#' @rdname scale_errors
47+
#' @export
48+
scale_size_errors <- function(...) {
49+
make_scale_errors(ggplot2::scale_size(...))
50+
}
51+
52+
#' @rdname scale_errors
53+
#' @export
54+
scale_size_area_errors <- function(...) {
55+
make_scale_errors(ggplot2::scale_size_area(...))
56+
}
57+
58+
#' @rdname scale_errors
59+
#' @export
60+
scale_radius_errors <- function(...) {
61+
make_scale_errors(ggplot2::scale_radius(...))
62+
}
63+
64+
#' @rdname scale_errors
65+
#' @export
66+
scale_linewidth_errors <- function(...) {
67+
make_scale_errors(ggplot2::scale_linewidth(...))
68+
}
69+
70+
make_scale_errors <- function(parent) {
71+
if (!requireNamespace("ggplot2", quietly=TRUE))
72+
stop("package 'ggplot2' is required for this functionality", call.=FALSE)
73+
74+
ggplot2::ggproto(
75+
paste0(class(parent)[1], "Errors"),
76+
parent,
77+
78+
map = function(self, x, limits = self$get_limits()) {
79+
# remove errors for comparisons
80+
ggplot2::ggproto_parent(parent, self)$map(.v(x), limits)
81+
}
82+
)
83+
}
84+
85+
# registered in .onLoad()
86+
scale_type.errors <- function(x) {
87+
if (!"errors" %in% .packages())
88+
stop("Variable of class 'errors' found, but 'errors' package is not attached.\n",
89+
" Please, attach it using 'library(errors)' to properly show scales with errors.")
90+
c("errors", "continuous")
91+
}

R/tidyverse.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ vec_restore.errors <- function(x, ...) {
3838
}
3939

4040
vec_proxy_equal.errors <- function(x, ...) {
41-
warn_once_bool("vctrs::vec_proxy_equal")
41+
old <- options(errors.compare.probabilistic = FALSE)
42+
on.exit(do.call(options, old), TRUE)
4243
x
4344
}
4445
# Currently necessary because of r-lib/vctrs/issues/1140

R/utils.R

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
.pm <- enc2native(intToUtf8(177))
22

3-
warn_once <- function(message, fun, type) {
4-
type <- paste0("errors.warn.", type)
5-
if (getOption(type)) {
6-
options(as.list(setNames(FALSE, type)))
7-
warning("In '", fun, "' : ", message, call. = FALSE)
8-
}
9-
}
10-
11-
warn_once_bool <- function(fun) warn_once(
12-
"boolean operators not defined for 'errors' objects, uncertainty dropped",
13-
fun = fun,
14-
type = "bool"
15-
)
16-
17-
warn_once_coercion <- function(fun) warn_once(
18-
"non-'errors' operand automatically coerced to an 'errors' object with no uncertainty",
19-
fun = "Ops",
20-
type = "coercion"
21-
)
22-
233
# ensure it's numeric
244
.v <- function(x) as.numeric(x)
255
.e <- function(x) as.numeric(errors(x))
6+
.c <- function(x, y) {
7+
if (is.null(cor <- correl(x, y)))
8+
cor <- rep(0, length(x))
9+
cor
10+
}
11+
12+
zstd <- function(x, op) {
13+
p <- pnorm(.v(x) / .e(x))
14+
replace(p, is.nan(p), if (nchar(op) == 1L) 0 else 1)
15+
}
2616

2717
get_exponent <- function(x) ifelse(.v(x), floor(log10(abs(.v(x)))), 0)
2818

man/geom_errors.Rd

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)