Skip to content

Commit a9697b7

Browse files
authored
Merge pull request #105 from cmu-delphi/head-tail-keep-class
Keep the `epi_df` class when head and tail are used
2 parents 8796ab8 + cd5aa48 commit a9697b7

File tree

5 files changed

+132
-18
lines changed

5 files changed

+132
-18
lines changed

DESCRIPTION

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ Imports:
3737
tidyr,
3838
tidyselect,
3939
tsibble,
40-
utils
40+
utils,
41+
vctrs
4142
Suggests:
4243
covidcast,
4344
delphi.epidata,

NAMESPACE

-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ S3method(as_tsibble,epi_df)
99
S3method(filter,epi_df)
1010
S3method(group_by,epi_df)
1111
S3method(group_modify,epi_df)
12-
S3method(head,epi_df)
1312
S3method(mutate,epi_df)
1413
S3method(print,epi_df)
1514
S3method(relocate,epi_df)
1615
S3method(rename,epi_df)
1716
S3method(slice,epi_df)
1817
S3method(summary,epi_df)
19-
S3method(tail,epi_df)
2018
S3method(ungroup,epi_df)
2119
S3method(unnest,epi_df)
2220
export("%>%")

R/epi_df.R

+3-3
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,9 @@ as_epi_df.tbl_df = function(x, geo_type, time_type, as_of,
278278
if (!("time_value" %in% names(x))) {
279279
Abort("`x` must contain a `time_value` column.")
280280
}
281-
281+
282282
new_epi_df(x, geo_type, time_type, as_of,
283-
additional_metadata = list(), ...)
283+
additional_metadata, ...)
284284
}
285285

286286
#' @method as_epi_df data.frame
@@ -317,4 +317,4 @@ as_epi_df.tbl_ts = function(x, geo_type, time_type, as_of,
317317
#' @export
318318
is_epi_df = function(x) {
319319
inherits(x, "epi_df")
320-
}
320+
}

R/methods-epi_df.R

+54-12
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,62 @@ summary.epi_df = function(object, ...) {
6363
dplyr::summarize(mean(.data$num)))))
6464
}
6565

