11# ' Convert to tibble
22# '
3- # ' Converts an `epi_df` object into a tibble, dropping metadata and any
4- # ' grouping.
3+ # ' Converts an `epi_df` object into a tibble, dropping metadata, any
4+ # ' grouping, and any unrelated classes and attributes .
55# '
66# ' Advanced: if you are working with a third-party package that uses
77# ' `as_tibble()` on `epi_df`s but you actually want them to remain `epi_df`s,
88# ' use `attr(your_epi_df, "decay_to_tibble") <- FALSE` beforehand.
99# '
1010# ' @param x an `epi_df`
11- # ' @inheritParams tibble::as_tibble
12- # ' @importFrom tibble as_tibble
11+ # ' @param ... if present, forwarded to [`tibble::as_tibble`]
12+ # ' @importFrom tibble as_tibble new_tibble
13+ # ' @importFrom rlang dots_n
14+ # ' @importFrom vctrs vec_data vec_size
1315# ' @export
1416as_tibble.epi_df <- function (x , ... ) {
1517 # Note that some versions of `tsibble` overwrite `as_tibble.grouped_df`, which
16- # also impacts grouped `epi_df`s don't rely on `NextMethod()`. Destructure
17- # first instead.
18- destructured <- tibble :: as_tibble(vctrs :: vec_data(x ), ... )
18+ # also impacts grouped `epi_df`s, so don't rely on `NextMethod()`. Destructure
19+ # and redispatch instead.
20+ destructured <- vec_data(x ) # -> data.frame, dropping extra attrs
21+ tbl <- if (dots_n(... ) == 0 &&
22+ is.null(pkgconfig :: get_config(" tibble::rownames" ))) { # nolint: indentation_linter
23+ # perf: new_tibble instead of as_tibble.data.frame which performs
24+ # extra checks whose defaults should be redundant here:
25+ new_tibble(destructured )
26+ # (^ We don't need to provide nrow= as we have >0 columns.)
27+ } else {
28+ as_tibble(destructured , ... )
29+ }
1930 if (attr(x , " decay_to_tibble" ) %|| % TRUE ) {
20- destructured
31+ tbl
2132 } else {
2233 # We specially requested via attr not to decay epi_df-ness but to drop any
23- # grouping.
24- reclass(destructured , attr(x , " metadata" ))
34+ # grouping. (Miscellaneous attrs are also dropped.)
35+ reclass(tbl , attr(x , " metadata" ))
2536 }
2637}
2738
@@ -151,7 +162,30 @@ dplyr_reconstruct.epi_df <- function(data, template) {
151162 # keep any grouping that has been applied:
152163 res <- NextMethod()
153164
154- col_names <- names(res )
165+ reconstruct_light_edf(res , template )
166+ }
167+
168+ # ' Like `dplyr_reconstruct.epi_df` but not recomputing any grouping
169+ # '
170+ # ' In the move to our current not-quite-proper/effective "implementation" of
171+ # ' [`dplyr::dplyr_extending`] for `epi_df`s, we moved a lot of checks in
172+ # ' `dplyr_reconstruct` and used it instead of `reclass()` in various
173+ # ' operations to prevent operations from outputting invalid metadata/classes,
174+ # ' instead of more careful tailored and relevant checks. However, this actually
175+ # ' introduced extra overhead due to `dplyr_reconstruct.epi_df()` passing off to
176+ # ' `dplyr_reconstruct.grouped_df()` when grouped, which assumes that it will
177+ # ' need to / should for safety recompute the groups, even when it'd be safe for
178+ # ' it not to do so. In many operations, we're using `NextMethod()` to dispatch
179+ # ' to `grouped_df` behavior if needed, and it should output something with valid
180+ # ' groupings.
181+ # '
182+ # ' This function serves the original purpose of performing `epi_df`-centric
183+ # ' checks rather than just throwing on potentially-incorrect metadata like
184+ # ' `reclass()`, but without unnecessary `dplyr_reconstruct()` delegation.
185+ # '
186+ # ' @keywords internal
187+ reconstruct_light_edf <- function (data , template ) {
188+ col_names <- names(data )
155189
156190 # Duplicate columns, cli_abort
157191 dup_col_names <- col_names [duplicated(col_names )]
@@ -169,23 +203,23 @@ dplyr_reconstruct.epi_df <- function(data, template) {
169203 if (not_epi_df ) {
170204 # If we're calling on an `epi_df` from one of our own functions, we need to
171205 # decay to a non-`epi_df` result. If `dplyr` is calling, `x` is a tibble,
172- # `res ` is not an `epi_df` yet (but might, e.g., be a `grouped_df`), and we
206+ # `data ` is not an `epi_df` yet (but might, e.g., be a `grouped_df`), and we
173207 # simply need to skip adding the metadata & class. Current `decay_epi_df`
174208 # should work in both cases.
175- return (decay_epi_df(res ))
209+ return (decay_epi_df(data ))
176210 }
177211
178- res <- reclass(res , attr(template , " metadata" ))
212+ data <- reclass(data , attr(template , " metadata" ))
179213
180214 # XXX we may want verify the `geo_type` and `time_type` here. If it's
181215 # significant overhead, we may also want to keep this less strict version
182216 # around and implement some extra S3 methods that use it, when appropriate.
183217
184218 # Amend additional metadata if some other_keys cols are dropped in the subset
185219 old_other_keys <- attr(template , " metadata" )$ other_keys
186- attr(res , " metadata" )$ other_keys <- old_other_keys [old_other_keys %in% col_names ]
220+ attr(data , " metadata" )$ other_keys <- old_other_keys [old_other_keys %in% col_names ]
187221
188- res
222+ data
189223}
190224
191225# ' @export
@@ -196,19 +230,40 @@ dplyr_reconstruct.epi_df <- function(data, template) {
196230 return (res )
197231 }
198232
199- dplyr :: dplyr_reconstruct(res , x )
233+ reconstruct_light_edf(res , x )
234+ }
235+
236+ # ' @export
237+ `[<-.epi_df` <- function (x , i , j , ... , value ) {
238+ res <- NextMethod()
239+
240+ reconstruct_light_edf(res , x )
241+ }
242+
243+ # ' @export
244+ `[[<-.epi_df` <- function (x , i , j , ... , value ) {
245+ res <- NextMethod()
246+
247+ reconstruct_light_edf(res , x )
248+ }
249+
250+ # ' @export
251+ `$<-.epi_df` <- function (x , name , value ) {
252+ res <- NextMethod()
253+
254+ reconstruct_light_edf(res , x )
200255}
201256
202257# ' @importFrom dplyr dplyr_col_modify
203258# ' @export
204259dplyr_col_modify.epi_df <- function (data , cols ) {
205- dplyr :: dplyr_reconstruct (NextMethod(), data )
260+ reconstruct_light_edf (NextMethod(), data )
206261}
207262
208263# ' @importFrom dplyr dplyr_row_slice
209264# ' @export
210265dplyr_row_slice.epi_df <- function (data , i , ... ) {
211- dplyr :: dplyr_reconstruct (NextMethod(), data )
266+ reconstruct_light_edf (NextMethod(), data )
212267}
213268
214269# ' @export
@@ -222,7 +277,7 @@ dplyr_row_slice.epi_df <- function(data, i, ...) {
222277 new_metadata [[" other_keys" ]] <- new_other_keys
223278 }
224279 result <- reclass(NextMethod(), new_metadata )
225- dplyr :: dplyr_reconstruct (result , result )
280+ reconstruct_light_edf (result , result )
226281}
227282
228283# ' @method group_by epi_df
@@ -251,7 +306,7 @@ ungroup.epi_df <- function(x, ...) {
251306# ' @param .keep Boolean; see [`dplyr::group_modify`]
252307# ' @export
253308group_modify.epi_df <- function (.data , .f , ... , .keep = FALSE ) {
254- dplyr :: dplyr_reconstruct (NextMethod(), .data )
309+ reconstruct_light_edf (NextMethod(), .data )
255310}
256311
257312# ' "Complete" an `epi_df`, adding missing rows and/or replacing `NA`s
@@ -331,7 +386,7 @@ group_modify.epi_df <- function(.data, .f, ..., .keep = FALSE) {
331386# ' )
332387# ' @export
333388complete.epi_df <- function (data , ... , fill = list (), explicit = TRUE ) {
334- result <- dplyr :: dplyr_reconstruct (NextMethod(), data )
389+ result <- reconstruct_light_edf (NextMethod(), data )
335390 if (" time_value" %in% names(rlang :: call_match(dots_expand = FALSE )[[" ..." ]])) {
336391 attr(result , " metadata" )$ time_type <- guess_time_type(result $ time_value )
337392 }
@@ -343,7 +398,7 @@ complete.epi_df <- function(data, ..., fill = list(), explicit = TRUE) {
343398# ' @param data an `epi_df`
344399# ' @export
345400unnest.epi_df <- function (data , ... ) {
346- dplyr :: dplyr_reconstruct (NextMethod(), data )
401+ reconstruct_light_edf (NextMethod(), data )
347402}
348403
349404# Simple reclass function
@@ -402,7 +457,7 @@ arrange_row_canonical.default <- function(x, ...) {
402457arrange_row_canonical.epi_df <- function (x , ... ) {
403458 rlang :: check_dots_empty()
404459 cols <- key_colnames(x )
405- x % > % dplyr :: arrange( dplyr :: across( dplyr :: all_of( cols )))
460+ x [ vctrs :: vec_order( x [ cols ]), ]
406461}
407462
408463arrange_col_canonical <- function (x , ... ) {
@@ -421,8 +476,10 @@ arrange_col_canonical.default <- function(x, ...) {
421476# ' @export
422477arrange_col_canonical.epi_df <- function (x , ... ) {
423478 rlang :: check_dots_empty()
424- cols <- key_colnames(x )
425- x %> % dplyr :: relocate(dplyr :: all_of(cols ), .before = 1 )
479+ all_names <- names(x )
480+ key_names <- key_colnames(x )
481+ val_names <- all_names [! all_names %in% key_names ]
482+ x [c(key_names , val_names )]
426483}
427484
428485# ' Group an `epi_df` object by default keys
@@ -432,7 +489,7 @@ arrange_col_canonical.epi_df <- function(x, ...) {
432489# ' @export
433490group_epi_df <- function (x , exclude = character ()) {
434491 cols <- key_colnames(x , exclude = exclude )
435- x % > % group_by(across(all_of( cols )))
492+ reclass(grouped_df( x , cols ), attr( x , " metadata " ))
436493}
437494
438495# ' Aggregate an `epi_df` object
0 commit comments