Skip to content

Commit ec9feff

Browse files
authored
feat: introduce epix_as_of_current() for convenience (#645)
* feat+bug: add epix_as_of_current() and set_versions_end() * fix yearmonth breaking in epix_as_of() and epix_slide()
1 parent 125098d commit ec9feff

10 files changed

+145
-26
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.11.2
4+
Version: 0.11.3
55
Authors@R: c(
66
person("Jacob", "Bien", role = "ctb"),
77
person("Logan", "Brooks", , "[email protected]", role = c("aut", "cre")),

NAMESPACE

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export(epi_slide_mean)
7676
export(epi_slide_opt)
7777
export(epi_slide_sum)
7878
export(epix_as_of)
79+
export(epix_as_of_current)
7980
export(epix_fill_through_version)
8081
export(epix_merge)
8182
export(epix_slide)
@@ -99,6 +100,7 @@ export(new_epi_df)
99100
export(relocate)
100101
export(rename)
101102
export(revision_summary)
103+
export(set_versions_end)
102104
export(slice)
103105
export(sum_groups_epi_df)
104106
export(time_column_names)

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Pre-1.0.0 numbering scheme: 0.x will indicate releases, while 0.x.y will indicat
77
## New features
88

99
- `is_epi_archive` function has been reintroduced.
10+
- `epix_as_of_current()` introduced as an alias for `epix_as_of(.$versions_end)`.
1011

1112
# epiprocess 0.11
1213

R/grouped_epi_archive.R

+5-1
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,13 @@ epix_slide.grouped_epi_archive <- function(
437437
out <- lapply(.versions, function(.version) {
438438
# Ungrouped as-of data; `epi_df` if `all_versions` is `FALSE`,
439439
# `epi_archive` if `all_versions` is `TRUE`:
440+
min_time_value <- .version - .before
441+
if (is.na(min_time_value)) {
442+
min_time_value <- -Inf
443+
}
440444
as_of_raw <- .x$private$ungrouped %>% epix_as_of(
441445
.version,
442-
min_time_value = .version - .before,
446+
min_time_value = min_time_value,
443447
all_versions = .all_versions
444448
)
445449

R/methods-epi_archive.R

+57-21
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#' epix_as_of(archive_cases_dv_subset2, max(archive_cases_dv_subset$DT$version))
5959
#'
6060
#' @importFrom data.table between key
61+
#' @importFrom checkmate assert_scalar assert_logical assert_class
6162
#' @export
6263
epix_as_of <- function(x, version, min_time_value = -Inf, all_versions = FALSE,
6364
max_version = deprecated()) {
@@ -88,6 +89,14 @@ epix_as_of <- function(x, version, min_time_value = -Inf, all_versions = FALSE,
8889
if (version > x$versions_end) {
8990
cli_abort("`version` must be at most `epi_archive$versions_end`.")
9091
}
92+
assert_scalar(min_time_value, na.ok = FALSE)
93+
min_time_value_inf <- is.infinite(min_time_value) && min_time_value < 0
94+
min_time_value_same_type <- typeof(min_time_value) == typeof(x$DT$time_value) &
95+
class(min_time_value) == class(x$DT$time_value)
96+
if (!min_time_value_inf && !min_time_value_same_type) {
97+
cli_abort("`min_time_value` must be either -Inf or a time_value of the same type and
98+
class as `epi_archive$time_value`.")
99+
}
91100
assert_logical(all_versions, len = 1)
92101
if (!is.na(x$clobberable_versions_start) && version >= x$clobberable_versions_start) {
93102
cli_warn(
@@ -100,39 +109,63 @@ epix_as_of <- function(x, version, min_time_value = -Inf, all_versions = FALSE,
100109
)
101110
}
102111

103-
# We can't disable nonstandard evaluation nor use the `..` feature in the `i`
104-
# argument of `[.data.table` below; try to avoid problematic names and abort
105-
# if we fail to do so:
106-
.min_time_value <- min_time_value
107-
.version <- version
108-
if (any(c(".min_time_value", ".version") %in% names(x$DT))) {
109-
cli_abort("epi_archives can't contain a `.min_time_value` or `.version` column")
110-
}
111-
112112
# Filter by version and return
113113
if (all_versions) {
114114
# epi_archive is copied into result, so we can modify result directly
115115
result <- epix_truncate_versions_after(x, version)
116-
result$DT <- result$DT[time_value >= .min_time_value, ] # nolint: object_usage_linter
116+
if (!min_time_value_inf) {
117+
# See below for why we need this branch.
118+
filter_mask <- result$DT$time_value >= min_time_value
119+
result$DT <- result$DT[filter_mask, ] # nolint: object_usage_linter
120+
}
117121
return(result)
118122
}
119123

120124
# Make sure to use data.table ways of filtering and selecting
121-
as_of_epi_df <- x$DT[time_value >= .min_time_value & version <= .version, ] %>% # nolint: object_usage_linter
122-
unique(
123-
by = c("geo_value", "time_value", other_keys),
124-
fromLast = TRUE
125-
) %>%
125+
if (min_time_value_inf) {
126+
# This branch is needed for `epix_as_of` to work with `yearmonth` time type
127+
# to avoid time_value > .min_time_value, which is NA for `yearmonth`.
128+
filter_mask <- x$DT$version <= version
129+
} else {
130+
filter_mask <- x$DT$time_value >= min_time_value & x$DT$version <= version
131+
}
132+
as_of_epi_df <- x$DT[filter_mask, ] %>%
133+
unique(by = c("geo_value", "time_value", other_keys), fromLast = TRUE) %>%
134+
as.data.frame() %>%
126135
tibble::as_tibble() %>%
127136
dplyr::select(-"version") %>%
128-
as_epi_df(
129-
as_of = version,
130-
other_keys = other_keys
131-
)
137+
as_epi_df(as_of = version, other_keys = other_keys)
132138

133139
return(as_of_epi_df)
134140
}
135141

142+
#' Get the latest snapshot from an `epi_archive` object.
143+
#'
144+
#' The latest snapshot is the snapshot of the last known version.
145+
#'
146+
#' @param x An `epi_archive` object
147+
#' @return The latest snapshot from an `epi_archive` object
148+
#' @export
149+
epix_as_of_current <- function(x) {
150+
assert_class(x, "epi_archive")
151+
x %>% epix_as_of(.$versions_end)
152+
}
153+
154+
#' Set the `versions_end` attribute of an `epi_archive` object
155+
#'
156+
#' An escape hatch for epix_as_of, which does not allow version >
157+
#' `$versions_end`.
158+
#'
159+
#' @param x An `epi_archive` object
160+
#' @param versions_end The new `versions_end` value
161+
#' @return An `epi_archive` object with the updated `versions_end` attribute
162+
#' @export
163+
set_versions_end <- function(x, versions_end) {
164+
assert_class(x, "epi_archive")
165+
validate_version_bound(versions_end, x$DT, na_ok = FALSE)
166+
x$versions_end <- versions_end
167+
x
168+
}
136169

137170
#' Fill `epi_archive` unobserved history
138171
#'
@@ -880,10 +913,13 @@ epix_slide.epi_archive <- function(
880913
#' @noRd
881914
epix_slide_versions_default <- function(ea) {
882915
versions_with_updates <- c(ea$DT$version, ea$versions_end)
883-
tidyr::full_seq(versions_with_updates, guess_period(versions_with_updates))
916+
if (ea$time_type == "yearmonth") {
917+
min(versions_with_updates) + seq(0, max(versions_with_updates) - min(versions_with_updates), by = 1)
918+
} else {
919+
tidyr::full_seq(versions_with_updates, guess_period(versions_with_updates))
920+
}
884921
}
885922

886-
887923
#' Filter an `epi_archive` object to keep only older versions
888924
#'
889925
#' Generates a filtered `epi_archive` from an `epi_archive` object, keeping

R/slide.R

+3-3
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@ epi_slide_sum <- function(
11081108
#' `before` and `after` args are assumed to have been validated by the calling
11091109
#' function (using `validate_slide_window_arg`).
11101110
#'
1111-
#' @importFrom checkmate assert_function
1111+
#' @importFrom checkmate assert_function anyInfinite
11121112
#' @keywords internal
11131113
full_date_seq <- function(x, before, after, time_type) {
11141114
if (!time_type %in% c("day", "week", "yearmonth", "integer")) {
@@ -1126,7 +1126,7 @@ full_date_seq <- function(x, before, after, time_type) {
11261126
if (time_type %in% c("yearmonth", "integer")) {
11271127
all_dates <- seq(min(x$time_value), max(x$time_value), by = 1L)
11281128

1129-
if (before != 0 && before != Inf) {
1129+
if (before != 0 && !anyInfinite(before)) {
11301130
pad_early_dates <- all_dates[1L] - before:1
11311131
}
11321132
if (after != 0) {
@@ -1139,7 +1139,7 @@ full_date_seq <- function(x, before, after, time_type) {
11391139
)
11401140

11411141
all_dates <- seq(min(x$time_value), max(x$time_value), by = by)
1142-
if (before != 0 && before != Inf) {
1142+
if (before != 0 && !anyInfinite(before)) {
11431143
# The behavior is analogous to the branch with tsibble types above. For
11441144
# more detail, note that the function `seq.Date(from, ..., length.out =
11451145
# n)` returns `from + 0:n`. Since we want `from + 1:n`, we drop the first

_pkgdown.yml

+2
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,13 @@ reference:
7676
desc: Functions operating on `epi_archive` objects.
7777
- contents:
7878
- epix_as_of
79+
- epix_as_of_current
7980
- epix_slide
8081
- epix_merge
8182
- revision_summary
8283
- epix_fill_through_version
8384
- epix_truncate_versions_after
85+
- set_versions_end
8486

8587
- title: Basic analysis and visualization
8688
- contents:

man/epix_as_of_current.Rd

+17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/set_versions_end.Rd

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-methods-epi_archive.R

+37
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,40 @@ test_that("group_vars works as expected", {
128128
"geo_value"
129129
)
130130
})
131+
132+
test_that("epix_as_of_now works as expected", {
133+
expect_equal(
134+
attr(ea2_data %>% as_epi_archive() %>% epix_as_of_current(), "metadata")$as_of,
135+
as.Date("2020-06-04")
136+
)
137+
time_value <- as.Date("2020-06-01")
138+
df <- dplyr::tribble(
139+
~geo_value, ~time_value, ~version, ~cases,
140+
"ca", time_value, time_value, 1,
141+
"ca", time_value + 7, time_value + 7, 2,
142+
)
143+
expect_equal(
144+
attr(df %>% as_epi_archive() %>% epix_as_of_current(), "metadata")$as_of,
145+
as.Date("2020-06-08")
146+
)
147+
time_value <- tsibble::yearmonth(as.Date("2020-06-01") - lubridate::month(1))
148+
df <- dplyr::tribble(
149+
~geo_value, ~time_value, ~version, ~cases,
150+
"ca", time_value, time_value, 1,
151+
"ca", time_value + lubridate::month(1), time_value + lubridate::month(1), 2,
152+
)
153+
expect_equal(
154+
attr(df %>% as_epi_archive() %>% epix_as_of_current(), "metadata")$as_of,
155+
tsibble::yearmonth("2020-06")
156+
)
157+
time_value <- 2020
158+
df <- dplyr::tribble(
159+
~geo_value, ~time_value, ~version, ~cases,
160+
"ca", time_value, time_value, 1,
161+
"ca", time_value + 7, time_value + 7, 2,
162+
)
163+
expect_equal(
164+
attr(df %>% as_epi_archive() %>% epix_as_of_current(), "metadata")$as_of,
165+
2027
166+
)
167+
})

0 commit comments

Comments
 (0)