Skip to content

Commit aecb7e5

Browse files
authored
Merge pull request #413 from cmu-delphi/ds/error-msg
refactor: clean up error messages
2 parents 7ae9e87 + d46aa2f commit aecb7e5

23 files changed

+436
-547
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: epiprocess
33
Title: Tools for basic signal processing in epidemiology
4-
Version: 0.7.3
4+
Version: 0.7.4
55
Authors@R: c(
66
person("Jacob", "Bien", role = "ctb"),
77
person("Logan", "Brooks", role = "aut"),

NAMESPACE

+14
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,24 @@ export(ungroup)
6969
export(unnest)
7070
importFrom(R6,R6Class)
7171
importFrom(checkmate,anyInfinite)
72+
importFrom(checkmate,anyMissing)
7273
importFrom(checkmate,assert)
7374
importFrom(checkmate,assert_character)
75+
importFrom(checkmate,assert_class)
76+
importFrom(checkmate,assert_data_frame)
7477
importFrom(checkmate,assert_int)
78+
importFrom(checkmate,assert_list)
79+
importFrom(checkmate,assert_logical)
80+
importFrom(checkmate,assert_numeric)
81+
importFrom(checkmate,assert_scalar)
82+
importFrom(checkmate,check_atomic)
83+
importFrom(checkmate,check_data_frame)
84+
importFrom(checkmate,test_set_equal)
85+
importFrom(checkmate,test_subset)
86+
importFrom(checkmate,vname)
87+
importFrom(cli,cli_abort)
7588
importFrom(cli,cli_inform)
89+
importFrom(cli,cli_warn)
7690
importFrom(data.table,":=")
7791
importFrom(data.table,address)
7892
importFrom(data.table,as.data.table)

NEWS.md

+144-143
Large diffs are not rendered by default.

R/archive.R

+93-117
Large diffs are not rendered by default.

R/correlation.R

+3-4
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,11 @@
7878
epi_cor <- function(x, var1, var2, dt1 = 0, dt2 = 0, shift_by = geo_value,
7979
cor_by = geo_value, use = "na.or.complete",
8080
method = c("pearson", "kendall", "spearman")) {
81-
# Check we have an `epi_df` object
82-
if (!inherits(x, "epi_df")) Abort("`x` must be of class `epi_df`.")
81+
assert_class(x, "epi_df")
8382

8483
# Check that we have variables to do computations on
85-
if (missing(var1)) Abort("`var1` must be specified.")
86-
if (missing(var2)) Abort("`var2` must be specified.")
84+
if (missing(var1)) cli_abort("`var1` must be specified.")
85+
if (missing(var2)) cli_abort("`var2` must be specified.")
8786
var1 <- enquo(var1)
8887
var2 <- enquo(var2)
8988

R/data.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ delayed_assign_with_unregister_awareness <- function(x, value,
144144
# all.)
145145
rlang::eval_bare(rlang::quo_get_expr(value_quosure), rlang::quo_get_env(value_quosure)),
146146
error = function(err) {
147-
Abort(
147+
cli_abort(
148148
paste(
149149
"An error was raised while attempting to evaluate a promise",
150150
"(prepared with `delayed_assign_with_unregister_awareness`)",

R/epi_df.R

+5-14
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,9 @@ NULL
114114
#' @export
115115
new_epi_df <- function(x = tibble::tibble(), geo_type, time_type, as_of,
116116
additional_metadata = list(), ...) {
117-
# Check that we have a data frame
118-
if (!is.data.frame(x)) {
119-
Abort("`x` must be a data frame.")
120-
}
117+
assert_data_frame(x)
118+
assert_list(additional_metadata)
121119

122-
if (!is.list(additional_metadata)) {
123-
Abort("`additional_metadata` must be a list type.")
124-
}
125120
if (is.null(additional_metadata[["other_keys"]])) {
126121
additional_metadata[["other_keys"]] <- character(0L)
127122
}
@@ -302,13 +297,9 @@ as_epi_df.epi_df <- function(x, ...) {
302297
#' @export
303298
as_epi_df.tbl_df <- function(x, geo_type, time_type, as_of,
304299
additional_metadata = list(), ...) {
305-
# Check that we have geo_value and time_value columns
306-
if (!("geo_value" %in% names(x))) {
307-
Abort("`x` must contain a `geo_value` column.")
308-
}
309-
if (!("time_value" %in% names(x))) {
310-
Abort("`x` must contain a `time_value` column.")
311-
}
300+
if (!test_subset(c("geo_value", "time_value"), names(x))) cli_abort(
301+
"Columns `geo_value` and `time_value` must be present in `x`."
302+
)
312303

313304
new_epi_df(
314305
x, geo_type, time_type, as_of,

R/epiprocess.R

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
#' measured over space and time, and offers associated utilities to perform
55
#' basic signal processing tasks.
66
#'
7-
#' @importFrom checkmate assert assert_character assert_int anyInfinite
7+
#' @importFrom checkmate assert assert_scalar assert_data_frame anyMissing
8+
#' assert_logical assert_list assert_character assert_class
9+
#' assert_int assert_numeric check_data_frame vname check_atomic
10+
#' anyInfinite test_subset test_set_equal
11+
#' @importFrom cli cli_abort cli_inform cli_warn
812
#' @name epiprocess
913
"_PACKAGE"
1014
utils::globalVariables(c(".x", ".group_key", ".ref_time_value"))

R/grouped_epi_archive.R

+28-58
Original file line numberDiff line numberDiff line change
@@ -53,41 +53,24 @@ grouped_epi_archive <-
5353
public = list(
5454
initialize = function(ungrouped, vars, drop) {
5555
if (inherits(ungrouped, "grouped_epi_archive")) {
56-
Abort("`ungrouped` must not already be grouped (neither automatic regrouping nor nested grouping is supported). Either use `group_by` with `.add=TRUE`, or `ungroup` first.",
56+
cli_abort("`ungrouped` must not already be grouped (neither automatic regrouping nor nested grouping is supported). Either use `group_by` with `.add=TRUE`, or `ungroup` first.",
5757
class = "epiprocess__grouped_epi_archive__ungrouped_arg_is_already_grouped",
5858
epiprocess__ungrouped_class = class(ungrouped),
5959
epiprocess__ungrouped_groups = groups(ungrouped)
6060
)
6161
}
62-
if (!inherits(ungrouped, "epi_archive")) {
63-
Abort("`ungrouped` must be an epi_archive",
64-
class = "epiprocess__grouped_epi_archive__ungrouped_arg_is_not_epi_archive",
65-
epiprocess__ungrouped_class = class(ungrouped)
66-
)
67-
}
68-
if (!is.character(vars)) {
69-
Abort("`vars` must be a character vector (any tidyselection should have already occurred in a helper method).",
70-
class = "epiprocess__grouped_epi_archive__vars_is_not_chr",
71-
epiprocess__vars_class = class(vars),
72-
epiprocess__vars_type = typeof(vars)
73-
)
74-
}
75-
if (!all(vars %in% names(ungrouped$DT))) {
76-
Abort("`vars` must be selected from the names of columns of `ungrouped$DT`",
77-
class = "epiprocess__grouped_epi_archive__vars_contains_invalid_entries",
78-
epiprocess__vars = vars,
79-
epiprocess__DT_names = names(ungrouped$DT)
62+
assert_class(ungrouped, "epi_archive")
63+
assert_character(vars)
64+
if (!test_subset(vars, names(ungrouped$DT))) {
65+
cli_abort(
66+
"All grouping variables `vars` must be present in the data.",
8067
)
8168
}
8269
if ("version" %in% vars) {
83-
Abort("`version` has a special interpretation and cannot be used by itself as a grouping variable")
84-
}
85-
if (!rlang::is_bool(drop)) {
86-
Abort("`drop` must be a Boolean",
87-
class = "epiprocess__grouped_epi_archive__drop_is_not_bool",
88-
epiprocess__drop = drop
89-
)
70+
cli_abort("`version` has a special interpretation and cannot be used by itself as a grouping variable")
9071
}
72+
assert_logical(drop, len = 1)
73+
9174
# -----
9275
private$ungrouped <- ungrouped
9376
private$vars <- vars
@@ -136,11 +119,9 @@ grouped_epi_archive <-
136119
invisible(self)
137120
},
138121
group_by = function(..., .add = FALSE, .drop = dplyr::group_by_drop_default(self)) {
139-
if (!rlang::is_bool(.add)) {
140-
Abort("`.add` must be a Boolean")
141-
}
122+
assert_logical(.add, len = 1)
142123
if (!.add) {
143-
Abort('`group_by` on a `grouped_epi_archive` with `.add=FALSE` is forbidden
124+
cli_abort('`group_by` on a `grouped_epi_archive` with `.add=FALSE` is forbidden
144125
(neither automatic regrouping nor nested grouping is supported).
145126
If you want to "regroup", replacing the existing grouping vars, `ungroup` first and then `group_by`.
146127
If you want to add to the existing grouping vars, call `group_by` specifying `.add=TRUE`.
@@ -210,7 +191,7 @@ grouped_epi_archive <-
210191
# early development versions and much more likely to be clutter than
211192
# informative in the signature.
212193
if ("group_by" %in% nse_dots_names(...)) {
213-
Abort("
194+
cli_abort("
214195
The `group_by` argument to `slide` has been removed; please use
215196
the `group_by` S3 generic function or `$group_by` R6 method
216197
before the slide instead. (If you were instead trying to pass a
@@ -221,7 +202,7 @@ grouped_epi_archive <-
221202
", class = "epiprocess__epix_slide_group_by_parameter_deprecated")
222203
}
223204
if ("all_rows" %in% nse_dots_names(...)) {
224-
Abort("
205+
cli_abort("
225206
The `all_rows` argument has been removed from `epix_slide` (but
226207
is still supported in `epi_slide`). Add rows for excluded
227208
results with a manual join instead.
@@ -230,32 +211,29 @@ grouped_epi_archive <-
230211

231212
if (missing(ref_time_values)) {
232213
ref_time_values <- epix_slide_ref_time_values_default(private$ungrouped)
233-
} else if (length(ref_time_values) == 0L) {
234-
Abort("`ref_time_values` must have at least one element.")
235-
} else if (any(is.na(ref_time_values))) {
236-
Abort("`ref_time_values` must not include `NA`.")
237-
} else if (anyDuplicated(ref_time_values) != 0L) {
238-
Abort("`ref_time_values` must not contain any duplicates; use `unique` if appropriate.")
239-
} else if (any(ref_time_values > private$ungrouped$versions_end)) {
240-
Abort("All `ref_time_values` must be `<=` the `versions_end`.")
241214
} else {
215+
assert_numeric(ref_time_values, min.len = 1L, null.ok = FALSE, any.missing = FALSE)
216+
if (any(ref_time_values > private$ungrouped$versions_end)) {
217+
cli_abort("Some `ref_time_values` are greater than the latest version in the archive.")
218+
}
219+
if (anyDuplicated(ref_time_values) != 0L) {
220+
cli_abort("Some `ref_time_values` are duplicated.")
221+
}
242222
# Sort, for consistency with `epi_slide`, although the current
243223
# implementation doesn't take advantage of it.
244224
ref_time_values <- sort(ref_time_values)
245225
}
246226

247227
# Validate and pre-process `before`:
248228
if (missing(before)) {
249-
Abort("`before` is required (and must be passed by name);
229+
cli_abort("`before` is required (and must be passed by name);
250230
if you did not want to apply a sliding window but rather
251231
to map `as_of` and `f` across various `ref_time_values`,
252232
pass a large `before` value (e.g., if time steps are days,
253233
`before=365000`).")
254234
}
255235
before <- vctrs::vec_cast(before, integer())
256-
if (length(before) != 1L || is.na(before) || before < 0L) {
257-
Abort("`before` must be length-1, non-NA, non-negative.")
258-
}
236+
assert_int(before, lower = 0L, null.ok = FALSE, na.ok = FALSE)
259237

260238
# If a custom time step is specified, then redefine units
261239

@@ -265,15 +243,9 @@ grouped_epi_archive <-
265243
new_col <- sym(new_col_name)
266244

267245
# Validate rest of parameters:
268-
if (!rlang::is_bool(as_list_col)) {
269-
Abort("`as_list_col` must be TRUE or FALSE.")
270-
}
271-
if (!(rlang::is_string(names_sep) || is.null(names_sep))) {
272-
Abort("`names_sep` must be a (single) string or NULL.")
273-
}
274-
if (!rlang::is_bool(all_versions)) {
275-
Abort("`all_versions` must be TRUE or FALSE.")
276-
}
246+
assert_logical(as_list_col, len = 1L)
247+
assert_logical(all_versions, len = 1L)
248+
assert_character(names_sep, len = 1L, null.ok = TRUE)
277249

278250
# Computation for one group, one time value
279251
comp_one_grp <- function(.data_group, .group_key,
@@ -290,9 +262,7 @@ grouped_epi_archive <-
290262
.data_group <- .data_group$DT
291263
}
292264

293-
if (!(is.atomic(comp_value) || is.data.frame(comp_value))) {
294-
Abort("The slide computation must return an atomic vector or a data frame.")
295-
}
265+
assert(check_atomic(comp_value, any.missing = TRUE), check_data_frame(comp_value), combine = "or", .var.name = vname(comp_value))
296266

297267
# Label every result row with the `ref_time_value`
298268
res <- list(time_value = ref_time_value)
@@ -312,10 +282,10 @@ grouped_epi_archive <-
312282
if (missing(f)) {
313283
quos <- enquos(...)
314284
if (length(quos) == 0) {
315-
Abort("If `f` is missing then a computation must be specified via `...`.")
285+
cli_abort("If `f` is missing then a computation must be specified via `...`.")
316286
}
317287
if (length(quos) > 1) {
318-
Abort("If `f` is missing then only a single computation can be specified via `...`.")
288+
cli_abort("If `f` is missing then only a single computation can be specified via `...`.")
319289
}
320290

321291
f <- quos[[1]]

R/growth_rate.R

+4-6
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,8 @@ growth_rate <- function(x = seq_along(y), y, x0 = x,
118118
h = 7, log_scale = FALSE,
119119
dup_rm = FALSE, na_rm = FALSE, ...) {
120120
# Check x, y, x0
121-
if (length(x) != length(y)) Abort("`x` and `y` must have the same length.")
122-
if (!all(x0 %in% x)) Abort("`x0` must be a subset of `x`.")
123-
124-
# Check the method
121+
if (length(x) != length(y)) cli_abort("`x` and `y` must have the same length.")
122+
if (!all(x0 %in% x)) cli_abort("`x0` must be a subset of `x`.")
125123
method <- match.arg(method)
126124

127125
# Arrange in increasing order of x
@@ -137,7 +135,7 @@ growth_rate <- function(x = seq_along(y), y, x0 = x,
137135
if (dup_rm) {
138136
o <- !duplicated(x)
139137
if (any(!o)) {
140-
Warn("`x` contains duplicate values. (If being run on a column in an `epi_df`, did you group by relevant key variables?)")
138+
cli_warn("`x` contains duplicate values. (If being run on a column in an `epi_df`, did you group by relevant key variables?)")
141139
}
142140
x <- x[o]
143141
y <- y[o]
@@ -238,7 +236,7 @@ growth_rate <- function(x = seq_along(y), y, x0 = x,
238236
# Check cv and df combo
239237
if (is.numeric(df)) cv <- FALSE
240238
if (!cv && !(is.numeric(df) && df == round(df))) {
241-
Abort("If `cv = FALSE`, then `df` must be an integer.")
239+
cli_abort("If `cv = FALSE`, then `df` must be an integer.")
242240
}
243241

244242
# Compute trend filtering path

0 commit comments

Comments
 (0)