Skip to content

Pass ref time value and group key to epix_slide for tidy computations #317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8ee084f
reference group, ref time by changing quo env
nmdefries May 5, 2023
267c441
set special arg names in func definition
nmdefries May 15, 2023
a3b31bd
import env
nmdefries May 15, 2023
d0fc5b0
describe dots var access
nmdefries May 15, 2023
d0374a1
basic way to calculate ref_time_value in epi_slide
nmdefries May 18, 2023
f2f3436
support using ref_time_value in epi_slide computations
nmdefries May 18, 2023
d1b5d72
drop .real, clean up names
nmdefries May 19, 2023
a95b2a8
import bind_rows
nmdefries May 19, 2023
05c311f
tests
nmdefries May 19, 2023
cd4c382
add empty rows for all group var combos
nmdefries May 23, 2023
51b13a6
add dot prefix to dots arg names
nmdefries May 23, 2023
8bfb4d8
add support for and test sliding on ungrouped epi_dfs
nmdefries May 23, 2023
5564c0d
speed up slow filter and select steps
nmdefries May 24, 2023
22b9b69
tests expect epi_df output
nmdefries May 24, 2023
0933801
In `epix_slide` tidyeval, use custom data mask rather than quo env
lcbrooks Jun 1, 2023
4410a39
Merge branch 'ndefries/epix-slide-pass-reftimevalue-without-env' into…
nmdefries Jun 5, 2023
e644624
import env
nmdefries Jun 5, 2023
feb65b3
some tests expect a tibble
nmdefries Jun 5, 2023
620648e
add placeholders for .x, .group_key, and .rtv
nmdefries Jun 5, 2023
e9580d3
test helper function behavior
nmdefries Jun 5, 2023
b7d7c53
test reftimevalue access on ungrouped epidf
nmdefries Jun 6, 2023
4aa59cb
Merge branch 'ndefries/epix-slide-dots-rtv' into ndefries/epi-slide-rtv
nmdefries Jun 6, 2023
e98e03d
expect output date col as date not double
nmdefries Jun 6, 2023
fed6f11
filter result by .real when all_rows == TRUE
nmdefries Jun 6, 2023
9ccfe91
keep .real col from being accessible from user f computation
nmdefries Jun 6, 2023
307fb6e
use custom data mask rather than quo env to match `epix_slide` tidyev…
nmdefries Jun 7, 2023
0b48ea4
test helper function behavior
nmdefries Jun 7, 2023
9db1165
rename inrange... for phony helper dates
nmdefries Jun 7, 2023
db00432
ci: set DELPHI_EPIDATA_KEY, from a GitHub Secret
lcbrooks Jun 9, 2023
f79bc08
ci: annotate actions explaining differences from usethis output
lcbrooks Jun 9, 2023
aeb9a25
cleanup: add renv-related entries to .Rbuildignore
lcbrooks Jun 9, 2023
d8a95c7
Merge pull request #313 from cmu-delphi/ndefries/epix-slide-pass-reft…
brookslogan Jun 9, 2023
1d26905
ci: note more edits in comments for GitHub Actions
lcbrooks Jun 9, 2023
572f6e6
Merge pull request #328 from cmu-delphi/lcb/use-api-key
brookslogan Jun 9, 2023
a4c19f7
Install `.x` etc. into data masks in same way pronouns are
brookslogan Jun 15, 2023
512f08a
Simplify&flatten epi_slide data mask implementation
brookslogan Jun 16, 2023
d22b88b
Describe our pronoun-like objects in `epi_slide` `...` docs
brookslogan Jun 16, 2023
39f4d2d
Merge pull request #318 from cmu-delphi/ndefries/epi-slide-rtv
brookslogan Jun 16, 2023
e7c4b8d
Rework NEWS.md about `.ref_time_value` and almost-pronouns available
brookslogan Jun 16, 2023
6039646
Merge remote-tracking branch 'upstream/dev' into ndefries/epix-slide-…
brookslogan Jun 16, 2023
e047a4a
Fix grammar in `epix_slide` `...` docs
brookslogan Jun 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
^renv$
^renv\.lock$
^.*\.Rproj$
^\.Rproj\.user$
^LICENSE\.md$
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
#
# Created with usethis + edited to run on PRs to dev, use API key.
on:
push:
branches: [main, master]
Expand Down Expand Up @@ -27,3 +29,5 @@ jobs:
needs: check

