Test element-wise near-equality to the natural numbers while allowing for small numeric errors.
Usage
is_natural(
x,
strict = TRUE,
allow_zero = FALSE,
allow_NA = FALSE,
tol = .Machine$double.eps^0.5
)
all_natural(
x,
strict = TRUE,
allow_zero = FALSE,
allow_NA = FALSE,
tol = .Machine$double.eps^0.5
)
make_natural(
x,
strict = TRUE,
allow_zero = FALSE,
allow_NA = FALSE,
all = TRUE,
tol = .Machine$double.eps^0.5
)Arguments
- x
object to test.
- strict
Exclude zero from the natural numbers?
- allow_zero
TRUEorFALSE: allow zero-lengthxof the correct type?- allow_NA
TRUEorFALSE: allowNAs of the correct type inx?- tol
A small positive number. Numbers that differ less in value than
tolare considered equal.- all
TRUEorFALSE: useall_natural()instead ofis_natural()?
Value
is_natural() and all_natural(): TRUE or FALSE indicating if
x is a vector of the appropriate length with only natural numbers.
make_natural(): x, rounded and coerced to integer.
Details
Natural numbers are the positive integers (1, 2, 3, etc.). Zero is
considered a natural number if argument strict is FALSE. integer(0) and
numeric(0) are considered natural numbers if argument allow_zero is
TRUE. Numbers that are too large to be represented as
integers, Inf, NaN, and NULL are never considered natural
numbers in this implementation.
is_natural() and all_natural() allow for small numeric errors when
comparing numbers. Such numeric errors can arise because of rounding or
representation error. As the Note at == warns, x == round(x) does
not allow for such errors but tests exact equality. Functions from other
packages with names like integerish frequently do not allow for small
numeric errors but are instead intended to allow values that are stored as
doubles (e.g., 3) in addition to integer-type values (e.g., 3L).
If allow_NA is TRUE, is_natural() and all_natural() return TRUE for
NA_integer_ and NA_real_ but not for the other NAs or NaN.
Notes
make_natural(x, all = FALSE) and make_natural(x, all = TRUE) throw an
error if x is not natural according to is_natural(x) or all_natural(x),
respectively.
The code of is_natural() and all_natural() is partly based on the example
is.wholenumber() in is.integer().
Programming notes
Use of is_natural(x) or all_natural(x) should be followed by assigning
the rounded value to the argument: x <- as.integer(round(x)). Alternatively,
assign the result of make_natural(x) to x without using is_natural(x)
or all_natural(x) inside stopifnot.
is.integer() does not check that x is a natural number (nor if x is a
whole number) but rather that x is of type integer (see the
Note in is.integer()).
See also
progutils::are_equal() to
check for element-wise near-equality of numbers;
all.equal() to check more generally for near-equality; identical() to
check for exact equality; Comparison to compare two vectors using binary
operators; match() and
progutils::not_in() to
compare character vectors;
R FAQ 7.31
for background on numerical equality.
The vignettes about design choices and about type coercion.
Other collections of checks on type and length:
all_characters(),
all_names(),
is_logical(),
is_number(),
is_zerolength()
Examples
is_natural(x = 5 + 1e-10) # TRUE
#> [1] TRUE
# Zero is not considered a natural number if 'strict' is TRUE:
is_natural(x = 1e-10, strict = TRUE) # FALSE
#> [1] FALSE
try(make_natural(x = 1e-10, strict = TRUE)) # Error
#> Error in make_natural(x = 1e-10, strict = TRUE) :
#> checkinput::all_natural(1e-10) is not TRUE
is_natural(x = 1e-10, strict = FALSE) # TRUE
#> [1] TRUE
make_natural(x = 1e-10, strict = FALSE) # 0
#> [1] 0
is_natural(x = -1e-10, strict = FALSE) # FALSE: wrong sign
#> [1] FALSE
is_natural(x = Inf, strict = FALSE) # FALSE
#> [1] FALSE
is_natural(x = "a") # FALSE
#> [1] FALSE
is_natural(x = 1:2) # FALSE: wrong length
#> [1] FALSE
# Allowing for small numeric errors is important
x <- sqrt(2)^2
is_natural(x = x) # TRUE
#> [1] TRUE
x == 2 # FALSE!
#> [1] FALSE
x - 2 # about 4.44e-16
#> [1] 4.440892e-16
all_natural(x = c(3, 5 + 1e-10)) # TRUE
#> [1] TRUE
try(make_natural(x = c(3, 5 + 1e-10))) # c(3L, 5L)
#> [1] 3 5
# Zero is not considered a natural number if 'strict' is TRUE:
all_natural(x = c(1e-10, 3, 5), strict = TRUE) # FALSE
#> [1] FALSE
all_natural(x = c(1e-10, 3, 5), strict = FALSE) # TRUE
#> [1] TRUE
all_natural(x = c(-1e-10, 3, 5), strict = FALSE) # FALSE: wrong sign
#> [1] FALSE
all_natural(x = c(3, 5, Inf), strict = FALSE) # FALSE
#> [1] FALSE
all_natural(x = "a") # FALSE
#> [1] FALSE
all_natural(x = 1:2) # TRUE
#> [1] TRUE
# Illustrate the need to follow use of is_natural(x) or all_natural(x) by
# assigning the rounded value to the argument
toy_fun_erroneous <- function(x) {
stopifnot(is_natural(x))
seq_len(x)
}
toy_fun_correct <- function(x) {
stopifnot(is_natural(x))
x <- round(x)
seq_len(x)
}
toy_fun_safe <- function(x, all = TRUE) {
x <- make_natural(x, all = all)
seq_len(x)
}
toy_fun_erroneous(x = 5 - 1e-8) # 1:4
#> [1] 1 2 3 4
toy_fun_correct(x = 5 - 1e-8) # 1:5
#> [1] 1 2 3 4 5
toy_fun_safe(x = 5 - 1e-8) # 1:5
#> [1] 1 2 3 4 5
try(toy_fun_erroneous(x = 5.1)) # Error: is_natural(x) is not TRUE
#> Error in toy_fun_erroneous(x = 5.1) : is_natural(x) is not TRUE
try(toy_fun_correct(x = 5.1)) # Error: is_natural(x) is not TRUE
#> Error in toy_fun_correct(x = 5.1) : is_natural(x) is not TRUE
try(toy_fun_safe(x = 5.1, all = FALSE)) # Error: is_natural(x) is not TRUE
#> Error in make_natural(x, all = all) :
#> checkinput::is_natural(x) is not TRUE
try(toy_fun_safe(x = 5.1, all = TRUE)) # Error: all_natural(x) is not TRUE
#> Error in make_natural(x, all = all) :
#> checkinput::all_natural(x) is not TRUE