diff --git a/DESCRIPTION b/DESCRIPTION index 024415d..39345ba 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -8,7 +8,8 @@ Authors@R: c( person("Rich", "FitzJohn", role = "aut", email = "rich.fitzjohn@gmail.com"), person("Jun", "Cai", role = "ctb", email = "cai-j12@mails.tsinghua.edu.cn"), person("Sangeeta", "Bhatia", role = "ctb", email = "sangeetabhatia03@gmail.com"), - person("Jakob", "Schumacher", role = "ctb")) + person("Jakob", "Schumacher", role = "ctb"), + person("Juliet R.C.", "Pulliam", role = "ctb", email = "pulliam@sun.ac.za")) Description: Provides functions and classes to compute, handle and visualise incidence from dated events for a defined time interval. Dates can be provided in various standard formats. The class 'incidence' is used to store computed incidence and can be easily manipulated, subsetted, and plotted. In addition, log-linear models can be fitted to 'incidence' objects using 'fit'. This package is part of the RECON () toolkit for outbreak analysis. Encoding: UTF-8 License: MIT + file LICENSE diff --git a/NEWS.md b/NEWS.md index 2b78bb8..1a0168c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,11 +7,14 @@ incidence 1.5.3 (2018-12-07) (See https://github.com/reconhub/incidence/issues/84) * `as.data.frame.incidence()` will now respect single groups. (See https://github.com/reconhub/incidence/issues/84) +* `incidence()` now returns an error when dates argument is character object. + (See https://github.com/reconhub/incidence/issues/88) ### MISC -* `demo("incidence-demo" package = "incidenc")` has been updated to show use of +* `demo("incidence-demo" package = "incidence")` has been updated to show use of custom colors. +* `incidence()` no longer accepts characters as input for dates, first_date, or last_date argeuments incidence 1.5.2 (2018-11-30) ============================ diff --git a/R/check_boundaries.R b/R/check_boundaries.R index 791d3dc..3302257 100644 --- a/R/check_boundaries.R +++ b/R/check_boundaries.R @@ -14,10 +14,14 @@ check_boundaries <- function(dates, boundary = NULL, what = "first") { MINMAX <- if (what == "first") min else max boundary <- MINMAX(dates, na.rm = TRUE) } + if (is.character(boundary)) { + msg <- '%s_date is a character. Did you forget to convert to Date?' + stop(sprintf(msg, what), call. = FALSE) + } res <- try(check_dates(boundary), silent = TRUE) if (inherits(res, "try-error")) { msg <- paste("%s_date could not be converted to Date. Accepted formats are:", - "\n Date, POSIXct, integer, numeric, character.") + "\n Date, POSIXct, integer, numeric.") stop(sprintf(msg, what), call. = FALSE) } res diff --git a/R/check_dates.R b/R/check_dates.R index 36ab5b5..58eb44c 100644 --- a/R/check_dates.R +++ b/R/check_dates.R @@ -2,7 +2,7 @@ #' #' This function checks that usable dates are provided, and set non-finite #' values to NA. It also makes a few trivial conversions on the fly. -#' +#' #' @param x a vector that represents dates. Can be in almost any format #' @param error_on_NA a logical specifing whether or not an error should be #' thrown if NAs are present in the dates. Defaults to FALSE. @@ -14,10 +14,6 @@ check_dates <- function(x, error_on_NA = FALSE, ...) { stop("dates is NULL", call. = FALSE) } - if (is.character(x)) { - x <- as.Date(x, ...) - } - not_finite <- !is.finite(x) if (sum(not_finite) > 0) { x[not_finite] <- NA @@ -59,7 +55,7 @@ check_dates <- function(x, error_on_NA = FALSE, ...) { } - formats <- c("Date", "POSIXct", "integer", "numeric", "character") + formats <- c("Date", "POSIXct", "integer", "numeric") msg <- paste0( "Input could not be converted to date. Accepted formats are:\n", paste(formats, collapse = ", ")) diff --git a/R/incidence.R b/R/incidence.R index e19dbda..d4c20e7 100644 --- a/R/incidence.R +++ b/R/incidence.R @@ -155,7 +155,12 @@ incidence <- function(dates, interval = 1L, ...) { #' @export #' @rdname incidence incidence.default <- function(dates, interval = 1L, ...) { + if (is.character(dates)) { + stop('Input is a character. Did you forget to convert to Date?') + } check_dates(dates) + msg <- "Unknown date input; accepted formats are Date, POSIXct, integer, numeric." + stop(msg) } #' @export diff --git a/tests/testthat/test-incidence.R b/tests/testthat/test-incidence.R index 917c9b0..58f07c7 100644 --- a/tests/testthat/test-incidence.R +++ b/tests/testthat/test-incidence.R @@ -1,13 +1,13 @@ context("Incidence main function") -# setting up the data -------------------------------------------------- +# setting up the data -------------------------------------------------- the_seed <- eval(parse(text = as.character(Sys.Date()))) -# Integer incidence -------------------------------------------------- +# Integer incidence -------------------------------------------------- set.seed(the_seed) dat <- as.integer(sample(-3:10, 50, replace = TRUE)) -# Date incidence -------------------------------------------------- +# Date incidence -------------------------------------------------- # note: the choice of dates here makes sure first date is 28 Dec 2015, which # starts an iso week, so that counts will be comparable with/without iso set.seed(the_seed) @@ -15,7 +15,7 @@ dat <- as.integer(c(-3, sample(-3:100, 50, replace = TRUE))) dat_dates <- as.Date("2015-12-31") + dat test_that("construction - default, integer input", { - + ## USING DAILY INCIDENCE x <- incidence(dat) @@ -57,7 +57,7 @@ test_that("construction - default, integer input", { }) test_that("construction - ISO week", { - + ## USING WEEKLY INCIDENCE inc.week <- incidence(dat_dates, interval = 7, standard = FALSE) @@ -80,7 +80,7 @@ test_that("construction - ISO week", { }) test_that("construction - numeric input", { - + ## USING DAILY INCIDENCE set.seed(1) @@ -102,21 +102,21 @@ test_that("construction - numeric input", { }) test_that("construction - Date input", { - + x <- incidence(dat) x.dates <- incidence(dat_dates) expect_message(x.i.trim <- incidence(dat, first_date = 0), "[0-9]+ observations outside of \\[0, [0-9]+\\] were removed." - ) - expect_message(x.d.trim <- incidence(dat_dates, first_date = "2016-01-01"), + ) + expect_message(x.d.trim <- incidence(dat_dates, first_date = as.Date("2016-01-01")), "[0-9]+ observations outside of \\[2016-01-01, [-0-9]{10}\\] were removed." - ) + ) x.7 <- incidence(dat_dates, 7L, standard = FALSE) x.7.iso <- incidence(dat_dates, "week") x.7.week <- incidence(dat_dates, "week", standard = FALSE) expect_warning(x.7.week2 <- incidence(dat_dates, "week", iso_week = FALSE), - "`iso_week` has been deprecated") + "`iso_week` has been deprecated") # iso_week can reset standard, but is given a warning expect_identical(x.7.week2, x.7.week) @@ -159,12 +159,12 @@ test_that("construction - Date input", { dat.yr <- c(dat_dates, sample(dat_dates + 366, replace = TRUE), sample(dat_dates + 366 + 365, replace = TRUE) - ) + ) x.yr.iso <- incidence(dat.yr, "year") x.yr <- incidence(dat.yr, "year", standard = FALSE) - expect_warning(x.yr.no <- incidence(dat.yr, "year", first_date = "2016-02-29"), + expect_warning(x.yr.no <- incidence(dat.yr, "year", first_date = as.Date("2016-02-29")), "The first_date \\(2016-02-29\\) represents a day that does not occur in all years." - ) + ) expect_equal(get_dates(x.yr.iso), as.Date(c("2015-01-01", "2016-01-01", "2017-01-01", "2018-01-01"))) expect_equal(get_dates(x.yr), as.Date(c("2015-12-28", "2016-12-28", "2017-12-28"))) expect_equal(sum(x.yr$counts), sum(x.yr.iso$counts)) @@ -190,7 +190,7 @@ test_that("construction - Date input", { }) test_that("construction - POSIXct input", { - + ## USING DAILY INCIDENCE dat.pos <- as.POSIXct(dat_dates) @@ -204,7 +204,7 @@ test_that("construction - POSIXct input", { }) test_that("corner cases", { - + expect_error(incidence(integer(0)), "At least one \\(non-NA\\) date must be provided") @@ -225,23 +225,23 @@ test_that("corner cases", { "The interval 'grind' is not valid. Please supply an integer.") expect_error(incidence(as.Date(Sys.Date()), last_date = "core"), - "last_date could not be converted to Date") + "last_date is a character. Did you forget to convert to Date?") expect_error(incidence(1, "week"), "The interval 'week' can only be used for Dates") expect_error(incidence(as.Date(Sys.Date()), standard = "TRUE"), "The argument `standard` must be either `TRUE` or `FALSE`") - + expect_error(incidence(sample(10), intrval = 2), - "intrval : interval") + "intrval : interval") expect_error(incidence(1, were = "wolf"), "were") expect_warning(incidence(c(dat_dates, as.Date("1900-01-01"))), "greater than 18262 days \\[1900-01-01 to" - ) + ) }) test_that("incidence constructor can handle missing data", { @@ -253,13 +253,13 @@ test_that("incidence constructor can handle missing data", { test_that("incidence constructor can handle data out of range with groups", { set.seed(the_seed) g <- sample(letters[1:2], length(dat), replace = TRUE) - expect_message(incidence(dat, first_date = 0, groups = g), + expect_message(incidence(dat, first_date = 0, groups = g), "[0-9]+ observations outside of \\[0, [0-9]+\\] were removed." - ) + ) }) test_that("Expected values, no group", { - + expect_true(all(incidence(1:10)$counts == 1L)) expect_true(all(incidence(sample(1:10))$counts == 1L)) @@ -285,7 +285,7 @@ test_that("Expected values, no group", { }) test_that("Expected values, with groups", { - + dat <- list( as.integer(c(3,2,-1,1,1)), @@ -313,12 +313,12 @@ test_that("user-defined group levels are preserved", { g <- factor(g, levels = LETTERS[5:1]) i <- incidence(rpois(100, 10), groups = g) expect_identical(group_names(i), levels(g)) - i.df <- as.data.frame(i, long = TRUE) + i.df <- as.data.frame(i, long = TRUE) expect_identical(levels(i.df$groups), levels(g)) }) test_that("Printing returns the object", { - + x <- incidence(as.Date("2001-01-01")) y <- incidence(1:2, groups = factor(1:2)) @@ -330,3 +330,21 @@ test_that("Printing returns the object", { expect_equal_to_reference(capture.output(print(z)), file = "rds/print3.rds") }) + +test_that("incidence returns error if input not in accepted format", { + + msg <- 'Input is a character. Did you forget to convert to Date?' + expect_error(incidence('daldkadl'), msg) + expect_error(incidence('2001-01-01'), msg) + + msg <- paste0("Input could not be converted to date. Accepted formats are:\n", + "Date, POSIXct, integer, numeric") + expect_error(incidence(factor("2001-01-01")), msg) + + msg <- 'first_date is a character. Did you forget to convert to Date?' + expect_error(incidence(as.Date('2016-02-29'), "year", first_date = '2016-02-29'), msg) + + msg <- 'last_date is a character. Did you forget to convert to Date?' + expect_error(incidence(as.Date('2016-02-29'), "year", last_date = '2016-02-29'), msg) + +}) diff --git a/tests/testthat/test-non-exported.R b/tests/testthat/test-non-exported.R index 4dbe5c1..d3ccf02 100644 --- a/tests/testthat/test-non-exported.R +++ b/tests/testthat/test-non-exported.R @@ -9,7 +9,7 @@ test_that("check_dates works", { expect_warning(check_dates(1.1), msg) msg <- paste0("Input could not be converted to date. Accepted formats are:\n", - "Date, POSIXct, integer, numeric, character") + "Date, POSIXct, integer, numeric") expect_error(check_dates(factor("2001-01-01")), msg) x <- list(1L, @@ -20,8 +20,6 @@ test_that("check_dates works", { for (e in x) { expect_equal(e, check_dates(e)) } - - expect_equal(check_dates("2001-01-01"), as.Date("2001-01-01")) }) test_that("check_interval", {