- uses: r-lib/actions/check-r-package@v2
env:
DELPHI_EPIDATA_KEY: ${{ secrets.SECRET_EPIPROCESS_GHACTIONS_DELPHI_EPIDATA_KEY }}
4 changes: 4 additions & 0 deletions .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
#
# Created with usethis + edited to run on PRs to dev, use API key.
on:
push:
branches: [main, master]
Expand Down Expand Up @@ -34,6 +36,8 @@ jobs:
needs: website

- name: Build site
env:
DELPHI_EPIDATA_KEY: ${{ secrets.SECRET_EPIPROCESS_GHACTIONS_DELPHI_EPIDATA_KEY }}
run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
shell: Rscript {0}

Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ importFrom(data.table,key)
importFrom(data.table,set)
importFrom(data.table,setkeyv)
importFrom(dplyr,arrange)
importFrom(dplyr,bind_rows)
importFrom(dplyr,dplyr_col_modify)
importFrom(dplyr,dplyr_reconstruct)
importFrom(dplyr,dplyr_row_slice)
importFrom(dplyr,filter)
importFrom(dplyr,group_by)
importFrom(dplyr,group_by_drop_default)
importFrom(dplyr,group_modify)
importFrom(dplyr,group_vars)
importFrom(dplyr,groups)
importFrom(dplyr,mutate)
importFrom(dplyr,relocate)
Expand All @@ -97,6 +99,7 @@ importFrom(rlang,caller_env)
importFrom(rlang,check_dots_empty0)
importFrom(rlang,enquo)
importFrom(rlang,enquos)
importFrom(rlang,env)
importFrom(rlang,f_env)
importFrom(rlang,f_rhs)
importFrom(rlang,global_env)
Expand Down
30 changes: 20 additions & 10 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,31 @@ inter-release development versions will include an additional ".9999" suffix.

## Breaking changes:

* Changes to `epix_slide`:
* The `f` computation is now required to take at least three arguments. `f`
must take an `epi_df` with the same column names as the archive's `DT`,
* Changes to `epi_slide` and `epix_slide`:
* If `f` is a function, it is now required to take at least three arguments.
`f` must take an `epi_df` with the same column names as the archive's `DT`,
minus the `version` column; followed by a one-row tibble containing the
values of the grouping variables for the associated group; followed by a
reference time value, usually as a `Date` object; followed by any number
of named arguments.
reference time value, usually as a `Date` object. Optionally, it can take
any number of additional arguments after that, and forward values for those
arguments through `epi[x]_slide`'s `...` args.
* To make your existing slide computations work, add a third argument to
your `f` function to accept this new input: e.g., change `f = function(x,
g, <any other arguments>) { <body> }` to `f = function(x, g, rt, <any
other arguments>) { <body> }`.

## New features:

* `epi_slide` and `epix_slide` also make the window data, group key and reference
time value available to slide computations specified as formulas or tidy
evaluation expressions, in additional or completely new ways.
* If `f` is a formula, it can now access the reference time value via `.z` or
`.ref_time_value`.
* If `f` is missing, the tidy evaluation expression in `...` can now refer to
the window data as an `epi_df` or `tibble` with `.x`, the group key with
`.group_key`, and the reference time value with `.ref_time_value`. The usual
`.data` and `.env` pronouns also work, but`pick()` and `cur_data()` are not;
work off of `.x` instead.
* `epix_slide` has been made more like `dplyr::group_modify`. It will no longer
perform element/row recycling for size stability, accepts slide computation
outputs containing any number of rows, and no longer supports `all_rows`.
Expand All @@ -29,11 +44,6 @@ inter-release development versions will include an additional ".9999" suffix.
more closely whether/when/how to output an `epi_df`.
* To keep the old behavior, convert the output of `epix_slide()` to `epi_df`
when desired and set the metadata appropriately.
* `epix_slide` `f` computations passed as functions or formulas now have
access to the reference time value. If `f` is a function, it is passed a
Date containing the reference time value as the third argument. If a
formula, `f` can access the reference time value via `.z` or
`.ref_time_value`.