66-
#' @method head epi_df
67-
#' @importFrom utils head
6866
#' @export
69-
#' @noRd
70-
head.epi_df = function(x, ...) {
71-
head(tibble::as_tibble(x), ...)
72-
}
67+
`[.epi_df` <- function(x, i, j, drop = FALSE) {
68+
res <- NextMethod()
69+
70+
if (!is.data.frame(res)) return(res)
71+
72+
i_arg <- substitute(i)
73+
j_arg <- substitute(j)
74+
75+
if (missing(i)) {
76+
i <- NULL
77+
i_arg <- NULL
78+
} else if (is.null(i)) {
79+
i <- integer()
80+
}
81+
82+
if (missing(j)) {
83+
j <- NULL
84+
j_arg <- NULL
85+
} else if (is.null(j)) {
86+
j <- integer()
87+
}
88+
89+
# Ignore drop as an argument for counting
90+
n_real_args <- nargs() - !missing(drop)
91+
92+
# Case when the number of args (excluding drop) is not 3 or more
93+
if (n_real_args <= 2L) {
94+
j <- i
95+
i <- NULL
96+
j_arg <- i_arg
97+
i_arg <- NULL
98+
}
99+
100+
cn <- names(res)
101+
nr <- vctrs::vec_size(x)
102+
not_epi_df <- (!("time_value" %in% cn) || !("geo_value" %in% cn)
103+
|| vctrs::vec_size(res) > nr || any(i > nr))
73104

74-
#' @method tail epi_df
75-
#' @importFrom utils tail
76-
#' @export
77-
#' @noRd
78-
tail.epi_df = function(x, ...) {
79-
tail(tibble::as_tibble(x), ...)
105+
if (not_epi_df) return(tibble::as_tibble(res))
106+
107+
# Case when i is numeric and there are duplicate values in it
108+
if (is.numeric(i) && vctrs::vec_duplicate_any(i) > 0)
109+
return(tibble::as_tibble(res))
110+
111+
# Column subsetting only, then return res as tibble
112+
if (rlang::is_null(i) && !rlang::is_null(j))
113+
return(tibble::as_tibble(res))
114+
115+
att_x = attr(x, "metadata")
116+
new_epi_df(tibble::as_tibble(res),
117+
geo_type = att_x$geo_type,
118+
time_type = att_x$time_type,
119+
as_of = att_x$as_of,
120+
additional_metadata =
121+
att_x[!(names(att_x) %in% c("geo_type", "time_type", "as_of"))])
80122
}
81123

82124
#' `dplyr` verbs

tests/testthat/test-methods-epi_df.R

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
toy_epi_df <- tibble::tibble(
2+
x = 1:10,
3+
y = 1:10,
4+
time_value = rep(seq(
5+
as.Date("2020-01-01"),
6+
by = 1,
7+
length.out = 5
8+
), times = 2),
9+
geo_value = rep(c("ca", "hi"), each = 5),
10+
indicator_var = as.factor(rep(1:2, times = 5)),
11+
) %>% as_epi_df(additional_metadata = c(other_keys = "indicator_var"))
12+
13+
att_toy = attr(toy_epi_df, "metadata")
14+
15+
test_that("head and tail do not drop the epi_df class", {
16+
att_head = attr(head(toy_epi_df), "metadata")
17+
att_tail = attr(tail(toy_epi_df), "metadata")
18+
19+
expect_true(is_epi_df(head(toy_epi_df)))
20+
expect_true(is_epi_df(tail(toy_epi_df)))
21+
expect_identical(att_head$geo_type, att_toy$geo_type)
22+
expect_identical(att_head$time_type, att_toy$time_type)
23+
expect_identical(att_head$as_of, att_toy$as_of)
24+
expect_identical(att_head$other_keys, att_toy$other_keys)
25+
expect_identical(att_tail$geo_type, att_toy$geo_type)
26+
expect_identical(att_tail$time_type, att_toy$time_type)
27+
expect_identical(att_tail$as_of, att_toy$as_of)
28+
expect_identical(att_tail$other_keys, att_toy$other_keys)
29+
})
30+
31+
32+
test_that("subsetting drops or does not drop the epi_df class appropriately", {
33+
34+
# Row subset - should be epi_df
35+
row_subset = toy_epi_df[1:2, ]
36+
att_row_subset = attr(row_subset, "metadata")
37+
38+
expect_true(is_epi_df(row_subset))
39+
expect_equal(nrow(row_subset), 2L)
40+
expect_equal(ncol(row_subset), 5L)
41+
expect_identical(att_row_subset$geo_type, att_toy$geo_type)
42+
expect_identical(att_row_subset$time_type, att_toy$time_type)
43+
expect_identical(att_row_subset$as_of, att_toy$as_of)
44+
expect_identical(att_row_subset$other_keys, att_toy$other_keys)
45+
46+
# Col subset - shouldn't be an epi_df
47+
col_subset = toy_epi_df[, 2:3]
48+
49+
expect_false(is_epi_df(col_subset))
50+
expect_true(tibble::is_tibble(col_subset))
51+
expect_equal(nrow(col_subset), 10L)
52+
expect_equal(ncol(col_subset), 2L)
53+
54+
# Row and col single value - shouldn't be an epi_df
55+
row_col_subset1 = toy_epi_df[1,2]
56+
expect_false(is_epi_df(row_col_subset1))
57+
expect_true(tibble::is_tibble(row_col_subset1))
58+
expect_equal(nrow(row_col_subset1), 1L)
59+
expect_equal(ncol(row_col_subset1), 1L)
60+
61+
# Row and col subset that contains geo_value and time_value - should be epi_df
62+
row_col_subset2 = toy_epi_df[2:3,1:3]
63+
att_row_col_subset2 = attr(row_col_subset2, "metadata")
64+
65+
expect_true(is_epi_df(row_col_subset2))
66+
expect_equal(nrow(row_col_subset2), 2L)
67+
expect_equal(ncol(row_col_subset2), 3L)
68+
expect_identical(att_row_col_subset2$geo_type, att_toy$geo_type)
69+
expect_identical(att_row_col_subset2$time_type, att_toy$time_type)
70+
expect_identical(att_row_col_subset2$as_of, att_toy$as_of)
71+
expect_identical(att_row_col_subset2$other_keys, att_toy$other_keys)
72+
73+
})

0 commit comments

Comments
 (0)