|
15 | 15 | #' and any number of named arguments (which will be taken from `...`). If a |
16 | 16 | #' formula, `f` can operate directly on columns accessed via `.x$var`, as |
17 | 17 | #' in `~ mean(.x$var)` to compute a mean of a column var over a sliding |
18 | | -#' window of n time steps. As well, `.y` may be used in the formula to refer |
| 18 | +#' window. As well, `.y` may be used in the formula to refer |
19 | 19 | #' to the groupings that would be described by `g` if `f` was a function. |
20 | 20 | #' @param ... Additional arguments to pass to the function or formula specified |
21 | 21 | #' via `f`. Alternatively, if `f` is missing, then the current argument is |
22 | 22 | #' interpreted as an expression for tidy evaluation. See details. |
23 | | -#' @param before A nonnegative integer specifying the number of time steps |
24 | | -#' before the `ref_time_value` to use in the rolling window. |
25 | | -#' This must be a vector of length 1. |
26 | | -#' Set to 0 for a left-aligned/leading sliding window, meaning that no |
27 | | -#' `time_value` after the slide will be used for the sliding calculation. |
28 | | -#' It is mandatory to specify a `before` value, unless `after` is specified |
29 | | -#' as a non-zero value. In this case, `before` will be assumed to be 0, as it |
30 | | -#' assumes the user wants to do a left-aligned/leading sliding window. |
31 | | -#' For example, if `before = 3`, and one time step is one day, then to produce |
32 | | -#' a value on January 7, we apply the given function or formula to data on |
33 | | -#' January 4 and later (with the latest date dependent on `after`). |
34 | | -#' A warning will be produced if `before` is not specified, but `after` is |
35 | | -#' also not specified or 0. Should this happen, `before` will be set to 0. |
36 | | -#' @param after A nonnegative integer specifying the number of time steps |
37 | | -#' after the `ref_time_value` to use in the rolling window. This must be a |
38 | | -#' vector of length 1. The default value for this is 0. Set to 0 for a |
39 | | -#' right-aligned/trailing sliding window, meaning that no |
40 | | -#' `time_value` before the slide will be used for the sliding calculation. |
41 | | -#' To specify this to be centrally aligned, set `before` and `after` to be |
42 | | -#' the same. |
43 | | -#' For example, if `after = 3`, and one time step is one day, then to produce |
44 | | -#' a value on January 7, we apply the given function or formula to data on |
45 | | -#' January 10 and earlier (with the earliest date dependent on `before`). |
46 | | -#' A warning will be produced if `after` is not specified, but `before` is |
47 | | -#' also not specified or 0. Should this happen, `after` will be set to 0. |
| 23 | +#' @param before,after How far `before` and `after` each `ref_time_value` should |
| 24 | +#' the sliding window extend? At least one of these two arguments must be |
| 25 | +#' provided; the other's default will be 0. Any value provided for either |
| 26 | +#' argument must be a single, non-`NA`, non-negative, |
| 27 | +#' [vctrs:vec_cast][integer-compatible] number of time steps. Endpoints of the |
| 28 | +#' window are inclusive. Common settings: |
| 29 | +#' * For trailing/right-aligned windows from `ref_time_value - time_step(k)` |
| 30 | +#' to `ref_time_value`: either pass `before=k` by itself, or pass `before=k, |
| 31 | +#' after=0`. |
| 32 | +#' * For center-aligned windows from `ref_time_value - time_step(k)` to |
| 33 | +#' `ref_time_value + time_step(k)`: pass `before=k, after=k`. |
| 34 | +#' * For leading/left-aligned windows from `ref_time_value` to `ref_time_value |
| 35 | +#' + time_step(k)`: either pass pass `after=k` by itself, or pass `before=0, |
| 36 | +#' after=k`. |
| 37 | +#' See "Details:" about the definition of a time step, (non)treatment of |
| 38 | +#' missing rows within the window, and avoiding warnings about |
| 39 | +#' `before`&`after` settings for a certain uncommon use case. |
48 | 40 | #' @param ref_time_values Time values for sliding computations, meaning, each |
49 | 41 | #' element of this vector serves as the reference time point for one sliding |
50 | 42 | #' window. If missing, then this will be set to all unique time values in the |
51 | 43 | #' underlying data table, by default. |
52 | 44 | #' @param time_step Optional function used to define the meaning of one time |
53 | 45 | #' step, which if specified, overrides the default choice based on the |
54 | | -#' `time_value` column. This function must take a positive integer and return |
| 46 | +#' `time_value` column. This function must take a non-negative integer and return |
55 | 47 | #' an object of class `lubridate::period`. For example, we can use `time_step |
56 | 48 | #' = lubridate::hours` in order to set the time step to be one hour (this |
57 | 49 | #' would only be meaningful if `time_value` is of class `POSIXct`). |
|
91 | 83 | #' `before = (n-1)/2` and `after = (n-1)/2` when the number of `time_value`s |
92 | 84 | #' in a sliding window is odd and `before = n/2-1` and `after = n/2` when |
93 | 85 | #' `n` is even. |
94 | | -#' |
95 | | -#' If `f` is missing, then an expression for tidy evaluation can be specified, |
96 | | -#' for example, as in: |
| 86 | +#' |
| 87 | +#' Sometimes, we want to experiment with various trailing or leading window |
| 88 | +#' widths and compare the slide outputs. In the (uncommon) case where |
| 89 | +#' zero-width windows are considered, manually pass both the `before` and |
| 90 | +#' `after` arguments in order to prevent potential warnings. (E.g., `before=k` |
| 91 | +#' with `k=0` and `after` missing may produce a warning. To avoid warnings, |
| 92 | +#' use `before=k, after=0` instead; otherwise, it looks too much like a |
| 93 | +#' leading window was intended, but the `after` argument was forgotten or |
| 94 | +#' misspelled.) |
| 95 | +#' |
| 96 | +#' If `f` is missing, then an expression for tidy evaluation can be specified, |
| 97 | +#' for example, as in: |
97 | 98 | #' ``` |
98 | 99 | #' epi_slide(x, cases_7dav = mean(cases), before = 6) |
99 | 100 | #' ``` |
|
121 | 122 | #' # slide a 7-day leading average |
122 | 123 | #' jhu_csse_daily_subset %>% |
123 | 124 | #' group_by(geo_value) %>% |
124 | | -#' epi_slide(cases_7dav = mean(cases), before = 0, after = 6) %>% |
| 125 | +#' epi_slide(cases_7dav = mean(cases), after = 6) %>% |
125 | 126 | #' # rmv a nonessential var. to ensure new col is printed |
126 | 127 | #' dplyr::select(-death_rate_7d_av) |
127 | 128 | #' |
@@ -166,51 +167,43 @@ epi_slide = function(x, f, ..., before, after, ref_time_values, |
166 | 167 | unique(x$time_value)] |
167 | 168 | } |
168 | 169 |
|
169 | | - # We must ensure that both before and after are of length 1 |
170 | | - if ((!missing(after) && length(after) != 1L) || |
171 | | - (!missing(before) && length(before) != 1L)) { |
172 | | - Abort("`before` and `after` must be vectors of length 1.") |
| 170 | + # Validate and pre-process `before`, `after`: |
| 171 | + if (!missing(before)) { |
| 172 | + before <- vctrs::vec_cast(before, integer()) |
| 173 | + if (length(before) != 1L || is.na(before) || before < 0L) { |
| 174 | + Abort("`before` must be length-1, non-NA, non-negative") |
| 175 | + } |
173 | 176 | } |
174 | | - |
175 | | - # Features that are warned due to likely being mistakes: |
176 | | - # `before` missing and `after` set to 0, and vice versa |
177 | | - # Both `before` and `after` missing |
178 | | - warn_before_after <- function(main_msg,set_msg) { |
179 | | - Warn(paste(main_msg,"Did you mean to set `before` or `after`?",set_msg)) |
| 177 | + if (!missing(after)) { |
| 178 | + after <- vctrs::vec_cast(after, integer()) |
| 179 | + if (length(after) != 1L || is.na(after) || after < 0L) { |
| 180 | + Abort("`after` must be length-1, non-NA, non-negative") |
| 181 | + } |
180 | 182 | } |
181 | | - |
182 | 183 | if (missing(before)) { |
183 | 184 | if (missing(after)) { |
184 | | - warn_before_after("`before` and `after` missing.", |
185 | | - "`before` and `after` set to 0.") |
186 | | - after = 0 |
187 | | - } else if (after == 0) { |
188 | | - warn_before_after("`before` missing and `after` set to 0.", |
189 | | - "`before` set to 0.") |
| 185 | + Abort("Either or both of `before`, `after` must be provided.") |
| 186 | + } else if (after == 0L) { |
| 187 | + Warn("`before` missing, `after==0`; maybe this was intended to be some |
| 188 | + non-zero-width trailing window, but since `before` appears to be |
| 189 | + missing, it's interpreted as a zero-width window (`before=0, |
| 190 | + after=0`).") |
190 | 191 | } |
191 | | - before = 0 |
| 192 | + before <- 0L |
192 | 193 | } else if (missing(after)) { |
193 | | - if (before == 0) { |
194 | | - warn_before_after("`before` set to 0 and `after` missing.", |
195 | | - "`after` set to 0.") |
| 194 | + if (before == 0L) { |
| 195 | + Warn("`before==0`, `after` missing; maybe this was intended to be some |
| 196 | + non-zero-width leading window, but since `after` appears to be |
| 197 | + missing, it's interpreted as a zero-width window (`before=0, |
| 198 | + after=0`).") |
196 | 199 | } |
197 | | - after = 0 |
198 | | - } |
199 | | - if (!(is.numeric(before) && is.numeric(after))|| |
200 | | - floor(before) < ceiling(before) || |
201 | | - floor(after) < ceiling(after)) { |
202 | | - Abort("`before` and `after` must be integers.") |
203 | | - } |
204 | | - |
205 | | - # Otherwise set up alignment based on passed before value |
206 | | - if (before < 0 || after < 0) { |
207 | | - Abort("`before` and `after` must be at least 0.") |
| 200 | + after <- 0L |
208 | 201 | } |
209 | 202 |
|
210 | | - # If a custom time step is specified, then redefine units |
| 203 | + # If a custom time step is specified, then redefine units |
211 | 204 | if (!missing(time_step)) { |
212 | | - before = time_step(before) |
213 | | - after = time_step(after) |
| 205 | + before <- time_step(before) |
| 206 | + after <- time_step(after) |
214 | 207 | } |
215 | 208 |
|
216 | 209 | # Now set up starts and stops for sliding/hopping |
|
0 commit comments