1
1
# ' Convert to tibble
2
2
# '
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 .
5
5
# '
6
6
# ' Advanced: if you are working with a third-party package that uses
7
7
# ' `as_tibble()` on `epi_df`s but you actually want them to remain `epi_df`s,
8
8
# ' use `attr(your_epi_df, "decay_to_tibble") <- FALSE` beforehand.
9
9
# '
10
10
# ' @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
13
15
# ' @export
14
16
as_tibble.epi_df <- function (x , ... ) {
15
17
# 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
+ }
19
30
if (attr(x , " decay_to_tibble" ) %|| % TRUE ) {
20
- destructured
31
+ tbl
21
32
} else {
22
33
# 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" ))
25
36
}
26
37
}
27
38
@@ -151,7 +162,30 @@ dplyr_reconstruct.epi_df <- function(data, template) {
151
162
# keep any grouping that has been applied:
152
163
res <- NextMethod()
153
164
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 )
155
189
156
190
# Duplicate columns, cli_abort
157
191
dup_col_names <- col_names [duplicated(col_names )]
@@ -169,23 +203,23 @@ dplyr_reconstruct.epi_df <- function(data, template) {
169
203
if (not_epi_df ) {
170
204
# If we're calling on an `epi_df` from one of our own functions, we need to
171
205
# 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
173
207
# simply need to skip adding the metadata & class. Current `decay_epi_df`
174
208
# should work in both cases.
175
- return (decay_epi_df(res ))
209
+ return (decay_epi_df(data ))
176
210
}
177
211
178
- res <- reclass(res , attr(template , " metadata" ))
212
+ data <- reclass(data , attr(template , " metadata" ))
179
213
180
214
# XXX we may want verify the `geo_type` and `time_type` here. If it's
181
215
# significant overhead, we may also want to keep this less strict version
182
216
# around and implement some extra S3 methods that use it, when appropriate.
183
217
184
218
# Amend additional metadata if some other_keys cols are dropped in the subset
185
219
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 ]
187
221
188
- res
222
+ data
189
223
}
190
224
191
225
# ' @export
@@ -196,19 +230,40 @@ dplyr_reconstruct.epi_df <- function(data, template) {
196
230
return (res )
197
231
}
198
232
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 )
200
255
}
201
256
202
257
# ' @importFrom dplyr dplyr_col_modify
203
258
# ' @export
204
259
dplyr_col_modify.epi_df <- function (data , cols ) {
205
- dplyr :: dplyr_reconstruct (NextMethod(), data )
260
+ reconstruct_light_edf (NextMethod(), data )
206
261
}
207
262
208
263
# ' @importFrom dplyr dplyr_row_slice
209
264
# ' @export
210
265
dplyr_row_slice.epi_df <- function (data , i , ... ) {
211
- dplyr :: dplyr_reconstruct (NextMethod(), data )
266
+ reconstruct_light_edf (NextMethod(), data )
212
267
}
213
268
214
269
# ' @export
@@ -222,7 +277,7 @@ dplyr_row_slice.epi_df <- function(data, i, ...) {
222
277
new_metadata [[" other_keys" ]] <- new_other_keys
223
278
}
224
279
result <- reclass(NextMethod(), new_metadata )
225
- dplyr :: dplyr_reconstruct (result , result )
280
+ reconstruct_light_edf (result , result )
226
281
}
227
282
228
283
# ' @method group_by epi_df
@@ -251,7 +306,7 @@ ungroup.epi_df <- function(x, ...) {
251
306
# ' @param .keep Boolean; see [`dplyr::group_modify`]
252
307
# ' @export
253
308
group_modify.epi_df <- function (.data , .f , ... , .keep = FALSE ) {
254
- dplyr :: dplyr_reconstruct (NextMethod(), .data )
309
+ reconstruct_light_edf (NextMethod(), .data )
255
310
}
256
311
257
312
# ' "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) {
331
386
# ' )
332
387
# ' @export
333
388
complete.epi_df <- function (data , ... , fill = list (), explicit = TRUE ) {
334
- result <- dplyr :: dplyr_reconstruct (NextMethod(), data )
389
+ result <- reconstruct_light_edf (NextMethod(), data )
335
390
if (" time_value" %in% names(rlang :: call_match(dots_expand = FALSE )[[" ..." ]])) {
336
391
attr(result , " metadata" )$ time_type <- guess_time_type(result $ time_value )
337
392
}
@@ -343,7 +398,7 @@ complete.epi_df <- function(data, ..., fill = list(), explicit = TRUE) {
343
398
# ' @param data an `epi_df`
344
399
# ' @export
345
400
unnest.epi_df <- function (data , ... ) {
346
- dplyr :: dplyr_reconstruct (NextMethod(), data )
401
+ reconstruct_light_edf (NextMethod(), data )
347
402
}
348
403
349
404
# Simple reclass function
@@ -402,7 +457,7 @@ arrange_row_canonical.default <- function(x, ...) {
402
457
arrange_row_canonical.epi_df <- function (x , ... ) {
403
458
rlang :: check_dots_empty()
404
459
cols <- key_colnames(x )
405
- x % > % dplyr :: arrange( dplyr :: across( dplyr :: all_of( cols )))
460
+ x [ vctrs :: vec_order( x [ cols ]), ]
406
461
}
407
462
408
463
arrange_col_canonical <- function (x , ... ) {
@@ -421,8 +476,10 @@ arrange_col_canonical.default <- function(x, ...) {
421
476
# ' @export
422
477
arrange_col_canonical.epi_df <- function (x , ... ) {
423
478
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 )]
426
483
}
427
484
428
485
# ' Group an `epi_df` object by default keys
@@ -432,7 +489,7 @@ arrange_col_canonical.epi_df <- function(x, ...) {
432
489
# ' @export
433
490
group_epi_df <- function (x , exclude = character ()) {
434
491
cols <- key_colnames(x , exclude = exclude )
435
- x % > % group_by(across(all_of( cols )))
492
+ reclass(grouped_df( x , cols ), attr( x , " metadata " ))
436
493
}
437
494
438
495
# ' Aggregate an `epi_df` object
0 commit comments