## Improvements:

Expand Down
1 change: 1 addition & 0 deletions R/epiprocess.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
#' @docType package
#' @name epiprocess
NULL
utils::globalVariables(c(".x", ".group_key", ".ref_time_value"))
18 changes: 16 additions & 2 deletions R/grouped_epi_archive.R
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ grouped_epi_archive =
#' object. See the documentation for the wrapper function [`epix_slide()`] for
#' details.
#' @importFrom data.table key address
#' @importFrom rlang !! !!! enquo quo_is_missing enquos is_quosure sym syms
#' @importFrom rlang !! !!! enquo quo_is_missing enquos is_quosure sym syms env
slide = function(f, ..., before, ref_time_values,
time_step, new_col_name = "slide_value",
as_list_col = FALSE, names_sep = "_",
Expand Down Expand Up @@ -370,7 +370,21 @@ grouped_epi_archive =
}

quo = quos[[1]]
f = function(x, quo, ...) rlang::eval_tidy(quo, x)
f = function(.x, .group_key, .ref_time_value, quo, ...) {
# Convert to environment to standardize between tibble and R6
# based inputs. In both cases, we should get a simple
# environment with the empty environment as its parent.
data_env = rlang::as_environment(.x)
data_mask = rlang::new_data_mask(bottom = data_env, top = data_env)
data_mask$.data <- rlang::as_data_pronoun(data_mask)
# We'll also install `.x` directly, not as an
# `rlang_data_pronoun`, so that we can, e.g., use more dplyr and
# epiprocess operations.
data_mask$.x = .x
data_mask$.group_key = .group_key
data_mask$.ref_time_value = .ref_time_value
rlang::eval_tidy(quo, data_mask)
}
Copy link
Contributor

@brookslogan brookslogan Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: ([maybe] shared by the old code [or maybe not... keep getting tricked by eval_tidy default env for exprs when using quosures]) env() uses the function execution environment [by default], which handles making .x, .group_key, and .ref_time_value available, but also:

  • probably makes quo available
  • messes with some helper functions:
