diff --git a/DESCRIPTION b/DESCRIPTION index 13f82df..6885a54 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 Imports: + checkmate, cli, copula, dplyr, @@ -51,9 +52,10 @@ Suggests: tidyr, roxygen2, usethis, - testthat, + testthat (>= 3.0.0), rcmdcheck, - httptest + httptest, + withr VignetteBuilder: knitr URL: https://cdcgov.github.io/forecasttools, https://github.com/CDCgov/forecasttools @@ -63,3 +65,4 @@ LazyData: true Remotes: hubverse-org/hubData BugReports: https://github.com/CDCgov/forecasttools/issues +Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 6b37075..1d612ed 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(bottom_up_aggregation) +export(categorize_vector) export(copula2tbl) export(count_trajectories) export(create_table_for_scoring) @@ -13,6 +14,7 @@ export(gather_hub_forecast_data) export(gather_location_data) export(gather_target_data) export(get_hubverse_table) +export(get_prism_cutpoints) export(inferencedata_to_tidy_draws) export(location_lookup) export(nhsn_soda_query) diff --git a/R/categorize_prism.R b/R/categorize_prism.R new file mode 100644 index 0000000..32bbbce --- /dev/null +++ b/R/categorize_prism.R @@ -0,0 +1,75 @@ +#' Get PRISM activity level cutpoints for given +#' diseases and locations +#' +#' @param diseases disease(s) for which to return the cutpoints. +#' One of `"ARI"`, `"COVID-19"`, `"Influenza"`, or `"RSV"`, or +#' an array of those values. +#' @param locations location(s) for which to return the cutpoints. +#' A location two-letter abbreviation as in the `short_name` +#' column of [forecasttools::us_location_table], or an array +#' of those abbreviations. +#' @return The cutpoints, as an ordered list of vectors. +#' +#' @examples +#' get_prism_cutpoints("WA", "Influenza") +#' +#' get_prism_cutpoints(c("US", "WA"), "COVID-19") +#' +#' get_prism_cutpoints(c("US", "WA"), c("ARI", "RSV")) +#' @export +get_prism_cutpoints <- function(locations, diseases) { + checkmate::assert_names( + diseases, + subset.of = + dimnames(forecasttools::prism_thresholds)$disease, + what = "disease" + ) + checkmate::assert_names( + locations, + subset.of = + dimnames(forecasttools::prism_thresholds)$location, + what = "location" + ) + + return(purrr::map2(locations, diseases, \(x, y) { + forecasttools::prism_thresholds[x, y, ] + })) +} + + + +#' Categorize a vector of values into PRISM +#' activity level bins. +#' +#' @param values values to categorize +#' @param locations vector of locations of length equal to +#' `values` or a single location for all `values`. +#' @param diseases vector of diseases of length equal to +#' `values` or a single disease for all `values`. +#' @param prism_bin_names Bin names for the PRISM bins. +#' in order from lowest to highest. Must be a vector of +#' length 5. `list(prism_bin_names)` will be passed as the +#' `label_sets` argument to [categorize_vector()]. +#' Defaults to the standard PRISM bin names in title case: +#' `c("Very Low", "Low", "Moderate", "High", "Very High")`. +#' @return A factor vector equal in length to `values` of +#' the categories, as the output of [categorize_vector()]. +#' @export +categorize_prism <- function(values, + locations, + diseases, + prism_bin_names = c( + "Very Low", + "Low", + "Moderate", + "High", + "Very High" + )) { + cutpoints <- get_prism_cutpoints(locations, diseases) + + return(categorize_vector( + values, + break_sets = cutpoints, + label_sets = list(prism_bin_names) + )) +} diff --git a/R/categorize_vector.R b/R/categorize_vector.R new file mode 100644 index 0000000..c1fa340 --- /dev/null +++ b/R/categorize_vector.R @@ -0,0 +1,58 @@ +#' Catgeorize entries in a numeric vector according +#' to a list of breakpoints and labels. +#' +#' Vectorized version of [base::cut()] that +#' allows each entry in the vector to use different +#' break points and labels. +#' +#' @param values Vector of values to categorize. +#' @param break_sets sets of category breakpoints, +#' with one set for each entry of `values`, as a list of +#' numeric vectors. Alternatively, a single set of category +#' breakpoints to use for all values, as a one-entry list +#' containing a single numeric vector. +#' @param label_sets sets of labels to associate, +#' with the corresponding breakpoints in `break_sets`, +#' with one set for each entry of `values`, as a list of +#' character vectors. Alternatively, a single set of labels +#' to use for all values, as a one-entry list containing a +#' single character vector. +#' @param include.lowest Passed to [base::cut()]. Default `TRUE`. +#' @param order Passed to [base::cut()]. Default `TRUE`. +#' @param right Passed to [base::cut()]. Default `TRUE`. +#' @param ... Additional keyword arguments passed to +#' [base::cut()]. +#' @return A categorized vector, as vector of ordered +#' factors. +#' @export +categorize_vector <- function(values, + break_sets, + label_sets, + include.lowest = TRUE, # nolint + order = TRUE, + right = TRUE, + ...) { + n_vals <- length(values) + + checkmate::assert_list(break_sets, + types = "numeric" + ) + checkmate::assert_list(label_sets, + types = "character" + ) + checkmate::qassert(break_sets, c("l1", glue::glue("l{n_vals}"))) + checkmate::qassert(label_sets, c("l1", glue::glue("l{n_vals}"))) + + return(purrr::pmap_vec( + list( + x = values, + breaks = break_sets, + labels = label_sets, + include.lowest = include.lowest, + order = order, + right = right, + ... + ), + cut + )) +} diff --git a/R/data.R b/R/data.R index c371a47..b57dae3 100644 --- a/R/data.R +++ b/R/data.R @@ -41,3 +41,25 @@ #' A data frame with 40 rows and 20 columns: #' @source "ex_inferencedata_dataframe" + +#' PRISM respiratory virus activity level thresholds +#' +#' A multi-dimensional array with PRISM +#' respiratory virus activity level thresholds. +#' Dimensions, in order, are `location`, +#' `disease`, and `breaks`. +#' +#' Values of `disease` are `Influenza`, `COVID-19`, +#' `RSV`, and `ARI` (acute respiratory infections). +#' +#' Values of `breaks` are `prop_lower_bound`, +#' `prop_low`, `prop_moderate`, `prop_high`, +#' `prop_very_high`, and `prop_upper_bound`. +#' +#' Values of `location` are US jurisdictions +#' and the United States as a whole, using +#' USPS two-letter codes (the values of `short_name`) +#' in [us_location_table]. +#' +#' @source +"prism_thresholds" diff --git a/data-raw/prism_thresholds.R b/data-raw/prism_thresholds.R new file mode 100644 index 0000000..fa657d9 --- /dev/null +++ b/data-raw/prism_thresholds.R @@ -0,0 +1,53 @@ +#' When there is an appropriate online endpoint for these +#' thresholds, this this script will be updated to point there. +#' For now, it reads from a bundled .tsv in inst/extdata + +thresholds <- readr::read_tsv( + fs::path( + "inst", + "extdata", + "prism_thresholds", + ext = "tsv" + ), + show_col_types = FALSE +) + +## Transform thresholds from percentage to proprotion +prop_thresholds <- thresholds |> + dplyr::transmute( + disease, + location = state_abb, + prop_lower_bound = 0, + prop_low = perc_level_low / 100, + prop_moderate = perc_level_moderate / 100, + prop_high = perc_level_high / 100, + prop_very_high = perc_level_very_high / 100, + prop_upper_bound = 1 + ) + +#' Transform thresholds from flat table to +#' multi-dimensional array, via a nested list. +#' +#' Method for conversion to multi-dim array: +#' 1. Transform the long-form tabular data to a +#' nested named list (via nest() and deframe()) +#' 2. Transform the nested named list to a multi-dimensional +#' array with dimension names (via simplify2array() and unlist()) +#' 3. Order and name the dimensions of that array. + +thresholds_nested_list <- prop_thresholds |> + tidyr::nest(breaks = dplyr::starts_with("prop_")) |> + tidyr::nest(loc_breaks = c(location, breaks)) |> + tibble::deframe() |> + purrr::map(deframe) # yields a nested list of tibbles + +prism_thresholds <- thresholds_nested_list |> + simplify2array() |> # yields a 2D array of length-1 tibbles + apply(1:2, unlist) |> # yields a 3D array + aperm(c(2, 3, 1)) # orders 3D array dimensions as desired + +names(dimnames(prism_thresholds)) <- c("location", "disease", "breaks") + +usethis::use_data(prism_thresholds, + overwrite = TRUE +) diff --git a/data/prism_thresholds.rda b/data/prism_thresholds.rda new file mode 100644 index 0000000..e3db6f5 Binary files /dev/null and b/data/prism_thresholds.rda differ diff --git a/inst/extdata/prism_thresholds.tsv b/inst/extdata/prism_thresholds.tsv new file mode 100644 index 0000000..6657c52 --- /dev/null +++ b/inst/extdata/prism_thresholds.tsv @@ -0,0 +1,213 @@ +disease state_abb state_code state_name perc_level_low perc_level_moderate perc_level_high perc_level_very_high +Influenza US US United States 0.339653993544693 1.92323278764894 3.5068115817532 5.09039037585745 +COVID-19 US US United States 0.601509069090881 1.70419326288427 2.80687745667766 3.90956165047104 +RSV US US United States 0.0716383969527895 0.35553747427476 0.63943655159673 0.9233356289187 +ARI US US United States 10.4648426922243 13.8181574261492 17.1714721600741 20.524786893999 +Influenza AK 02 Alaska 0.980271843148604 3.27706539993575 5.57385895672289 7.87065251351003 +COVID-19 AK 02 Alaska 0.910853966945788 2.45917900621476 4.00750404548373 5.5558290847527 +RSV AK 02 Alaska 0.108836758958285 0.776941155646192 1.4450455523341 2.11314994902201 +ARI AK 02 Alaska 12.0325208608167 15.7896879739664 19.5468550871162 23.3040222002659 +Influenza AL 01 Alabama 0.405774191534674 3.37678721933339 6.3478002471321 9.31881327493081 +COVID-19 AL 01 Alabama 0.721251202776328 2.46576569857216 4.210280194368 5.9547946901638404 +RSV AL 01 Alabama 0.0549766487333309 0.48933369382138 0.923690738909429 1.35804778399748 +ARI AL 01 Alabama 11.0707571553582 15.9540807957783 20.8374044361984 25.7207280766185 +Influenza AR 05 Arkansas 0.407918012977306 2.61327017610197 4.81862233922664 7.02397450235131 +COVID-19 AR 05 Arkansas 0.45144584816555 1.40132733439487 2.35120882062419 3.30109030685351 +RSV AR 05 Arkansas 0.0751933833915953 0.633327398862675 1.19146141433375 1.74959542980483 +ARI AR 05 Arkansas 8.61531194725447 11.9832893748361 15.3512668024178 18.7192442299994 +Influenza AZ 04 Arizona 0.416088204662123 2.14117700133523 3.86626579800833 5.59135459468143 +COVID-19 AZ 04 Arizona 0.84685830428958 2.16651677001998 3.48617523575039 4.80583370148079 +RSV AZ 04 Arizona 0.0409779572441818 0.383211885015569 0.725445812786956 1.06767974055834 +ARI AZ 04 Arizona 11.4533628016604 15.3024693224721 19.1515758432838 23.0006823640955 +Influenza CA 06 California 0.226327183316214 1.69160209129706 3.15687699927791 4.62215190725875 +COVID-19 CA 06 California 0.558108660515558 1.73564597703968 2.9131832935638 4.09072061008792 +RSV CA 06 California 0.0344265530499923 0.362341041789881 0.690255530529769 1.01817001926966 +ARI CA 06 California 11.0228425209899 14.9338128652802 18.8447832095704 22.7557535538606 +Influenza CO 08 Colorado 0.185509100201764 1.61218388046304 3.03885866072432 4.4655334409856 +COVID-19 CO 08 Colorado 0.697231245966627 1.74398516098764 2.79073907600865 3.83749299102966 +RSV CO 08 Colorado 0.0431537609053263 0.668897107306689 1.29464045370805 1.92038380010942 +ARI CO 08 Colorado 12.4096908218521 16.6268464364414 20.8440020510308 25.0611576656201 +Influenza CT 09 Connecticut 0.20507343794368 2.16808276690581 4.13109209586794 6.09410142483008 +COVID-19 CT 09 Connecticut 0.56429242884738 1.78858048168432 3.01286853452126 4.23715658735821 +RSV CT 09 Connecticut 0.027742021145623 0.254310764673641 0.48087950820166 0.707448251729679 +ARI CT 09 Connecticut 12.183243737408 16.562508840949 20.94177394449 25.3210390480311 +Influenza DC 11 District of Columbia 0.159476150800101 1.86715984989864 3.57484354899718 5.28252724809572 +COVID-19 DC 11 District of Columbia 0.526433374147159 1.7120674978765 2.89770162160584 4.08333574533518 +RSV DC 11 District of Columbia 0.098625078652519 0.601922910551889 1.10522074245126 1.60851857435063 +ARI DC 11 District of Columbia 11.2120287686789 15.15450620356 19.0969836384412 23.0394610733224 +Influenza DE 10 Delaware 0.109073949476632 2.17023351649464 4.23139308351265 6.29255265053066 +COVID-19 DE 10 Delaware 0.500070197051349 1.50823954874698 2.51640890044261 3.52457825213824 +RSV DE 10 Delaware 0.0929108216428634 0.723376592241813 1.35384236284076 1.98430813343971 +ARI DE 10 Delaware 9.86691707533298 13.6141171814386 17.3613172875442 21.1085173936498 +Influenza FL 12 Florida 0.836011839170292 2.08527355145154 3.3345352637328 4.58379697601405 +COVID-19 FL 12 Florida 1.1049827880024 2.55967138225653 4.01435997651066 5.46904857076479 +RSV FL 12 Florida 0.090842972515709 0.276793454076287 0.462743935636866 0.648694417197444 +ARI FL 12 Florida 13.682909199968 16.0678095777651 18.4527099555622 20.8376103333594 +Influenza GA 13 Georgia 0.332114011595581 2.16634716346359 4.0005803153316 5.83481346719961 +COVID-19 GA 13 Georgia 0.448875073863044 1.20225812238962 1.95564117091619 2.70902421944277 +RSV GA 13 Georgia 0.0464097808652688 0.228273481893565 0.410137182921861 0.592000883950157 +ARI GA 13 Georgia 8.78163783501836 12.0715811692079 15.3615245033975 18.651467837587 +Influenza GU 66 Guam 1.36987145456657 2.3958246435565 3.42177783254644 4.44773102153638 +COVID-19 GU 66 Guam 1.70022224239727 4.32394716960283 6.94767209680839 9.57139702401395 +RSV GU 66 Guam 0.0434653845684039 0.264617599911222 0.48576981525404 0.706922030596857 +ARI GU 66 Guam 14.7132748188422 17.539021154782102 20.3647674907219 23.1905138266618 +Influenza HI 15 Hawaii 1.20634438328273 2.05152708478552 2.89670978628831 3.7418924877911 +COVID-19 HI 15 Hawaii 1.3848702406628 2.95422375691452 4.52357727316624 6.09293078941796 +RSV HI 15 Hawaii 0.0691583103604069 0.454787996872479 0.840417683384552 1.22604736989662 +ARI HI 15 Hawaii 15.8733762946673 17.6805026947188 19.4876290947704 21.2947554948219 +Influenza IA 19 Iowa 0.310883509962675 2.24681588824838 4.18274826653409 6.1186806448198 +COVID-19 IA 19 Iowa 0.705216900119656 1.94461409079116 3.18401128146266 4.42340847213416 +RSV IA 19 Iowa 0.114465550559083 0.617363744292784 1.12026193802648 1.62316013176018 +ARI IA 19 Iowa 11.3216203348727 15.2047392453378 19.0878581558028 22.9709770662679 +Influenza ID 16 Idaho 0.173518705997438 2.21977276605722 4.266026826117 6.31228088617678 +COVID-19 ID 16 Idaho 0.517549129078217 1.88616706393311 3.254784998788 4.62340293364289 +RSV ID 16 Idaho 0.0452887440033941 0.789012847272541 1.53273695054169 2.27646105381083 +ARI ID 16 Idaho 9.26000256906029 13.2441844464682 17.2283663238762 21.2125482012841 +Influenza IL 17 Illinois 0.219024609883691 2.67487242237358 5.13072023486347 7.58656804735336 +COVID-19 IL 17 Illinois 0.629502061203994 1.64890993897819 2.66831781675239 3.68772569452659 +RSV IL 17 Illinois 0.0807410991366061 0.56458990601095 1.04843871288529 1.53228751975964 +ARI IL 17 Illinois 9.85408533909644 14.0655294261757 18.276973513255 22.4884176003343 +Influenza IN 18 Indiana 0.190640113402513 2.95231071385513 5.71398131430775 8.47565191476036 +COVID-19 IN 18 Indiana 0.439965735656953 1.72767209988354 3.01537846411012 4.3030848283367 +RSV IN 18 Indiana 0.0737012308096612 0.468568107412954 0.863434984016248 1.25830186061954 +ARI IN 18 Indiana 9.89493187733481 15.2034842359254 20.512036594516 25.8205889531065 +Influenza KS 20 Kansas 0.160408294434422 2.10476923765141 4.0491301808684 5.99349112408539 +COVID-19 KS 20 Kansas 0.355420758935411 1.55048130444068 2.74554184994595 3.94060239545123 +RSV KS 20 Kansas 0.0500403520982661 0.435440231919359 0.820840111740451 1.20623999156154 +ARI KS 20 Kansas 9.38425870686798 14.0270751464575 18.6698915860469 23.3127080256364 +Influenza KY 21 Kentucky 0.269116155766922 3.49935688483254 6.72959761389816 9.95983834296378 +COVID-19 KY 21 Kentucky 0.909576325747603 2.17315690355804 3.43673748136848 4.70031805917892 +RSV KY 21 Kentucky 0.068022259600647 0.560698318234278 1.05337437686791 1.54605043550154 +ARI KY 21 Kentucky 12.0384252216093 16.629820375167 21.2212155287246 25.8126106822823 +Influenza LA 22 Louisiana 0.525503676488859 2.92567479862962 5.32584592077039 7.72601704291115 +COVID-19 LA 22 Louisiana 0.654250121285289 2.6066092423007 4.55896836331611 6.51132748433152 +RSV LA 22 Louisiana 0.0510041480553787 0.446905958630119 0.842807769204859 1.2387095797796 +ARI LA 22 Louisiana 9.57073099615822 13.4692855010584 17.3678400059585 21.2663945108586 +Influenza MA 25 Massachusetts 0.214661386007536 2.07116867971577 3.927675973424 5.78418326713223 +COVID-19 MA 25 Massachusetts 0.651185991880751 1.82948468746477 3.0077833830488 4.18608207863282 +RSV MA 25 Massachusetts 0.0670209420307515 0.665273786348602 1.26352663066645 1.8617794749843 +ARI MA 25 Massachusetts 10.1755348538362 13.5477614985896 16.919988143343 20.2922147880964 +Influenza MD 24 Maryland 0.166566858410208 2.04985748878942 3.93314811916863 5.81643874954784 +COVID-19 MD 24 Maryland 0.546305741007218 1.60834266260778 2.67037958420835 3.73241650580891 +RSV MD 24 Maryland 0.0591043672668986 0.613415081927477 1.16772579658805 1.72203651124863 +ARI MD 24 Maryland 8.50635457808995 11.8662937603706 15.2262329426513 18.5861721249319 +Influenza ME 23 Maine 0.156056147912831 2.72861981621678 5.30118348452072 7.87374715282467 +COVID-19 ME 23 Maine 0.69510471408335 1.76495953583068 2.83481435757801 3.90466917932534 +RSV ME 23 Maine 0.0345178372991826 0.51783502909311 1.00115222088704 1.48446941268097 +ARI ME 23 Maine 10.602944735988 14.718016839629 18.8330889432699 22.9481610469109 +Influenza MI 26 Michigan 0.198899383681348 2.44068490779078 4.68247043190021 6.92425595600964 +COVID-19 MI 26 Michigan 0.564815218447782 1.62113070346039 2.677446188473 3.7337616734856 +RSV MI 26 Michigan 0.0391618200145449 0.615730594729324 1.1922993694441 1.76886814415888 +ARI MI 26 Michigan 9.32339611319278 13.414057815787 17.5047195183812 21.5953812209754 +Influenza MN 27 Minnesota 0.227893615527177 3.04451571726336 5.86113781899954 8.67775992073573 +COVID-19 MN 27 Minnesota 0.586214555636831 1.57378125785207 2.56134796006731 3.54891466228255 +RSV MN 27 Minnesota 0.0905385802732976 0.666491269096988 1.24244395792068 1.81839664674437 +ARI MN 27 Minnesota 10.4202177684303 14.9752756858733 19.5303336033163 24.0853915207593 +Influenza MO 29 Missouri 0.0020277041100999 0.0508479335129959 0.099668162915892 0.148488392318788 +COVID-19 MO 29 Missouri 0.00793462807198301 0.0312817613746453 0.0546288946773075 0.0779760279799697 +RSV MO 29 Missouri 7.71391329087799e-4 0.0178666474123378 0.0349619034955877 0.0520571595788377 +ARI MO 29 Missouri 0.146132016728597 0.244833011704006 0.343534006679416 0.442235001654825 +Influenza MS 28 Mississippi 0.458529612426411 3.04871049911109 5.63889138579577 8.22907227248045 +COVID-19 MS 28 Mississippi 0.6964788841527 2.14702473660205 3.59757058905141 5.04811644150076 +RSV MS 28 Mississippi 0.0532949022520541 0.350134209266904 0.646973516281753 0.943812823296603 +ARI MS 28 Mississippi 11.0886183437294 15.3454286875145 19.6022390312996 23.8590493750847 +Influenza MT 30 Montana 0.10444777891344 2.56765207350132 5.0308563680892 7.49406066267708 +COVID-19 MT 30 Montana 0.818830191937599 2.03213980315244 3.24544941436727 4.45875902558211 +RSV MT 30 Montana 0.0544391758221658 0.569642941188658 1.08484670655515 1.60005047192164 +ARI MT 30 Montana 10.2923694704229 14.0093412053813 17.7263129403396 21.443284675298 +Influenza NC 37 North Carolina 0.53944866721049 3.91000410366721 7.28055954012393 10.6511149765807 +COVID-19 NC 37 North Carolina 0.957637497938248 2.34620243383482 3.73476736973139 5.12333230562796 +RSV NC 37 North Carolina 0.052673744506241 0.41527639169893 0.77787903889162 1.14048168608431 +ARI NC 37 North Carolina 12.5462641511737 17.6298354412814 22.713406731389 27.7969780214967 +Influenza ND 38 North Dakota 0.112930343017489 2.62452986279678 5.13612938257607 7.64772890235537 +COVID-19 ND 38 North Dakota 0.699611618838267 1.867781287173 3.03595095550773 4.20412062384246 +RSV ND 38 North Dakota 0.0646653141036112 0.819442963648635 1.57422061319366 2.32899826273868 +ARI ND 38 North Dakota 9.4044814603834 13.8762153012153 18.3479491420471 22.819682982879 +Influenza NE 31 Nebraska 0.142123247520877 2.04338657077044 3.94464989402 5.84591321726956 +COVID-19 NE 31 Nebraska 0.308022398798573 1.3132531948107 2.31848399082282 3.32371478683494 +RSV NE 31 Nebraska 0.0601351478758112 0.499493821357885 0.938852494839959 1.37821116832203 +ARI NE 31 Nebraska 7.89373963676057 11.3460008242845 14.7982620118084 18.2505231993323 +Influenza NH 33 New Hampshire 0.187345932755749 2.07974215749176 3.97213838222778 5.86453460696379 +COVID-19 NH 33 New Hampshire 0.555696355548634 1.47316381552908 2.39063127550954 3.30809873548999 +RSV NH 33 New Hampshire 0.0452020647941643 0.439866751578225 0.834531438362285 1.22919612514635 +ARI NH 33 New Hampshire 8.14785825720061 11.5925514918363 15.037244726472 18.4819379611078 +Influenza NJ 34 New Jersey 0.321586577636629 2.11829624218902 3.91500590674142 5.71171557129381 +COVID-19 NJ 34 New Jersey 0.429078579510625 1.40677814171534 2.38447770392005 3.36217726612477 +RSV NJ 34 New Jersey 0.0501750296729801 0.326255204094114 0.602335378515248 0.878415552936382 +ARI NJ 34 New Jersey 9.36969876260443 12.8014294345102 16.2331601064161 19.6648907783219 +Influenza NM 35 New Mexico 0.378159575817063 3.45109157421842 6.52402357261978 9.59695557102114 +COVID-19 NM 35 New Mexico 1.20519051983787 2.8005977044009 4.39600488896392 5.99141207352695 +RSV NM 35 New Mexico 0.0948330729151471 0.948925940795856 1.80301880867657 2.65711167655727 +ARI NM 35 New Mexico 14.2370777902424 19.5583117690265 24.8795457478107 30.2007797265949 +Influenza NV 32 Nevada 0.331848165602097 1.74076298114575 3.14967779668941 4.55859261223307 +COVID-19 NV 32 Nevada 0.554330593283608 1.9522803599949 3.35023012670618 4.74817989341747 +RSV NV 32 Nevada 0.0386034631585323 0.455543135704951 0.87248280825137 1.28942248079779 +ARI NV 32 Nevada 12.6346692559538 16.9407175282059 21.2467658004581 25.5528140727103 +Influenza NY 36 New York 0.239330551323172 1.4905015902329 2.74167262914263 3.99284366805236 +COVID-19 NY 36 New York 0.450918945714812 1.26656212007167 2.08220529442853 2.89784846878539 +RSV NY 36 New York 0.0353350085642889 0.297119813124248 0.558904617684206 0.820689422244165 +ARI NY 36 New York 8.88556129222044 12.1839014529949 15.4822416137694 18.7805817745438 +Influenza OH 39 Ohio 0.133365036676197 2.07837795667362 4.0233908766710504 5.96840379666847 +COVID-19 OH 39 Ohio 0.428391015969636 1.5444863442969 2.66058167262416 3.77667700095142 +RSV OH 39 Ohio 0.031697956459707 0.347938585023004 0.6641792135863 0.980419842149597 +ARI OH 39 Ohio 8.94046717769431 12.977330814341 17.0141944509877 21.0510580876345 +Influenza OK 40 Oklahoma 0.194519088918796 2.26880517554912 4.34309126217945 6.41737734880977 +COVID-19 OK 40 Oklahoma 0.451722004139253 1.60750109481465 2.76328018549005 3.91905927616545 +RSV OK 40 Oklahoma 0.0665630227990645 0.505625255974784 0.944687489150504 1.38374972232622 +ARI OK 40 Oklahoma 9.64048917383551 13.9926778536221 18.3448665334086 22.6970552131952 +Influenza OR 41 Oregon 0.184733555894893 2.63694594852427 5.08915834115365 7.54137073378303 +COVID-19 OR 41 Oregon 0.721723680396138 1.90299855149265 3.08427342258916 4.26554829368567 +RSV OR 41 Oregon 0.0369486523403626 0.518469000442146 0.999989348543929 1.48150969664571 +ARI OR 41 Oregon 10.2315408639826 14.4130813884515 18.5946219129204 22.7761624373892 +Influenza PA 42 Pennsylvania 0.109848618133185 1.66112000752232 3.21239139691146 4.7636627863006 +COVID-19 PA 42 Pennsylvania 0.400993530421904 1.22890460427557 2.05681567812923 2.88472675198289 +RSV PA 42 Pennsylvania 0.0332074441883945 0.402715867407431 0.772224290626467 1.1417327138455 +ARI PA 42 Pennsylvania 7.64881877824774 10.8527592171087 14.0566996559697 17.2606400948307 +Influenza RI 44 Rhode Island 0.160757569379592 1.63884952386386 3.11694147834813 4.5950334328324 +COVID-19 RI 44 Rhode Island 0.42262534696292 1.33998691303549 2.25734847910806 3.17471004518063 +RSV RI 44 Rhode Island 0.0352095675786539 0.351174190651514 0.667138813724374 0.983103436797233 +ARI RI 44 Rhode Island 8.22210598236906 11.3953545332548 14.5686030841405 17.7418516350263 +Influenza SC 45 South Carolina 0.366504838565552 3.3035836912866 6.24066254400765 9.1777413967287 +COVID-19 SC 45 South Carolina 0.779678647853532 2.21360504445429 3.64753144105504 5.0814578376558 +RSV SC 45 South Carolina 0.0437758036567582 0.349666927241627 0.655558050826495 0.961449174411364 +ARI SC 45 South Carolina 11.2393482868308 16.1646313545215 21.0899144222122 26.0151974899029 +Influenza SD 46 South Dakota 0.107663520150527 1.4517450637419 2.79582660733327 4.13990815092464 +COVID-19 SD 46 South Dakota 0.463047734076824 1.25261668291056 2.04218563174429 2.83175458057803 +RSV SD 46 South Dakota 0.0556838174146855 0.429426543281552 0.803169269148418 1.17691199501528 +ARI SD 46 South Dakota 7.09293991662318 9.85081799618781 12.6086960757524 15.3665741553171 +Influenza TN 47 Tennessee 0.332859566854169 2.79116438433375 5.24946920181332 7.7077740192929 +COVID-19 TN 47 Tennessee 0.738046426680436 1.92739752851377 3.1167486303471 4.30609973218043 +RSV TN 47 Tennessee 0.0743188919664283 0.477605612759586 0.880892333552745 1.2841790543459 +ARI TN 47 Tennessee 11.159036448309 15.2813460920594 19.4036557358097 23.5259653795601 +Influenza TX 48 Texas 0.520174196798285 2.54353442584249 4.5668946548867 6.59025488393091 +COVID-19 TX 48 Texas 0.565954504897554 2.0777207241502 3.58948694340284 5.10125316265549 +RSV TX 48 Texas 0.0816356369911876 0.486860055469191 0.892084473947194 1.2973088924252 +ARI TX 48 Texas 11.6516815193029 15.4725592639722 19.2934370086415 23.1143147533108 +Influenza UT 49 Utah 0.204051288549113 2.02694474679294 3.84983820503676 5.67273166328058 +COVID-19 UT 49 Utah 0.801491794024371 2.27392276043664 3.7463537268489 5.21878469326117 +RSV UT 49 Utah 0.0453125500085216 0.885549389631906 1.72578622925529 2.56602306887868 +ARI UT 49 Utah 10.8931248279202 14.916684224283 18.9402436206458 22.9638030170086 +Influenza VA 51 Virginia 0.269996393322792 2.46251085628429 4.65502531924579 6.84753978220729 +COVID-19 VA 51 Virginia 0.613311182232651 1.96038096836806 3.30745075450347 4.65452054063888 +RSV VA 51 Virginia 0.0554625861287223 0.332493376801982 0.609524167475242 0.886554958148502 +ARI VA 51 Virginia 12.1658602931845 16.7785328242972 21.3912053554099 26.0038778865227 +Influenza VT 50 Vermont 0.161478759526905 2.50512968820188 4.84878061687685 7.19243154555183 +COVID-19 VT 50 Vermont 0.575327309281069 1.50341128784659 2.43149526641212 3.35957924497764 +RSV VT 50 Vermont 0.0838052625684766 0.829157176714467 1.57450909086046 2.31986100500645 +ARI VT 50 Vermont 10.9177788316109 15.0677948691483 19.2178109066857 23.3678269442231 +Influenza WA 53 Washington 0.259309741488702 3.02574862465439 5.79218750782007 8.55862639098575 +COVID-19 WA 53 Washington 0.871115374009525 2.2004050199449 3.52969466588027 4.85898431181564 +RSV WA 53 Washington 0.0455148517416957 0.61158985349889 1.17766485525608 1.74373985701328 +ARI WA 53 Washington 12.2895232596611 17.3144751055576 22.3394269514541 27.3643787973506 +Influenza WI 55 Wisconsin 0.140496439155569 1.76157489086172 3.38265334256787 5.00373179427401 +COVID-19 WI 55 Wisconsin 0.527507482921962 1.43534356986904 2.34317965681611 3.25101574376319 +RSV WI 55 Wisconsin 0.0457745963981928 0.416852705416996 0.7879308144358 1.1590089234546 +ARI WI 55 Wisconsin 10.604721131927 14.4005766476894 18.1964321634519 21.9922876792143 +Influenza WV 54 West Virginia 0.125027003643313 2.35494682914699 4.58486665465066 6.81478648015433 +COVID-19 WV 54 West Virginia 0.57679671134205 1.98858985883762 3.4003830063332 4.81217615382877 +RSV WV 54 West Virginia 0.072610173823666 0.87339747551367 1.67418477720367 2.47497207889368 +ARI WV 54 West Virginia 11.0197618734323 15.6814338737168 20.3431058740013 25.0047778742858 +Influenza WY 56 Wyoming 0.18207320882403 2.03349714897363 3.88492108912323 5.73634502927283 +COVID-19 WY 56 Wyoming 0.791321117375274 2.25557531691173 3.7198295164482 5.18408371598466 +RSV WY 56 Wyoming 0.067830399921913 0.85301183737459 1.63819327482727 2.42337471227994 +ARI WY 56 Wyoming 9.62666199404063 13.6752626336958 17.723863273351 21.7724639130062 diff --git a/man/categorize_vector.Rd b/man/categorize_vector.Rd new file mode 100644 index 0000000..514c71b --- /dev/null +++ b/man/categorize_vector.Rd @@ -0,0 +1,51 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/categorize_vector.R +\name{categorize_vector} +\alias{categorize_vector} +\title{Catgeorize entries in a numeric vector according +to a list of breakpoints and labels.} +\usage{ +categorize_vector( + values, + break_sets, + label_sets, + include.lowest = TRUE, + order = TRUE, + right = TRUE, + ... +) +} +\arguments{ +\item{values}{Vector of values to categorize.} + +\item{break_sets}{sets of category breakpoints, +with one set for each entry of \code{values}, as a list of +numeric vectors. Alternatively, a single set of category +breakpoints to use for all values, as a one-entry list +containing a single numeric vector.} + +\item{label_sets}{sets of labels to associate, +with the corresponding breakpoints in \code{break_sets}, +with one set for each entry of \code{values}, as a list of +character vectors. Alternatively, a single set of labels +to use for all values, as a one-entry list containing a +single character vector.} + +\item{include.lowest}{Passed to \code{\link[base:cut]{base::cut()}}. Default \code{TRUE}.} + +\item{order}{Passed to \code{\link[base:cut]{base::cut()}}. Default \code{TRUE}.} + +\item{right}{Passed to \code{\link[base:cut]{base::cut()}}. Default \code{TRUE}.} + +\item{...}{Additional keyword arguments passed to +\code{\link[base:cut]{base::cut()}}.} +} +\value{ +A categorized vector, as vector of ordered +factors. +} +\description{ +Vectorized version of \code{\link[base:cut]{base::cut()}} that +allows each entry in the vector to use different +break points and labels. +} diff --git a/man/get_prism_cutpoints.Rd b/man/get_prism_cutpoints.Rd new file mode 100644 index 0000000..25cde3d --- /dev/null +++ b/man/get_prism_cutpoints.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/categorize_prism.R +\name{get_prism_cutpoints} +\alias{get_prism_cutpoints} +\title{Get PRISM activity level cutpoints for given +diseases and locations} +\usage{ +get_prism_cutpoints(locations, diseases) +} +\arguments{ +\item{locations}{location(s) for which to return the cutpoints. +A location two-letter abbreviation as in the \code{short_name} +column of \link{us_location_table}, or an array +of such abbreviations.} + +\item{diseases}{disease(s) for which to return the cutpoints. +One of \code{"ARI"}, \code{"COVID-19"}, \code{"Influenza"}, or \code{"RSV"}, or +an array of those values.} +} +\value{ +The cutpoints, as a single vector or array of vectors. +} +\description{ +Get PRISM activity level cutpoints for given +diseases and locations +} +\details{ +Note that if both \code{locations} and \code{diseases} are vectors, +the result will be a 3-dimensional array whose first dimension +is location, second is disease, and third is the cutpoints. +} +\examples{ +get_prism_cutpoints("WA", "Influenza") + +get_prism_cutpoints(c("US", "WA"), "COVID-19") + +get_prism_cutpoints(c("US", "WA"), c("ARI", "RSV")) +} diff --git a/man/prism_thresholds.Rd b/man/prism_thresholds.Rd new file mode 100644 index 0000000..d261d51 --- /dev/null +++ b/man/prism_thresholds.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{prism_thresholds} +\alias{prism_thresholds} +\title{PRISM respiratory virus activity level thresholds} +\format{ +An object of class \code{array} of dimension 53 x 4 x 6. +} +\source{ + +} +\usage{ +prism_thresholds +} +\description{ +A multi-dimensional array with PRISM +respiratory virus activity level thresholds. +Dimensions, in order, are \code{location}, +\code{disease}, and \code{breaks}. +} +\details{ +Values of \code{disease} are \code{Influenza}, \code{COVID-19}, +\code{RSV}, and \code{ARI} (acute respiratory infections). + +Values of \code{breaks} are \code{prop_lower_bound}, +\code{prop_low}, \code{prop_moderate}, \code{prop_high}, +\code{prop_very_high}, and \code{prop_upper_bound}. + +Values of \code{location} are US jurisdictions +and the United States as a whole, using +USPS two-letter codes (the values of \code{short_name}) +in \link{us_location_table}. +} +\keyword{datasets} diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 0000000..a328e6d --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + +library(testthat) +library(forecasttools) + +test_check("forecasttools") diff --git a/tests/testthat/test_categorize_prism.R b/tests/testthat/test_categorize_prism.R new file mode 100644 index 0000000..0af71ac --- /dev/null +++ b/tests/testthat/test_categorize_prism.R @@ -0,0 +1,75 @@ +test_that(paste0( + "get_prism_cutpoints() works identically ", + "to a manual read from the table " +), { + locations <- names(forecasttools::prism_thresholds[, 1, 1]) + diseases <- names(forecasttools::prism_thresholds[1, , 1]) + locs_diseases <- tidyr::expand_grid( + locations, + diseases + ) + + purrr::pmap( + locs_diseases, + \(locations, diseases) { + result <- get_prism_cutpoints(locations, diseases) + expected <- forecasttools::prism_thresholds[ + locations, diseases, + ] + expect_equal(result, expected) + } + ) +}) + +test_that(paste0( + "get_prism_cutpoints() gives the expected ", + "values for particular examples" +), { + test_cases <- list( + list( + diseases = c( + "Influenza" + ), + locations = c("US", "MA"), + expected = array( + c( + 0, 0, + 0.00339654, 0.002146614, + 0.019232328, 0.020711687, + 0.035068116, 0.03927676, + 0.050903904, 0.057841833, + 1, 1 + ), + dim = c(2, 6), + dimnames = list( + location = c("US", "MA"), + breaks = c( + "prop_lower_bound", + "prop_low", + "prop_moderate", + "prop_high", + "prop_very_high", + "prop_upper_bound" + ) + ) + ), + list( + diseases = "RSV", + locations = "UT", + expected = c( + prop_lower_bound = 0, + prop_low = 0.0004531255, + prop_moderate = 0.0088554939, + prop_high = 0.0172578623, + prop_very_high = 0.0256602307, + prop_upper_bound = 1 + ) + ) + ) + ) + + purrr::map(test_cases, \(x) { + result <- get_prism_cutpoints(x$locations, x$diseases) + expect_equal(result, x$expected) + }) +}) diff --git a/tests/testthat/test_categorize_vector.R b/tests/testthat/test_categorize_vector.R new file mode 100644 index 0000000..33fbd3a --- /dev/null +++ b/tests/testthat/test_categorize_vector.R @@ -0,0 +1,231 @@ +n_categories <- 10 +n_cutpoints <- n_categories + 1 +n_to_categorize <- 100 + +withr::with_seed(5, { + to_categorize <- runif(n_to_categorize, -100, 100) + cutpoints <- runif(n_to_categorize * n_cutpoints, -200, 200) |> + array(dim = c(n_to_categorize, n_cutpoints)) |> + apply(1, sort) |> + t() + ## need lower and upper bound cutpoints in addition to one + ## for each label + lab_no <- sample(1:(5 * n_to_categorize * n_categories), + n_to_categorize * n_categories, + replace = FALSE + ) +}) + +labs <- glue::glue("L_{lab_no}") |> + array(dim = c(n_to_categorize, n_categories)) + +testthat::test_that("test data has appropriate dimensions", { + expect_equal(dim(cutpoints), c(n_to_categorize, n_cutpoints)) + expect_equal(dim(labs), c(n_to_categorize, n_categories)) + expect_equal(dim(array(to_categorize)), n_to_categorize) +}) + + +testthat::test_that("categorize_vector() argument checking works", { + ## this works + expect_no_error(categorize_vector( + to_categorize, + list(cutpoints[1, ]), + list(labs[1, ]) + )) + + ## these error even though the entries are the right + ## shapes and types because one or the other is + ## not wrapped into a length-1 list + expect_error(categorize_vector( + to_categorize, + list(cutpoints[1, ]), + labs[1, ] + )) + expect_error(categorize_vector( + to_categorize, + cutpoints[1, ], + list(labs[1, ]) + )) + + ## these errors because a dimension for breaks or + ## labels is neither 1 nor the length of to_categorize + expect_error(categorize_vector( + to_categorize, + list( + cutpoints[1, ], + cutpoints[2, ] + ), + list(labs[1, ]) + )) + expect_error(categorize_vector( + to_categorize, + list( + cutpoints[1, ] + ), + list( + labs[1, ], + labs[2, ] + ) + )) + + ## this errors because the labels are not a list of + ## character vectors + expect_error(categorize_vector( + to_categorize, + list(cutpoints[1, ]), + list(a = rep(1L, length(labs[1, ]))) + )) + + ## this errors because the cutpoints are not numeric + expect_error(categorize_vector( + to_categorize, + list(as.character(cutpoints[1, ])), + list(labs[1, ]) + )) +}) + +testthat::test_that( + paste0( + "categorize_vector() is equivalent to cut() ", + "for a single set of bins and cutpoints" + ), + { + expected <- cut( + to_categorize, + breaks = cutpoints[1, ], + labels = labs[1, ], + include.lowest = TRUE, + order = TRUE, + right = TRUE + ) + + result <- categorize_vector( + to_categorize, + list(cutpoints[1, ]), + list(labs[1, ]) + ) + expect_equal(expected, result) + } +) + +testthat::test_that( + paste0( + "categorize_vector() is equivalent to iterated ", + "cut() with paired sets of cutpoints ", + "and labels" + ), + { + expected_vec_both <- replicate(n_to_categorize, NA, simplify = FALSE) + for (i in 1:n_to_categorize) { + expected_vec_both[[i]] <- cut( + to_categorize[i], + breaks = cutpoints[i, ], + labels = labs[i, ], + include.lowest = TRUE, + order = FALSE, + right = TRUE + ) + } + result_vec_both <- categorize_vector(to_categorize, + purrr::array_branch(cutpoints, 1), + purrr::array_branch(labs, 1), + order = FALSE + ) + + expect_equal( + unlist(expected_vec_both), + result_vec_both + ) + } +) + +testthat::test_that( + paste0( + "categorize_vector() is equivalent to iterated ", + "cut() with paired sets of cutpoints ", + "and labels, and can produce ordered factors ", + "provided all label sets are the same." + ), + { + expected_vec_both <- replicate(n_to_categorize, NA, simplify = FALSE) + for (i in 1:n_to_categorize) { + expected_vec_both[[i]] <- cut( + to_categorize[i], + breaks = cutpoints[i, ], + labels = labs[1, ], + include.lowest = TRUE, + order = TRUE, + right = TRUE + ) + } + result_vec_both <- categorize_vector( + to_categorize, + purrr::array_branch(cutpoints, 1), + replicate(n_to_categorize, + labs[1, ], + simplify = FALSE + ) + ) + + expect_equal( + factor(unlist(expected_vec_both), ordered = TRUE), + result_vec_both + ) + } +) + + + + + +testthat::test_that( + paste0( + "categorize_vector() is equivalent to iterated ", + "cut() with vectorized cutpoints and single ", + "sets of labels, or the reverse." + ), + { + expected_vec_bins <- replicate(n_to_categorize, NA, simplify = FALSE) + expected_vec_labs <- replicate(n_to_categorize, NA, simplify = FALSE) + + for (i in 1:n_to_categorize) { + expected_vec_bins[[i]] <- cut( + to_categorize[i], + breaks = cutpoints[i, ], + labels = labs[25, ], + include.lowest = TRUE, + order = TRUE, + right = TRUE + ) + expected_vec_labs[[i]] <- cut( + to_categorize[i], + breaks = cutpoints[7, ], + labels = labs[i, ], + include.lowest = TRUE, + order = FALSE, + right = TRUE + ) + } + result_vec_labs <- categorize_vector(to_categorize, + list(cutpoints[7, ]), + purrr::array_branch(labs, 1), + order = FALSE + ) + + result_vec_bins <- categorize_vector( + to_categorize, + purrr::array_branch(cutpoints, 1), + list(labs[25, ]) + ) + + expect_equal( + factor(unlist(expected_vec_bins), ordered = TRUE), + result_vec_bins + ) + expect_equal( + unlist(expected_vec_labs), + result_vec_labs + ) + } +) diff --git a/tests/testthat/test_to_hubverse.R b/tests/testthat/test_to_hubverse.R index 0a11854..fd7bac5 100644 --- a/tests/testthat/test_to_hubverse.R +++ b/tests/testthat/test_to_hubverse.R @@ -9,7 +9,7 @@ testthat::test_that( tibble::tibble(), "2025-01-01" ), - "which is day number 4" + "4" ) testthat::expect_error( forecasttools::get_hubverse_table( @@ -17,7 +17,7 @@ testthat::test_that( "2025-01-01", week_start = 3 ), - "which is day number 1" + "1" ) testthat::expect_error( forecasttools::get_hubverse_table( @@ -25,7 +25,7 @@ testthat::test_that( "2025-01-01", reference_dow = 3 ), - "to be day number 3" + "3" ) } )