helper = function(archive_haystack, time_value_needle) {
  archive_haystack %>% epix_slide(has_needle = time_value_needle %in% time_value, before = 365000L)
}
helper(archive_cases_dv_subset, as.Date("2021-01-01"))
  • probably doesn't work if there were another package, e.g., epipredict, that used slides but relied on packages not in epiprocess imports nor attached in the user's GlobalEnv. (The GlobalEnv thing saves us in some other cases, but seems like an artifact that suggests that R didn't have proper package import namespacing at one point and relied on everything being attached via Depends:.)

I'll push an update for this.

Copy link
Contributor Author

@nmdefries nmdefries Jun 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

helper functions

I can see that the helper function example works with your updates and exits with an error with the old code, but I don't understand the difference in scope of the two approaches.

This line

data_mask = rlang::new_data_mask(data_env, top = data_parent_env)
says that only objects included in all environments (inclusive) between top and bottom will be accessible to the quosure -- right? So why is time_value_needle findable?

And how is the helper function example

helper = function(archive_haystack, time_value_needle) {
  archive_haystack %>% epix_slide(has_needle = time_value_needle %in% time_value, before = 365000L)
}
helper(archive_cases_dv_subset, as.Date("2021-01-01"))

different from

archive_haystack <- archive_cases_dv_subset
time_value_needle <- as.Date("2021-01-01")
archive_haystack %>% epix_slide(has_needle = time_value_needle %in% time_value, before = 365000L)

? The issue is more than the fact that we're passing a variable rather than a "raw", unassigned value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: In various places in this package we use :: to use some (but not all) external functions. Why is that? Like here, we're using rlang::new_data_mask but there aren't any conflicting function names in epiprocess or other imported packages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. We haven't really standardized this yet, and it is probably more of a mix because at some point, in new code, I started to try to favor :: in our internal code (but favor no :: in examples) based on previous discussion, but never spent the time and effort to refactor to any standard.

Copy link
Contributor

@brookslogan brookslogan Jun 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, didn't see your first question. eval_tidy makes both the data mask and the quosure environment available to the computation. It looks like rlang probably combines these into a single environment. [In the old approach, the data mask didn't have any of the stuff, and the environment was something like an env() with no bindings -> our f's execution environment -> slide execution environment -> archive internal environment -> epiprocess pkg namespace -> epiprocess imports -> stuff leading to the global namespace for backward compatibility reasons with old old R stuff, which saved us in a lot of cases. In the newer approaches, we're putting stuff in the data mask part and leaving the env alone, which lets it get stuff from the helper execution environment. Maybe we could have gotten similar results by modifying the first approach to not use the default parent env in the env() call, but instead setting its parent to the quosure's original quo_get_env() and manually adding bindings for .x etc. like you did in even older versions.]

library(rlang)
# put in a bunch of duplicate names and tricks to see what eval_tidy actually grabs:
data_env_parent = env(a = 15)
data_env = env(data_env_parent, a = 10, .data = list(a = 8), .env = list(b = 10000))
## data_env = env(data_env_parent, a = 10, .env = list(b = 10000))
data_mask = new_data_mask(bottom = data_env, top = data_env_parent)
data_mask$.custom_pronoun <- as_data_pronoun(fizz = "buzz")
#> Error in as_data_pronoun(fizz = "buzz"): unused argument (fizz = "buzz")
class(data_mask)
#> [1] "environment"
data_mask %>% names()
#> Error in data_mask %>% names(): could not find function "%>%"
class(data_mask$.env) # not sure if this is a real or a placeholder or something else
#> [1] "rlang_ctxt_pronoun"
# ?eval_tidy tells us that it will add the .data and .env pronouns, so maybe
# it's something else?
data_mask %>% env_parent(n = 1L) %>% names()
#> Error in data_mask %>% env_parent(n = 1L) %>% names(): could not find function "%>%"
data_mask %>% env_parent(n = 2L) %>% names()
#> Error in data_mask %>% env_parent(n = 2L) %>% names(): could not find function "%>%"
data_mask %>% env_parent(n = 3L) %>% env_label()
#> Error in data_mask %>% env_parent(n = 3L) %>% env_label(): could not find function "%>%"
# 
get_quo = function(x) {
  .data = list(a = 80000)
  quo(paste(a, .data$a, .data$.data$a, x, .env$x))
}
my_quo = get_quo(42)
# ?new_data_mask tells us that the tidy eval engine will change the parent of
# `top`. So I'd guess that `eval_tidy` is constructing the following environment
# chain: data_mask (holds pronouns etc.) -> data_env -> data_env_parent -> quo_get_env(my_quo) -> our current environment -> our current environment's ancestors
eval_tidy(my_quo, data = data_mask)
#> [1] "10 8  42 42"
# Oops, I forgot to install `.data` pronoun. ?as_data_mask tells us that
# `as_data_mask` automatically installs `.data`, but `new_data_mask` does not.
# Need to check on my suggested edits to make sure we install it properly.

Created on 2023-06-15 by the reprex package (v2.0.1)

Also, I don't think we can use as_data_pronoun for .ref_time_value, it's going to mess up the type, and I don't see what the benefit is anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, looking at this again, I think I overcomplicated it with the top & bottom. I think maybe we should be "installing" our pronoun-like objects into the data mask in the same way as regular pronouns. And regardless, we may need to document differences between the actual-pronoun .data vs. the non-pronoun .x. I'll test this out.

Question: what do you think about disallowing . in the formula interface? We're faced with . meaning something different in formulas vs. tidyeval, or meaning something different between slides and mutate/etc. We can't make these all consistent, but we can decide on whether the inconsistency is between two different non-error behaviors, one thing working & the other forbidden, or both forbidden.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wanted . to be available in the formula interface so that it matches standard formula behavior, i.e. with access to the first arg via ., .x, and ..1. (In reality, I'm not sure . will be used much.)

. by itself doesn't mean anything in tidy expressions, right? It's just a prefix to some pronoun/var names. Unless there's a subtlety I'm missing, it doesn't seem inconsistent or confusing to me.

new_col = sym(names(rlang::quos_auto_name(quos)))

x = purrr::map_dfr(ref_time_values, function(ref_time_value) {
Expand Down
5 changes: 4 additions & 1 deletion R/methods-epi_archive.R
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,10 @@ group_by.epi_archive = function(.data, ..., .add=FALSE, .drop=dplyr::group_by_dr
#' computation.
#' @param ... Additional arguments to pass to the function or formula specified
#' via `f`. Alternatively, if `f` is missing, then `...` is interpreted as an
#' expression for tidy evaluation. See details of [`epi_slide`].
#' expression for tidy evaluation; in addition to referring to columns
#' directly by name, the expression has access to `.data` and `.env` pronouns
#' as in `dplyr` verbs, and can also refer to the `.group_key` and
#' `.ref_time_value`. See details of [`epi_slide`].
#' @param before How far `before` each `ref_time_value` should the sliding
#' window extend? If provided, should be a single, non-NA,
#' [integer-compatible][vctrs::vec_cast] number of time steps. This window
Expand Down
81 changes: 71 additions & 10 deletions R/slide.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
#' If `f` is missing, then `...` will specify the computation.
#' @param ... Additional arguments to pass to the function or formula specified
#' via `f`. Alternatively, if `f` is missing, then the `...` is interpreted as
#' an expression for tidy evaluation. See details.
#' an expression for tidy evaluation; in addition to referring to columns
#' directly by name, the expression has access to `.data` and `.env` pronouns
#' as in `dplyr` verbs, and can also refer to `.x`, `.group_key`, and
#' `.ref_time_value`. See details.
#' @param before,after How far `before` and `after` each `ref_time_value` should
#' the sliding window extend? At least one of these two arguments must be
#' provided; the other's default will be 0. Any value provided for either
Expand Down Expand Up @@ -119,7 +122,8 @@
#' through the `new_col_name` argument.
#'
#' @importFrom lubridate days weeks
#' @importFrom rlang .data .env !! enquo enquos sym
#' @importFrom dplyr bind_rows group_vars filter select
#' @importFrom rlang .data .env !! enquo enquos sym env
#' @export
#' @examples
#' # slide a 7-day trailing average formula on cases
Expand Down Expand Up @@ -166,11 +170,8 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,

# Check that `f` takes enough args
if (!missing(f) && is.function(f)) {
assert_sufficient_f_args(f, ...)
assert_sufficient_f_args(f, ..., n_mandatory_f_args = 3L)
}

# Arrange by increasing time_value
x = arrange(x, time_value)

if (missing(ref_time_values)) {
ref_time_values = unique(x$time_value)
Expand Down Expand Up @@ -231,6 +232,35 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
after <- time_step(after)
}

min_ref_time_values = ref_time_values - before
min_ref_time_values_not_in_x <- min_ref_time_values[!(min_ref_time_values %in% unique(x$time_value))]

# Do set up to let us recover `ref_time_value`s later.
# A helper column marking real observations.
x$.real = TRUE

# Create df containing phony data. Df has the same columns and attributes as
# `x`, but filled with `NA`s aside from grouping columns. Number of rows is
# equal to the number of `min_ref_time_values_not_in_x` we have * the
# number of unique levels seen in the grouping columns.
before_time_values_df = data.frame(time_value=min_ref_time_values_not_in_x)
if (length(group_vars(x)) != 0) {
before_time_values_df = dplyr::cross_join(
# Get unique combinations of grouping columns seen in real data.
unique(x[, group_vars(x)]),
before_time_values_df
)
}
# Automatically fill in all other columns from `x` with `NA`s, and carry
# attributes over to new df.
before_time_values_df <- bind_rows(x[0,], before_time_values_df)
before_time_values_df$.real <- FALSE

x <- bind_rows(before_time_values_df, x)

# Arrange by increasing time_value
x = arrange(x, time_value)

# Now set up starts and stops for sliding/hopping
time_range = range(unique(x$time_value))
starts = in_range(ref_time_values - before, time_range)
Expand Down Expand Up @@ -272,7 +302,9 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
o = .data_group$time_value %in% time_values
num_ref_rows = sum(o)

# Count the number of appearances of each reference time value
# Count the number of appearances of each reference time value (these
# appearances should all be real for now, but if we allow ref time values
# outside of .data_group's time values):
counts = .data_group %>%
dplyr::filter(.data$time_value %in% time_values) %>%
dplyr::count(.data$time_value) %>%
Expand All @@ -282,7 +314,7 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
!all(purrr::map_lgl(slide_values_list, is.data.frame))) {
Abort("The slide computations must return always atomic vectors or data frames (and not a mix of these two structures).")
}

# Unlist if appropriate:
slide_values =
if (as_list_col) {
Expand Down Expand Up @@ -318,16 +350,24 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
# fills with NA equivalent.
vctrs::vec_slice(slide_values, o) = orig_values
} else {
# This implicitly removes phony (`.real` == FALSE) observations.
.data_group = filter(.data_group, o)
}
return(mutate(.data_group, !!new_col := slide_values))
}

# If f is not missing, then just go ahead, slide by group
if (!missing(f)) {
if (rlang::is_formula(f)) f = as_slide_computation(f)
f_rtv_wrapper = function(x, g, ...) {
ref_time_value = min(x$time_value) + before
x <- x[x$.real,]
x$.real <- NULL
f(x, g, ref_time_value, ...)
}
x = x %>%
group_modify(slide_one_grp,
f = f, ...,
f = f_rtv_wrapper, ...,
starts = starts,
stops = stops,
time_values = ref_time_values,
Expand All @@ -347,7 +387,18 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
}

quo = quos[[1]]
f = function(x, quo, ...) rlang::eval_tidy(quo, x)
f = function(.x, .group_key, quo, ...) {
.ref_time_value = min(.x$time_value) + before
.x <- .x[.x$.real,]
.x$.real <- NULL
data_mask = rlang::as_data_mask(.x)
# We'll also install `.x` directly, not as an `rlang_data_pronoun`, so
# that we can, e.g., use more dplyr and epiprocess operations.
data_mask$.x = .x
data_mask$.group_key = .group_key
data_mask$.ref_time_value = .ref_time_value
rlang::eval_tidy(quo, data_mask)
}
new_col = sym(names(rlang::quos_auto_name(quos)))

x = x %>%
Expand All @@ -365,5 +416,15 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
if (!as_list_col) {
x = unnest(x, !!new_col, names_sep = names_sep)
}

# Remove any remaining phony observations. When `all_rows` is TRUE, phony
# observations aren't necessarily removed in `slide_one_grp`.
if (all_rows) {
x <- x[x$.real,]
}

# Drop helper column `.real`.
x$.real <- NULL

return(x)
}
9 changes: 6 additions & 3 deletions man/epi_slide.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions man/epix_slide.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/reexports.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading