From 5bf397c8bcef5fd23e3a1216b6c5b2f0eea92c2f Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Tue, 14 Nov 2023 15:10:59 -0500 Subject: [PATCH 01/15] added Aggregated sims and obs contribution to the likelihood in inference --- flepimop/main_scripts/inference_slot.R | 48 ++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/flepimop/main_scripts/inference_slot.R b/flepimop/main_scripts/inference_slot.R index 38c1f1f2b..887c0f48f 100644 --- a/flepimop/main_scripts/inference_slot.R +++ b/flepimop/main_scripts/inference_slot.R @@ -32,9 +32,12 @@ option_list = list( optparse::make_option(c("-L", "--reset_chimeric_on_accept"), action = "store", default = Sys.getenv("FLEPI_RESET_CHIMERICS", FALSE), type = 'logical', help = 'Should the chimeric parameters get reset to global parameters when a global acceptance occurs'), optparse::make_option(c("-M", "--memory_profiling"), action = "store", default = Sys.getenv("FLEPI_MEM_PROFILE", FALSE), type = 'logical', help = 'Should the memory profiling be run during iterations'), optparse::make_option(c("-P", "--memory_profiling_iters"), action = "store", default = Sys.getenv("FLEPI_MEM_PROF_ITERS", 100), type = 'integer', help = 'If doing memory profiling, after every X iterations run the profiler'), - optparse::make_option(c("-g", "--geoid_len"), action="store", default=Sys.getenv("GEOID_LENGTH", 5), type='integer', help = "number of digits in geoid") + optparse::make_option(c("-g", "--geoid_len"), action="store", default=Sys.getenv("GEOID_LENGTH", 5), type='integer', help = "number of digits in geoid"), + optparse::make_option(c("-a", "--incl_aggr_likelihood"), action = "store", default = Sys.getenv("INCL_AGGR_LIKELIHOOD", TRUE), type = 'logical', help = 'Should the likelihood be calculated with the aggregate estiamtes.') ) + + parser=optparse::OptionParser(option_list=option_list) opt = optparse::parse_args(parser) @@ -61,6 +64,12 @@ if (opt$config == ""){ config = flepicommon::load_config(opt$config) +if (!is.null(config$inference$incl_aggr_likelihood)){ + print("Using config option for `incl_aggr_likelihood`.") + opt$incl_aggr_likelihood <- config$inference$incl_aggr_likelihood +} + + if (!is.null(config$seeding)){ if (('perturbation_sd' %in% names(config$seeding))) { if (('date_sd' %in% names(config$seeding))) { @@ -140,14 +149,14 @@ if (all(npi_scenarios == "all")){ ##Creat heirarchical stats object if specified hierarchical_stats <- list() -if ("hierarchical_stats_geo"%in%names(config$inference)) { +if ("hierarchical_stats_geo" %in% names(config$inference)) { hierarchical_stats <- config$inference$hierarchical_stats_geo } ##Create priors if specified defined_priors <- list() -if ("priors"%in%names(config$inference)) { +if ("priors" %in% names(config$inference)) { defined_priors <- config$inference$priors } @@ -201,6 +210,7 @@ if (config$inference$do_inference){ # variant_filename = config$seeding$variant_filename # ) + # Read observed ground truth obs <- suppressMessages( readr::read_csv(config$inference$gt_data_path, col_types = readr::cols(FIPS = readr::col_character(), @@ -212,6 +222,20 @@ if (config$inference$do_inference){ dplyr::mutate_if(is.numeric, dplyr::coalesce, 0) %>% dplyr::rename(!!obs_nodename := FIPS) + # add aggregate groundtruth to the obs data for the likelihood calc + if (opt$incl_aggr_likelihood){ + obs <- obs %>% + dplyr::bind_rows( + obs %>% + dplyr::select(date, where(is.numeric)) %>% + dplyr::group_by(date) %>% + summarise(across(everything(), sum)) %>% # no likelihood is calculated for time periods with missing data for any subpop + mutate(source = "Total", + FIPS = "Total", + geoid = "Total") + ) + } + geonames <- unique(obs[[obs_nodename]]) @@ -234,7 +258,7 @@ if (config$inference$do_inference){ likelihood_calculation_fun <- function(sim_hosp){ - sim_hosp <- dplyr::filter(sim_hosp,sim_hosp$time >= min(obs$date),sim_hosp$time <= max(obs$date)) + sim_hosp <- dplyr::filter(sim_hosp, sim_hosp$time >= min(obs$date), sim_hosp$time <= max(obs$date)) lhs <- unique(sim_hosp[[obs_nodename]]) rhs <- unique(names(data_stats)) all_locations <- rhs[rhs %in% lhs] @@ -476,7 +500,7 @@ for(npi_scenario in npi_scenarios) { } proposed_seeding <- initial_seeding } - + # proposed_snpi <- inference::perturb_snpi_from_file(initial_snpi, config$interventions$settings, chimeric_likelihood_data) # proposed_hnpi <- inference::perturb_hnpi_from_file(initial_hnpi, config$interventions$settings, chimeric_likelihood_data) # proposed_spar <- inference::perturb_spar_from_file(initial_spar, config$interventions$settings, chimeric_likelihood_data) @@ -512,6 +536,18 @@ for(npi_scenario in npi_scenarios) { sim_hosp <- flepicommon::read_file_of_type(gsub(".*[.]","",this_global_files[['hosp_filename']]))(this_global_files[['hosp_filename']]) %>% dplyr::filter(time >= min(obs$date),time <= max(obs$date)) + # add aggregate groundtruth to the obs data for the likelihood calc + if (opt$incl_aggr_likelihood){ + sim_hosp <- sim_hosp %>% + dplyr::bind_rows( + sim_hosp %>% + dplyr::select(-tidyselect::all_of(obs_nodename), -starts_with("date")) %>% + dplyr::group_by(time) %>% + dplyr::summarise(dplyr::across(tidyselect::everything(), sum)) %>% # no likelihood is calculated for time periods with missing data for any subpop + dplyr::mutate(!!obs_nodename := "Total") + ) + } + lhs <- unique(sim_hosp[[obs_nodename]]) rhs <- unique(names(data_stats)) all_locations <- rhs[rhs %in% lhs] @@ -598,7 +634,7 @@ for(npi_scenario in npi_scenarios) { effective_index <- (opt$this_block - 1) * opt$iterations_per_slot + this_index avg_global_accept_rate <- ((effective_index-1)*old_avg_global_accept_rate + proposed_likelihood_data$accept)/(effective_index) # update running average acceptance probability - proposed_likelihood_data$accept_avg <-avg_global_accept_rate + proposed_likelihood_data$accept_avg <- avg_global_accept_rate proposed_likelihood_data$accept_prob <- exp(min(c(0, proposed_likelihood - global_likelihood))) #acceptance probability From df83fa81aeebc1f51057b70ec4322da89a251821 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 12:55:00 -0500 Subject: [PATCH 02/15] remove all instances of reshape2 and plyr packages --- build/renv/renv.lock | 24 ------------------- flepimop/R_packages/flepicommon/DESCRIPTION | 1 - flepimop/R_packages/flepicommon/NAMESPACE | 1 - flepimop/R_packages/flepicommon/R/DataUtils.R | 3 +-- 4 files changed, 1 insertion(+), 28 deletions(-) diff --git a/build/renv/renv.lock b/build/renv/renv.lock index c02e1a5c1..0616d96ae 100644 --- a/build/renv/renv.lock +++ b/build/renv/renv.lock @@ -945,8 +945,6 @@ "foreach", "glue", "lubridate", - "raster", - "reshape2", "rlang", "sf", "stringdist", @@ -1520,16 +1518,6 @@ "Hash": "2ebe8c2ec200da649738b0fc35a9b1a1", "Requirements": [] }, - "plyr": { - "Package": "plyr", - "Version": "1.8.8", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "d744387aef9047b0b48be2933d78e862", - "Requirements": [ - "Rcpp" - ] - }, "png": { "Package": "png", "Version": "0.1-7", @@ -1769,18 +1757,6 @@ "withr" ] }, - "reshape2": { - "Package": "reshape2", - "Version": "1.4.4", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "bb5996d0bd962d214a11140d77589917", - "Requirements": [ - "Rcpp", - "plyr", - "stringr" - ] - }, "reticulate": { "Package": "reticulate", "Version": "1.25", diff --git a/flepimop/R_packages/flepicommon/DESCRIPTION b/flepimop/R_packages/flepicommon/DESCRIPTION index e5c32cbba..e7c0a9daf 100644 --- a/flepimop/R_packages/flepicommon/DESCRIPTION +++ b/flepimop/R_packages/flepicommon/DESCRIPTION @@ -19,7 +19,6 @@ Imports: rlang, cdlTools, ggraph, - plyr, doParallel, foreach, jsonlite, diff --git a/flepimop/R_packages/flepicommon/NAMESPACE b/flepimop/R_packages/flepicommon/NAMESPACE index d7f0983f1..c6ded5703 100644 --- a/flepimop/R_packages/flepicommon/NAMESPACE +++ b/flepimop/R_packages/flepicommon/NAMESPACE @@ -36,4 +36,3 @@ import(lubridate) import(purrr) import(vroom) importFrom(magrittr,"%>%") -importFrom(plyr,revalue) diff --git a/flepimop/R_packages/flepicommon/R/DataUtils.R b/flepimop/R_packages/flepicommon/R/DataUtils.R index c1a73d0b9..267ffc65b 100755 --- a/flepimop/R_packages/flepicommon/R/DataUtils.R +++ b/flepimop/R_packages/flepicommon/R/DataUtils.R @@ -163,7 +163,6 @@ download_USAFacts_data <- function(filename, url, value_col_name, incl_unassigne ##' $ Confirmed: num [1:198] 3 4 1 3 5 1 3 5 2 3 ... ##' $ Deaths : num [1:198] 0 0 0 0 0 0 0 0 0 0 ... ##' -##' @importFrom plyr revalue ##' @return the case data frame ##' get_islandareas_data <- function() { @@ -180,7 +179,7 @@ get_islandareas_data <- function() { nyt_data <- dplyr::filter(nyt_data, state %in% names(ISLAND_AREAS)) nyt_data <- dplyr::rename(nyt_data, Update=date, source=state, FIPS=fips, Confirmed=cases, Deaths=deaths) # Rename columns - nyt_data <- dplyr::mutate(nyt_data, FIPS=paste0(FIPS,"000"), source=plyr::revalue(source, ISLAND_AREAS, warn_missing=FALSE)) + nyt_data <- dplyr::mutate(nyt_data, FIPS=paste0(FIPS,"000"), source=dplyr::recode(source, ISLAND_AREAS)) validation_date <- Sys.getenv("VALIDATION_DATE") if ( validation_date != '' ) { From 0fe7dd00f27630c0f682b388bb4ecd75e9540239 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 12:55:00 -0500 Subject: [PATCH 03/15] remove all instances of reshape2 and plyr packages --- build/renv/renv.lock | 25 ------------------- flepimop/R_packages/flepicommon/DESCRIPTION | 1 - flepimop/R_packages/flepicommon/NAMESPACE | 1 - flepimop/R_packages/flepicommon/R/DataUtils.R | 3 +-- 4 files changed, 1 insertion(+), 29 deletions(-) diff --git a/build/renv/renv.lock b/build/renv/renv.lock index c02e1a5c1..89da7e99f 100644 --- a/build/renv/renv.lock +++ b/build/renv/renv.lock @@ -945,10 +945,7 @@ "foreach", "glue", "lubridate", - "raster", - "reshape2", "rlang", - "sf", "stringdist", "stringi", "stringr", @@ -1520,16 +1517,6 @@ "Hash": "2ebe8c2ec200da649738b0fc35a9b1a1", "Requirements": [] }, - "plyr": { - "Package": "plyr", - "Version": "1.8.8", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "d744387aef9047b0b48be2933d78e862", - "Requirements": [ - "Rcpp" - ] - }, "png": { "Package": "png", "Version": "0.1-7", @@ -1769,18 +1756,6 @@ "withr" ] }, - "reshape2": { - "Package": "reshape2", - "Version": "1.4.4", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "bb5996d0bd962d214a11140d77589917", - "Requirements": [ - "Rcpp", - "plyr", - "stringr" - ] - }, "reticulate": { "Package": "reticulate", "Version": "1.25", diff --git a/flepimop/R_packages/flepicommon/DESCRIPTION b/flepimop/R_packages/flepicommon/DESCRIPTION index e5c32cbba..e7c0a9daf 100644 --- a/flepimop/R_packages/flepicommon/DESCRIPTION +++ b/flepimop/R_packages/flepicommon/DESCRIPTION @@ -19,7 +19,6 @@ Imports: rlang, cdlTools, ggraph, - plyr, doParallel, foreach, jsonlite, diff --git a/flepimop/R_packages/flepicommon/NAMESPACE b/flepimop/R_packages/flepicommon/NAMESPACE index d7f0983f1..c6ded5703 100644 --- a/flepimop/R_packages/flepicommon/NAMESPACE +++ b/flepimop/R_packages/flepicommon/NAMESPACE @@ -36,4 +36,3 @@ import(lubridate) import(purrr) import(vroom) importFrom(magrittr,"%>%") -importFrom(plyr,revalue) diff --git a/flepimop/R_packages/flepicommon/R/DataUtils.R b/flepimop/R_packages/flepicommon/R/DataUtils.R index c1a73d0b9..267ffc65b 100755 --- a/flepimop/R_packages/flepicommon/R/DataUtils.R +++ b/flepimop/R_packages/flepicommon/R/DataUtils.R @@ -163,7 +163,6 @@ download_USAFacts_data <- function(filename, url, value_col_name, incl_unassigne ##' $ Confirmed: num [1:198] 3 4 1 3 5 1 3 5 2 3 ... ##' $ Deaths : num [1:198] 0 0 0 0 0 0 0 0 0 0 ... ##' -##' @importFrom plyr revalue ##' @return the case data frame ##' get_islandareas_data <- function() { @@ -180,7 +179,7 @@ get_islandareas_data <- function() { nyt_data <- dplyr::filter(nyt_data, state %in% names(ISLAND_AREAS)) nyt_data <- dplyr::rename(nyt_data, Update=date, source=state, FIPS=fips, Confirmed=cases, Deaths=deaths) # Rename columns - nyt_data <- dplyr::mutate(nyt_data, FIPS=paste0(FIPS,"000"), source=plyr::revalue(source, ISLAND_AREAS, warn_missing=FALSE)) + nyt_data <- dplyr::mutate(nyt_data, FIPS=paste0(FIPS,"000"), source=dplyr::recode(source, ISLAND_AREAS)) validation_date <- Sys.getenv("VALIDATION_DATE") if ( validation_date != '' ) { From 81c7566a07dbdaa601a23c5963934c9a8516b207 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 12:59:23 -0500 Subject: [PATCH 04/15] remove rgdal package --- build/conda_environment.yml | 1 - build/environment_rockfish.yml | 1 - build/renv/renv.lock | 10 ---------- 3 files changed, 12 deletions(-) diff --git a/build/conda_environment.yml b/build/conda_environment.yml index 660e106ce..ce165be7c 100644 --- a/build/conda_environment.yml +++ b/build/conda_environment.yml @@ -408,7 +408,6 @@ dependencies: - conda-forge/linux-64::r-openssl==2.0.5=r42habfbb5e_0 - conda-forge/linux-64::r-promises==1.2.0.1=r42h7525677_1 - conda-forge/linux-64::r-raster==3.5_21=r42h7525677_1 - - conda-forge/linux-64::r-rgdal==1.5_32=r42h282678f_2 - conda-forge/noarch::r-rversions==2.1.2=r42hc72bb7e_1 - conda-forge/linux-64::r-s2==1.1.1=r42h5be344c_0 - conda-forge/noarch::r-scales==1.2.1=r42hc72bb7e_1 diff --git a/build/environment_rockfish.yml b/build/environment_rockfish.yml index 03ba033a5..ca200567a 100644 --- a/build/environment_rockfish.yml +++ b/build/environment_rockfish.yml @@ -388,7 +388,6 @@ dependencies: - r-remotes=2.4.2=r42hc72bb7e_1 - r-reprex=2.0.2=r42hc72bb7e_1 - r-reticulate=1.25=r42h884c59f_0 - - r-rgdal=1.5_32=r42h282678f_2 - r-rlang=1.0.6=r42h7525677_1 - r-rmarkdown=2.20=r42hc72bb7e_0 - r-roxygen2=7.2.3=r42h38f115c_0 diff --git a/build/renv/renv.lock b/build/renv/renv.lock index 89da7e99f..c5660ff43 100644 --- a/build/renv/renv.lock +++ b/build/renv/renv.lock @@ -1773,16 +1773,6 @@ "withr" ] }, - "rgdal": { - "Package": "rgdal", - "Version": "1.5-32", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "7e8f18c3a55c90a44ea05e0620c05a7e", - "Requirements": [ - "sp" - ] - }, "rlang": { "Package": "rlang", "Version": "1.0.6", From d116f6b5f534817171e2c5c3533897c28ffd4eb7 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 14:24:17 -0500 Subject: [PATCH 05/15] remove cdlTools package dependence --- build/conda_environment.yml | 1 - build/environment_rockfish.yml | 1 - build/renv/renv.lock | 11 - datasetup/usdata/fips_us_county.parquet | Bin 0 -> 30447 bytes datasetup/usdata/state_fips_abbr.parquet | Bin 0 -> 1935 bytes .../usdata/us_county_census_2019.parquet | Bin 0 -> 77107 bytes flepimop/R_packages/flepicommon/DESCRIPTION | 1 - flepimop/R_packages/flepicommon/NAMESPACE | 1 - flepimop/R_packages/flepicommon/R/DataUtils.R | 275 +----------------- postprocessing/plot_predictions.R | 100 +++---- postprocessing/sim_processing_source.R | 24 +- 11 files changed, 63 insertions(+), 351 deletions(-) create mode 100644 datasetup/usdata/fips_us_county.parquet create mode 100644 datasetup/usdata/state_fips_abbr.parquet create mode 100644 datasetup/usdata/us_county_census_2019.parquet diff --git a/build/conda_environment.yml b/build/conda_environment.yml index ce165be7c..f5da549af 100644 --- a/build/conda_environment.yml +++ b/build/conda_environment.yml @@ -444,7 +444,6 @@ dependencies: - conda-forge/noarch::seaborn-base==0.12.2=pyhd8ed1ab_0 - conda-forge/linux-64::r-arrow==10.0.1=r42hcb278e6_0 - conda-forge/noarch::r-bslib==0.4.2=r42hc72bb7e_0 - - defaults/linux-64::r-cdltools==0.15=r42h76d94ec_0 - conda-forge/noarch::r-gargle==1.3.0=r42h785f33e_0 - conda-forge/linux-64::r-gert==1.5.0=r42hf3f2ec2_3 - conda-forge/noarch::r-gh==1.3.1=r42hc72bb7e_1 diff --git a/build/environment_rockfish.yml b/build/environment_rockfish.yml index ca200567a..de65f406e 100644 --- a/build/environment_rockfish.yml +++ b/build/environment_rockfish.yml @@ -261,7 +261,6 @@ dependencies: - r-bslib=0.4.2=r42hc72bb7e_0 - r-cachem=1.0.6=r42h06615bd_1 - r-callr=3.7.3=r42hc72bb7e_0 - - r-cdltools=0.15=r42h76d94ec_0 - r-cellranger=1.1.0=r42hc72bb7e_1005 - r-class=7.3_21=r42h133d619_0 - r-classint=0.4_8=r42h8da6f51_0 diff --git a/build/renv/renv.lock b/build/renv/renv.lock index c5660ff43..0a074793a 100644 --- a/build/renv/renv.lock +++ b/build/renv/renv.lock @@ -348,17 +348,6 @@ "Hash": "ac6cdb8552c61bd36b0e54d07cf2aab7", "Requirements": [] }, - "cdlTools": { - "Package": "cdlTools", - "Version": "0.15", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "330978ce333c8b2f29bfa7744cd29e22", - "Requirements": [ - "httr", - "raster" - ] - }, "cellranger": { "Package": "cellranger", "Version": "1.1.0", diff --git a/datasetup/usdata/fips_us_county.parquet b/datasetup/usdata/fips_us_county.parquet new file mode 100644 index 0000000000000000000000000000000000000000..cbe5ff30f22f597c4d252b4851335e98621ad182 GIT binary patch literal 30447 zcmdSBd0-QD`aeETnSq&h@@|qQA?cB3hIXKlhBh>T7N{h>@0(ggWtvXgp-HAqGA%_B z3W6srxQl{vhzi~ZDk@$JiuZ|%tE-FRwd$&iw|KC>C$9VZ>}PjBzwht(=lA{N-ApFe z%=`Tuuh;8&-fa!E7h7rTt&H{9q(*DZN|`C@wG>MVMNxr9W-`@)V=<0)m`qm$@Sp`p zCqAjH!h;H!OjR>}s)osZs@@`H8N(ZiK)d=L^W|{ zCWuG=zdvJ^17eSD2uEj5|$I*mewHVJ|fuj?DHxI|lIG&?gnSQDrM+f8n zWppwzy7LUErMvLTs72gl{FQF}yPWD~=Hm!c0p?n|rVEPbrdF6-(%PQ9zpkd_-$iv+ z%KBQ0^;pVG>oKp@Xq{}Gob9J(q;&V#Qvwb6j$7cw&6Du)m=`9m2dWmFDObRx1icy< z!)N}d56zPO?KFs zctTDn))d<@ukBsdwtb4Uo|f2uzCXuITz<-$Lg61HK2X%(&Ed6wHHV^NToW&j5*$7p zejIagl%h2h7vW+RQN_53#j_dnKMv>Mp&!rs@q_<5!gy}}KL+ic3&CHW%*K;A=-FPd zClCI|QG&1i*YM-rf^P3>-#Tz_}vs~J1_11yFvVVm;ccq*6`L-+&|*O#N?S% za54g6xmS+JlesiDJ|r_%drV!SMP(MOjjur0eT+Na7sgPbwG~7>8>Uf zFFdX#WcU-`q{ibgi5(tRnJPOTYkEwDO~xiInve?$_-3UyCM*={EzQcP+$TeSQL{4U zs+A+d@j*4F2$f*6)GM*LGHxQ4CS|!A(qJ)UpV6bmh6r9nvldGXx+>(DhW91&R!VeC|v%W+|h?@Gvn@PlQM5{qcj{Lix&sW{D1RfZ$A-7;Qp zbReNclT*{J#1k%4E_AxO)p$sY#?>fP*t^HI2)+k476l>-E}tBAb;uEoIcBWU<%r$% ztzC&lT{y+UONPg&xUlV_R$YlDG*>&`brybYm#4#%oLnvDYS9znar`nD_XQo#Fy)r~ z{aWx&lFJkS&rUk!*DJ73`PDA*JO6ic*R9TfbJwUG`PE&YOSipbkUq?mo-!um_rKro ze{|f>^Q<>p8?Dzezhw6N%=Tm^D=I4HrckqS_;Ad@QOZoAin(e3HoSQ5U&m}b_phS_ z&;9H0;kkbuemwWDV-BAC*D)8*{p%>jbN_8Hc)bN@|9=~WXVb<0`~$SN2xlNU8-BwS zE}QnxWwP<2tjUSNreS{L2vaG#Bnd}oz1AUw}h$2R}SKtGx2c;KK5ej%epWJpTUU-nw$?|M{3hc?;_P7h@j2{;#71&;6%S zi0A)l)XD$;zPi!>^ZRDkt)ynF7gDqF8vZXwVI5aZ`6Bss<#bU|RTW+2t@;dCxM~}I zEdJj|-H-Tu#s9lew;w+@@t?;puU&+%{ofxY_}-F&AdckY|7x6#@A?1d@Zoj+)1ZrH zS6zcAS}EU6>wg`~@$oq2dwM!PuELRgK0^7pG0KnU{AvmYkf#4MX#X6=i7#~G`25$= zi%xvze;)-;;`8MBe;RmIe}CSIQRiPr6IFzcRfN%}h|B(E6m!LSMZS_`D4ktYg!$;^ zQtK0$|D8Wfnp;w2pM;SX-*hzl#QI+u1hB+%d4KXdCq$1Jjkei&Q1Z%yINOY zHZh6Qzjgtvrcx#wXDoe(T0^JWiU!|#2QFaEQtORNkEg8XGyTIKy>oohMMklD(+v4t z`VxzK(Vc_G-(@bdd;4FyQGRdo#_Y1u6CWLaZ^|ZD@R_ZpYo!fW`N9-!ELyz~CQUKs zwcfcpFlkD=={on8o4amZd|(B0+7-9< z+_UVB2iBc-; z3>A|NS#uL7PCzAVnpXR}qLGxURO@MH+%x<|N;U78J^1RKBa>=K&b;McpZH=@tx-^( z+aCCB_4Ap2?VZD$XJ6QyGjHs~`$z7+uqF5ORX0+pd4rp*!BbyMTKUDD1)&f5t4fx= zcIWt{=^M9~CO&!nz}o4**}34nAHF|X!L+&pYi~8WFL?aQ-4z#6bU_onYRai*7;T1< z3sbLIxMp&X(8*pptK!18+gV(&rrS&xow|^!uBBLd{jvCn}a6dGzr# zdvV`$*R(zMgdw{$@##B99(yv~6}aGc#qr5anZBXdADYOhc_F(@|CCBD;Y7-0;;Ox7 zDXg7LoC_~<$tmkj+30Py@)s#;$|H-JWsXUUUZGh!b=9elCj~#A)OV0#ezBo*))S%r z`a#om)f=6GOY%ORy87DL}zN zrB8fuNPWDDqMm7^-rYgduk-$=rm*c#@ITx%$^L6p@%vMk96NaNv;nfCY}M%J?>v3o zK>Duo^G~&cg?;8P6g~XKhkK@+xOd&1-+L2mn^<+J-52|sZO^MaQ);`p?_=}c#;g1z zpS=HZ#*A6JD>sN;&ses0+}M88;>T~^a!dD}y-#0Nb;}jEoq2!k<3B!r%hY|nPizz# z_J$pI_rI_+aH$l@+`sI#+vR8XMzZdU9(!aWZF)5OKZ?OG*lw zw^55P<`=b0qDwQbI>6mNOw+9^F8QA8KbxL;M*RiqD`G>}O!-~Q=u;_l%bb^o4B5vH zPCW2Cwu6#3wf-0PNDFCtG%@~9TEVHaZLbwc>wVG#xk>WzU&-x1l8#RvrmcJTq%>N; zcm6^6_VyamDFdWa zVJJ74m`aNt4r`$ygPEDeBRf<~6)??$(!!von+9g4<7X-cxcd?olFP=vk86!W66xFel2^(WdG(KqE#q2ZURYqjfV{i>y zDTh59C?xFRuw2Z-PGhCgfTv<$tO`f8m}xc)*sJ0pyk^z35q!p~gf=Fd?ttZ%YIz(* z2alh!RLA7#P}uhw_>DD6R2lWuP<%~HQGBPtZB(t}Hi6-5m1wN^Ea>NJH9cNB3CzZN zIW!da-7OTLw9wzL#2gl=<<} zH>wc@R+&UtZE4iTqJ2tntDVPBHOV3CCJ^~1xleU$#L2|Z4QPd*2@X6JQ>{L@EE^Bv zgLuE5Jc`wNRqr#9R6=5!=`wgNJE@5bXc5IXn>mw31*gl1lhmrjtO9Jbw5p*YC2D$9 zSVy<2UB}@vdpi<+Ej;Y`9E|OHJg!)G3fb8m36BdGOfBw*bB!H?O1QW9_l$|vBMJQ` z@Kc?E4RoKfB@dopEY$E>rx>eI#~BXiSfPdQmcv8V7Lf2DKIqM##7IUp9MYB-*1;ES zw;C0mFqkRZ#7&)HMNgn~6n9n0sB;9hn5GX5W_mNJ`M(Gs6}Z-V+!cr^GBdX`8X81F zYd~=|=}|SL4x`qU7&lMcYg7_=d7dJQ$^ctl3>7Uc>VE|VsaE0UC+NpT1UXLoW?yA7+bK%^Y=0et7l*44z zg_@Cy_Y{^*S|>1LEQ-@xt2iKL0_WPOHzS=oVdel;$r; zK-AcwCWho-90ZZ?7?KBsX3s>$aO3;`|%@G_^2OLOQY;+l~-(tLbpBXS0QZ}mmb|{P#!g%i(#T! z9+j7Q#woT{jhKq}JkFvpcqC`y2Nc^-b97b2bdw4%+k5bB!?J2P7k*Fo=+Eys70*2k8qy=-ABTO-GE9)~Urf^yV!682eY71UA_j(xB*uU1RAI)-EDFNP;z3C6=Q*?Jf~ z5tU$Nm^m!o!-7oLt#C{iR`T`QU{qSoLY!@s`@OewAXysKXh;he6F3JC6PRI+tYY4S zbLd7*YGDCwT#IVrEEY^`lNy#brXA;hRe$N>;kHX*y73o{xA<(%M2+>FAuP3^h2qKw z{=lq+47zpj(1+A;@g63_qM@@XiQ*37Mz&Q8w>5C-)bPle@QI}rCoHBWQz80de8{u| z?j|p=5w7f?=zz1rL zxcG^f@G%wlJqz<$6!p!U*y4-9l3f*cRVJ1H%_m_UUma88``6+@O<0K*w+6soiz2@k z?rQ_cu-8Q}9;)&_0Zy@X(Q(rvNM{?An5hyv`379w;!hygyp%1N2^V1CiN}3fz-4UI zFeD$kU;H5rrYCch!hAyhN}zl9C#C_|3Px(!{|5s~?3h$SZzxPHuf#&i=G*Z=!!w3W zu#0Zed}gNGhT_ z#8yj(9E!=2LzJO`v)XZbF`y<)R`N23#Yw9%7L_sGz6IUj;44*Sz*J~H%+hjnBua%$ zcA+2a#(GU2(S22Bo-4xTX~e>WtEE?s51WciOYn9QipgSFl8ycylrjAKt~M+Q{DulU zY{F!=V^Hqdi%C$$R617(XLqRaA$45S3xUaFwXp2lgqIxEz8TD)hF=g>Q9{?-`WQ)W z8aW$Ge5W!fTj$8v@tx>7#K!ep%y**sd9O~VJN4t1En5(KEJ{{QtoH;AY!7-!29r+f zz4C9`_V9r|Ibzxh3nv^s*TdHsVKC+libF&gvSVBy)WcX`VCvuu3^XCr8B98*9Q_Eo z`JkqU&0HE&B-mWRn5ISgRVCc^7nsf>hI1F1;CxHPpfavRt?vmBvlwK^i|H)Fo_!xN z^KgBXWt!>Pyn6!fx#@GryE*)p(XwvqquY-V(QS%xDa{wxhmC53h z{sU*G^#N&-398sCdBA&HI$aem`_g1V6K$2_#al7bp@Bv;UkMyc1{@E{B^&4(b@$$; z3{XicTO43YSUnnb4;vwauSIt*`_yQx#cD=>&%IEGMyib!9~5?38q@?9Z`^ezXdut0 z(zq76aG$Xe!+NCaX(*!`<=sw7-PRgNr<;_cHk@fJf1vSQ3xC9j-`_9qdm_-c?hn)o z|6QqIp<|l3Hx2IRThKs@YZxP~$HdJUFwVBhu{H??_*P7WTUy}~zBM@?nHFXmt0SuQ z!jbHDRdWTgc(P@u$(p}%p9m}2c1;(}tWd;u$OGgFWY=7VG3u(<<>JH8F!2gTw*j4a zjaB{345QLFFs6Dxw(uQ#BqChFAfvZx$-ZK7%Ki0%!2Gax76s(u^2G z5#h|BU_RE)`uZ?^E6#Z)u-N}z0UP~>hljzEjH-RaZDh6yU2LZoc7LA=W**U4pET0| zw^9w;1XC?rfw3)-%GJVgs)MLz)0*8cAvF3&k&wK_+KfDgZ46mxb8QEOOX;92<(SEH z8ALa6J;G$NhhH!mDln-jqyBkmbTH;gqzQmlCda~>G?|0zc?3qW{eQ&e!$cOA-s9je zR3%AC=T?Fjb5n=*%`CW;tyGtbm!~GDepJtfcDgz|(Q>2^FS$nUi%XC2aJ3N=duWiz z<{Xu+3Cq$B3tY_B=wV^*?w`kv0V;{j6Z$8CUQ3fIN7TZy%Ybjjm`PqV`z&y|7wowmtO@|TNx9L>7|Mn>u8e?)?+kYJ-EFB6K2(_b|m<*#nCP$;zBf<}Sr#v1O z|C$Ex8W$meh>PD%O&S2Awv(;kqr2s@F9{nM9wrxA3=hB%71%u@ts#Py3cB;Aaz?Vt zQUI`s3X)=@Gn{TPQ3Km?b<$ zo}uC)W+NRN-@l9LF~>679%n>Km*`B+NGB4eAatPZMaFla=~N(QmTc*qnM$}{w87Qu zmPnJ*@k1buPvIyj@Ps`nOg(gO_ zn6=0*M|Ab*qnt=-p3|owY#Nik<={si(NWy_2!RkX$g$E-VLcMZkem?T&4Sr{rPep_ zg&D67Km9B`hZcJNR9cDYZ$lmivzRpx28>m>l;Pqy_}Hrce&^q8N-dnrS4HIL$u6db z8YLf2ltY2>QJbF<-6x>kAP8^k7cG3nYISjg6+qhDw*D47@~au)BZlt-;yaH+9g zi4Lh^btd-r)N5psotp!-2;8D4tuPydD0$q7A?qqcYT}n>yfQs9EZxe%+m@zKCBmBh zC?z5YOdxH)7Iqt(5L}Fr3C9=3%nUvmq@+nExSVa$W727PY6JQiIH4^e?SKSl zWw#83kE;}qzIM6SyWeTS z()M6P^Bzm%+flo4KbrMuu$_aNU*y?N zwcqm)YwX12+S}$7axu7$b$OWObW9;9b3NUYFyCvsX9LZ4>M{RrE8jV$4v3$i@6cVt zrKi0B0a-G z*j!;`$K=G8JRyy(Q6h#FNwe3qvR5bZ$qtj^#h7ccUBld*{rCvn#y4nM!c6CI8E_j~ zV?r!Iu+1uw!Qxfupkr#=hv-t6(A7STe4fQD64oF^SmA2jQI7uD5>-^+0mej+jZ0=0 zeowV}-=B){t(AOC3|GStsvBB-&>yy8nw5%KVLH`$h-a(i@Hay+#3Emnf*kC&2g2CA z5$*Fe%5_w5+W|O_G#Ey1hDvhCCXS^+k#MbHJ9LnJ(|9C<1EvzrLf+;{AzzRUACi|@ z$K9|D1Cn%w6&|B2bn?SAvXjr5gZa+YEq%noX0}pqB~Ryai_zh#`UZ$%#?G{5bX6Z& z?J!*rcT?3;XQt3X)sW|1QjGtFnd#AQJ^~Xt+aM0J@G3r1N*UtUx|lYiYh5;&%O_i2 z#N$?Y4c+;JwUi?6%Y@BzgS=%sY(}$)4#`7DUxdeblqdQQ+`^PlQE!fbQ`{jOD`fHK z8&xu%=k^K+rqIQ;D7k?%6u=U?Ddf3h8r;h_g&GyHJ`FA>+0$r!*ayM@-6U`O8iUs+ zgYz>R8i+!wZH7VQYBA{{8?%TI%QikYof7T;zepXw%`I^0)Hg%fnQDxQ-A!$@;5M>~em{X?m2uRO5%QFw^V$fWvv zo}_s5S0WYurc+>(6ab@9Sc`~1XG4<_ogg%nQx*VXN$uH;gtby3Z%-G899+hsYsANG z=qHtWZ_npw`BmYeLFp0}m9lF2LQ3u`1wRJ*UdKC-XZtwS7lOU=*(_=f;xU7e&DWwt z>&?&G{R34m&bB~3U9S)uFV0}0KD$AShg6h#JPRNhfGLakEDsMLv~9(SdlfChtQ(sG zU}T%MUh(2|;elkslBp9Jz?g3(c*w&R@6#!8_P)c6Kxy0Tf{krghYxIrHo9H!S^%eP zXBz3a;(aln?ieQDOQvq9sXAmDhc}EJ2}SRNAa*sQ?(IMcpWq z154#1s!lwQ6*g0KQkfljbAzIpN2cY!1q+S1RB>IbN(V3cjn|cqkXu*9Tsy2uVl{DN zHdEw3&LLtP#H+~TpqPxhOsC)iGA)OSI@4|N19{7Ee5dIn*vhs{54yc(7^GSyngw4H zA!@EbSiIF5_#XN3c!Zeq3l|AP*{xa#!B8||y_>R-_ww_!1S;>CwB(4mk1(Bf4Yj{P zRcR}NPE3-~fdNhAvV>l$;~NIv<~vkPcTSm#Fgu}%8Ay4kPJdapv0ENi!rsU1dwwn- zqjsO7dv$A_Fc+0M#LEa;uBHOyb?(3-1bEL*cVxgEOdzpd6ekY(^%+7q$vR`>WpFr2 zGR3DILUroq$_#h0=h;MAIoRbr6opzqHAJg zDlsht1>tRUOw&AXQ%DA4Qp5z$B~j6+)sHfLE$Tj!kA-81omw)adL#O3y{89NCu0MO zJyFw2s3Bjv93^1Jc-ZTHv=APp8l{%hiA#|QkRKeLsQ^!40Bjxf97+{3kyrXp*iqVW zuQkJO*d{I3<3!TmtVConoX?rac0SUFH`y#LW*L_!NI`;biH|=BFH$Y!JiGom%%)nD zO@fMu1x2q@jLWpX2uSM`ocPJ#n8*as4>lpOhMuB2MH6PnMyN+`Ch-XWWqGr$$ zQTH!rd~8DQFT0l`J6xq35WgnKLbIotVUdVRx1_@vbZ1O@Ob8&wOGQo|_l9|ltVVE(3!zgrZJCiw+8YhuVDV_xq6@)WI!*r-4Uc0!96-JObT2C_R z<0ZImA6nr>ltahGEgZ9yj>*J2t8g`}x1g6J=$B@bz$}z6kW{GAm@2(!V-^qx=RKRJ zD#&+U$Apm7PO)euJvf8QU?ao5CU6Qa30+db-a&38Wjog zH5Q7^!#2~`C?zTJ@Hbb(IMqn@c#b}aproI#cxRP#GRwB`)n^p3eT|A40Jh~<9ZpuyK zpZKGWZwIZVJ z+k7V!Cxxp4Pcvd+lvSjYJPLQ+S}BjrFA&076`5JcBcmDwfeM%}Cb)CJ2)9`RNOGfb zl9SF^;5K^zBV=Sa;jl6V?5H+Q90C>3DWHQ2iQ1`wz!9z-3izO^L~X+s&LZ58p#&-P zjdsoq7vUQB^=SrlRFrzcAvvxpy1^n`r@xCVr4A)hY=*_QSu}8gu;~NLBUr-f z)ehanmEp05vU9A|0L>^>xy&UlP9l+Fs)}4b3vE$&*N8IBfMUp+*v4cz^+L`|woGTH zW>+g=EH|MDzD}qnwk&!;G5^WUdEga7on}7h<1&O))IX_&!6@8K*Y%Qt0)q)208@Dz zQ1zMD`nVd9NFdWTn?*?Y+H{A-_7u-$f=9Z2rq_biJ3U6e<#2NDMB{MT9yH?@`kY5+ z;vIkDe%{PkVY(B)#0&|n>%|l-!p1#afQl*kE)`vLJg2P(7Vs@Glhc; zgUsWMsG^SOyJhP^$=w-7e5?Mh#!AmHy0h<7J{dlQOO zy}R=`6D%j6rz0V_L!e`QWj4wjaauPDSum5WRl;No;Q}xYR=OUkD1My9zGBW&f?~f5 z%RPvkh7)SM&1$Me&|)qTxe}8#bCZB~^C+A}HId3RF2l6X-YG{g`i~oIQ10qPn1@xp zL9HzepCJ}EKUl=g$I>CPEv1)q*$vjf-L?gVTs|DJwHdb+K|PNR)KM%)*1K!{T$yl% z^AiVWme-gw{8X@g%_JtP8vjf!Uo(kpV#zLpVX=J37>M@8G;&!sH6VUvhKEr|#UgV; z+MFv?Q4J)!fbLbC;Z%5uZdvX|WooVkOLkgsTpB4BmSOD_Yn%yk)@dGf3YG{4Wb%RC z)B>1HNyl6&E}D$OBBXI)k;+RJgEaB47Vz$VfEo}Ud-^E%G9Oi30g^uTo3(J25zFxj z)vc8YD$=CzsN|dnt#}mE*6rk4VGUoQgw!vdV|-*=zKDA3<8-AqfzBcoaxk1!P5TtL z#|-nx$E;vR;R0hnh;S!W0#gxFwZ<`L0BX~hnq zfUeY~!E6*1s^Zcs)4@x&`lMS$I0MX{GpMBYh&3{7|L4aA}Ojit&7cHXuG>Br5 zbiT%6iK1#<26qVF=aZ#=f0lu4olX{K<>c|&fYK{^$?@BdN7z1BCrPAHF>!{8Y2jmX zLKUyfg&PT%AAI*TveQ9wSgsGY(zQC87iSb0sxEU@nR!JjO5t@{)GIltx`_)6#qyu2 zI>$ofxpi@w+{rsT(%}+vzLSs0D>RcIYbeOhhupVkpjM}h;W~-Gx4;H0w+t&?nHaqh zYfyeMFB^WK8lgn&BXBw0sEaaz5@cFrv%#C4LgJZJ#QAnPm&aq(A%Qi>dl6;EMs)Yj zMhLS_N~r8EZ!&>s0VU!)8?Hbg^Ike7I~HS^ z0Hq!ANh`jVz=ncl6K|nVuEa^E4PotVU@8@nm@K#<=}2nn5zfp;)v)hU_}bNh zvYHmgI*<5~Ag@4vuF7LXp6xjU$cY7%=3Sg}{C8wYuG2OHLP#Y%pG=0wsDS5KVc-=y z5S1EpaRq`(NSZoRh@N+hO^Fr-7i>8mVI4FK`IbcYo6~jAWp6qk?J4BjuSm}}+xCI$sZ!alG?Wr-MRYViT znebb_E`hY*gJLK_i-^k7N*4Cm@u&|=Oyc{lpyBmV{iW zZxW6Zzc!K&=9$kIX|3Npl4=&rhU?*Jd!yp&&=tKn0ei`qQ(R<)JPUG=DAL+oGj1kS zlpa>Nl3?Xsap9MF&Z2AwEc&KE8`V@+C0Lp|A-*tQlzrfdiLXdtW+5dEBe5U$ZVAFLN_7#x#Y`6R`BepP z!P;c%ai3=sB&ywMNXNJyCcM#*&YVGOacRE*E6Inf`5}iPpRv$`3VAZid#;u3Pp=fFZ3wxTFqcR>x+ zeSC-XyphQ>pPO$u7gdRfsr{mZFdT&ycxMOKte@XCa&oQ^DiZhVO6ZpgK0cUyfLz4A?Jf>wq2=+ z38tZT3%1%@h7#D>rFg#p zq!ZCMW=t$Lwj!?yXBMV&Bk+4mD{@9H;r#<|N6(C6vNoL*7Sin~@wyFjkdt0Lj~WtJ zIp6|ghaT+@@0fIay7{h3YS5f(G`%7(z~#36DV>fcn6t=~T;DEzov@bb zBz2X=-@yYo@8N_ywHhw4^dN)n)q74N_v~@&9MqB^5wZ0AERO^~CIti(bAu=e?uBkd z9rt;0Wp<^(`Q!#0B}+Hh$ktSZc*BaTMkU#{Z3bp$vh1$6aUNiTvexC)wdEK`_9 zRZ5de;CjqZwwSr#e)t{E`rKbWj;L{IcRIQ=cH1Px?dh1;uu*B)b4LofWDe>Xi;$w-G@X2qO|^Sp z6D{q^cv7-GB^T~KDBLHNr6VFm3C80h`+0U$Rm4}a10TaIGSxzbq&ubwcGR`Cg!}ST zVG@Q&6mYFyVv!`F_4P^Tuy6;9cuHS_#9mqM{lJ#F)l^5AMRdaRye2xZI!q?Y%T5H0 z$$(gU;0obJBUXpyKF?nZgfOQROL1!OjCYL{{3kuSf=m8gH2FO!vkM zU_Eh?H&nTiFvL(Q+{)JLedNb1w1Bu+Vcxx;YBXzZN+w+%@^qu27_LFdkp$+U2ZS(@ z$paE`z+L1m-h9x;T@%19&Sdu-IvjT|o+aOxjT4Uh_!QmwB@Zoht48Wwo8N@%unA<0 zl(@~y%8_LkSlY31CmeQ;r9unUPS#JOM-}~5M&h+Nf`BX+n5hnPFmFTlLzKe zL9wiavGQ084|~#5;7MZ#ChP=xvGl01YBQ>92L;N~iIf#ppik4`YE?n>-d zdu4sO;bl16b~;Nc3W}GScU6vI+b3uz^Cl zF-@>13tamh*r1~L`)4Oht6e`bE+p}No<9==lNbtmK7u~J8rw4UpJ6XmBfXdlF7%r# z=X$WgwMKJa=>-Kb=D2l>V8A@~MKK!Ta~@Rfu>nr{HcfDv?Sgk5jd4nzQ{aIb)Wych zs7=&a#?PveUa9&t43t<&A|+YAjqqr)gmf|+e7JDC{Nio6kC{vr#7$W&c0iJW4DU-u zV>9AMO?pm(MVMj4+UeMrj!lg2nK^-X$fs$9a(ll?A%%7t^Y-Fgh$4=N4STKx z-AGWrZugtUL>z1JQe&l1%Xbdq)qfhe0}u3A;GG4fNnm)bgHo;Uz{4mp4LLK-f}gF5 zM+j3c?oO&LReDSj|Co={-)Ogn54}b=kB?NtisWUOziGmvg_rM0XffZb_>TETpI^u$ zrWv&2a@QMRhx4;?O3l2r-KZp6tDMbV7)t(PFkk;0>g<|0vQXHFGyImZ>qmGm|64jD z`@Y9;x5(g~RSX4eMGg zVpB1!GOy31!>Y7JLRelQC!}@@V22$+<@9AhjsB$A3SQ?Hmj|gG4m>(5x1uW#xZprc?4cI)74n2jdVe> zzMxyj^XKs)>^72qASfs_Y2VP0PPggay+D>NJbhb ziaW5$Puk`~2+%28w?hf)6v(A*rV_}oVBH40ZN;yxLJUbpuk?UJh+$I#&I6fDX3ZlN zEoQ&%&@l{g@6SMON0*MVSa_+B$NfKbVf&pD(S5yee)0&R*xz_2gVth!ubmbSJIpF| za$wFkTj33=O4{HCk*eKI`-e{>ThrWIG6fgktYUonyb9(TTcX%>A(h%8pKXaI$fX@; zz3F@>hdTj(WLx56(sV1B%#$+P?hRxi7>W{C2PJR052Et7ZYgcg+2o~E|7a(;jmgS> zLV9+d(3g$a7&%KQA^FP!Pm?w)Jt(81v4QG<8ErQT2T_H0G+3cIIg zfktl3rbC0}TQlkIaq}IdFb$Sq7Oo($L^e|L8;`;D*wYbK1_u2dDOA9HO`h-)i@l3& zSD^C+My1C(VG(&qp!+rNel}@_2{Dq6pgTxDS-APP*hn3Wp&~eTa)ha~=%}(pdQD-- zK*6RLwYT2~P><-%7hlc@OvM?FmjwyFf-1GX7uF(v$?YvrC6iz@LoZH4AbM#om_#$>4pkyw~7slbIJvRn%;oo`H@03t499$ zF6bs#b+kPRKGMhAUJ&YjhK;D-t6_I`j*x@WFWPx=DmECz+|SR)hw-wg1q9_bLtn zL2WxZ*x`BE3DfBg+4E>VtRkG-+jlw!5zRl}Np<|ZQYfK2#-w)h#7nMD?CAVuIW1kDLvv{xsrel0uJl!-`z5!ECf!Te320BuM z+Sk({BpVWOq8}hwWoQaDcx<^)PcO7Dcm}HA~7mUGg5N_#-cD*r+hV%2LheCp$#_Qwq;K`fdPjsa$DV5lBS*i4^fo~1< zs5->UypM2L?{LS?!Pev&?0}2<4Vli{YLNKY5WkYSxnKMdAxopVpu}W`Fy>eHLK~ck z`Soj*B8+IQLbB5VXG>ALZbdeO^hDf71fG29#Lngc(QSqva`_T>zzV-1-~0V0Ba6&H zN}neDM72u~cR>~LrLoCMX1lw#;~7M?`jwJk#Q%FacWGw%m_0`tOJ?3 zcY?*rA8F4mtU#08nvEoQU*^4@95V)+ew461S8+I1<=dU)c}vc0a+~wyQ$j12>@IWH zHNaIEE26}In(1N;zUTnuHKq*Mo^0xAt{2wX);JB-%p&6J_8KW^aSOOm)E)D_Nn?Ll zY*bui0fGFv&~yNLu~|mu4DAR`adW}`outt~HF&?{$wn{Rj|e?#5h@UuB+6b(L(g_q zyXR)Yn@MzXtu3jrgh*4CX+@RHhbr`#dtMm=>n3@b_;D>~l;)X*ioh01^RtymyvAhl zra8%8mZ5%fn=rDG%b>!Z{aML^gdTR(LxgH|2ZONt`0q38mh4_twcgmO#|I&3B+3&X*(B+z@AZYcn)TrZjvqf9>eez zci)PtLEv4FN`cY6*Nfds6~s2*y)yuH7Tm;@P(hS#&X8|GUz_LiEU-Z%Rd0T)Ou9Y?j~d8Zr6TcSX`~5v0!bw4qh!1q@V-oQsH6m;O2*f~brlhWx4AU^Z{_ z)M>(*crWc)ZO<^%fxWJ!bxA3xjb8%!og!r^C;sy4$sxHWlOz3#gmaHZ{?9{0-xP9lCs;CSZLs#@bP z_Fc%HlSGI+^V7=8DK>yDV>ern%VDAD59b&zLM<&wDkSkEKOC|LwWzBW#Vn_VvF!fL zJ!ZfjEW{&S6|k)-jdnHp&p*w)s;0CD)?x5)Up61sku;O^Il(4=s#09cf-6}O)TJ*) zVJ6v@R_|)b z(-um_y-(L9o0iD@M#H&6G2uC3gY$t@?00V*KlyuD5^#%GwgfB~kx!KP4KBJ#J$VGq zz^4c2Q-jjenV5E(G)>H$iiP~wr_uvqa$6qN=vgrXb-PX@3eoSjCM9$^>h7C?>SG5A zO)nU+iy|Srf3)Ev#)YLu+}?rJxnZZBg~yXw%X9X8sN#FD)2Al|op-US^h&)QxOXiW zR{G-N+YTWY>!M{VUFcAIbIR(|Xe}fj55h{iA_k_e7cnQilHRjKc#o>|f162Fe=t|P z+6J#;leW|-pi)yqd&w`^Y_CcI10TV`3 z;gy^o>?CTIRqxys)UTyur^8OVrT64v=x19-75Bvpz)a3Flum}P>GnbKJ3CZhWRz^> zf!BzZ>isb<5GEHjc{Y3C4r3SY1Jj(Zzarl z;RjyG#@xMJd}BH@iwcw7ZH3n&Awr^ou#)FY$KS*)lNI=nkHj~vNro1aSGXUlWsrh+ z@*&v;J?5TgW}c^Fq$SVP!_<){%snsQgR{kX9f3Ua=lRl%fN%FeAKDS+SJq+&T&*nnPs9BLxR1&G zp+UHxbZ1E4q++MqJ)~|CQ)1Nn;=_t~HAyxmd#78eI`^mna;bW$!366doP}if@5S@KJW~@C?1|yFb$StE*kZ}lZ$&phvVegOhXzAdb%Y)Do;xEDCll~>PTeQnkQ5EbIDVo9Ks!|xVI-j-gc3hnFhPC0qofD99mFJS`A>s_Ik2ssrM2J|6y5&EP9>7u=#;* zS`A4LvCR2Y$gN~C{l-D;^;0~0j$kxznMaLE#vGxa>~AW*8Gc4urI@qx40khgFdLEw zy1eHCxw!x6YC%MjEsOTr08;MN%t8gtA|mv9KbzgVHcLY#f47J zf_q37WAj_VG6dPjGqAmuypYj#BH%>iElxX2NJHRtRjY3nc0hNl;a>7~X7RZwZ=!f9 zi*L@sy*bgLvL4Ex-p%)DGBy-D;6togOS7iIhx}sPUL>xyVZs|Fw;Sqi!jdKBrmEci z0~j1D%KkjeSIPqe(($ywbnKQxO(HG|8K~w}xvNI;-<{R#^8ZoWcgHo6wfoON0wIJU z1QHT3fdmK`AwnQD1rmDip(#q}p(T`1RR~3jqKL>M3er?iRB+W*S9d|NH+0oiY=8}{ zU@z$26LjC}-rv2S-yiqA`9Lyr>YQ_)nRCwbe4p?1OMujrlbBe5b^(qI(i{sJ0K7>| zs2k-IeGU?aXcz$hA23jo1mMy<4EjQxz#!v+saGHZO8R;tm|X-Eb=U<=W7$X%PE7TM^@uSx>R;+=_T8+r3AVJxGUhkiB7$h_TM8{khR? zS5b6t1pkimB4eudOICnhy$wh zWW+vCEr713^PUH}u_q{0GG`2;`*dZXY}Y z@kz&`233(ofEkQdL?PmJ76IHVy^B2%0`?!|1R~Pw>sJJmLEryL!T?hd@`wRz%Kc>l zE26Lk%ss)*XcC=|Ttvu#Y%fjUGl5?MWuqx{7FYuhU||cGFC&A^w@x())`0slZs`&N z3$KCC1WuiFcb$7`grCgNMA#j+X;U6KRuV$X{p-^WdGdZO$-$&;{8?0akjnI=vYvfKC^{3;@>*L{==4?SVDq2CA%_ zY?7iGXmS{MrjnX;VLWU#I7Qeg1knJoFLWCL?ckB{W-tQpaO*|sJ0lm|R93;RK#MJu z5(N4%6LzHqxPVlf@UuLC$RZ=KI9ePFYr|W>9TP-DUIap7v?d&rF0(t1HUU~X6EKhf zhXVY2`PpJ?lBQh8?WmJZq3;9z4#4xvB%@NK=On};)=`Ii^qI}YjY6g_Upk%;j@BzS2Ud&|U;0?epBuZ8#GDot<>*kXy1jdqMELs%%6s8pr`0cHYp zYjUcH^|K0p7vg{gmX{Aj)^r*&$DTtsLQ2f7i`DVKmtB-Ty%EloFX;05Jt{xJzk(fy zH|25wtO10j*hP&2p5wqr2w7#zsE^{SKE$G8vp%}h_ad1jwxJSo$_dJ2r0au674S=7 z%m9lBAut@3B$R`~B>=#iAVds!w6ALLLFB6%f z>~ytz3_##w!3jDBsMyxIA`>MR)%5q^Hh30;ikBjeJhL_MJflMPJ1o47=2%SV!NW57 z8b%B>&G11jpM*T2G=tHsP8R_~-|Zq`m1iis1H1|_;sEyqnpzfY40tGjRaBfO)>sLY zb$*B+8%pL%fG_^b4XJAbM5+yUaN01T@e#UQz;_1q{+siFFMD z=s1xI2ZkjX=rMpRbuMd<2T;X&08bFc3ktjeY-eeAi`ce`a?JfoR|{=8Q*Nk380&z| z7w<0;$7We)Lcm8eHjVXBm!D5KZ_6IDh2Ic-)cgR?DcHOSQ;8C|2XWHpm?$7FB*dfu za@EPn1c)-wXHsJ!QIozCsj)*e)i40#0mOr73T3Nl&^N*nfO`ak0%ou>OH?`)nYKpw zW-#>(6#yDem%tZ*);CAUAwuo9wL*jdFqh#BI445KsMgv5Y=P&;r${pf!@yW9cEX7y zA~D>E;wK?WR)Cu%0&sPR+K&?FAyx~(+9z9|Fr`9lM~TGfMZm&?XgNY+iaEgSW)L7l zqAS3N0p^W3QLN)!0H)0n7f`hj1?(Jv!-`8t17M-VbfPo7QZ@?QtC20~?3EB$9Rg`2 ztdAwmhtJOex__~LusDUf6u4lz2$Q629z?$iX6kflfukdY&kzIVR5lBOr*x|Q9Bcp+ zT#j%Ru~;P(RggdpMQZ{R^`BM%m}C!$2+i6=EwnL46rV*Ef=N*(FexG+h`cq_V+MquYnt45#6}g#}X3QSfe*w-oX422SY0LhB|#34+*KQd8&+V11v$ zrYQh%Boz>^z`+31WrzVV23R?B1w06o!AdD19$C18NI|<|#G*oKBi$6-eP}#lXG@$5 z4YyU_n6U+XE;1ahL?iZUEqaoXgoUuCIq>aE7iNEF!3Cs)rS8Pl?*AkmMC7^@;0FDjVTp|A1Gr5tSY0Q4 z4*{$yGJ`iI8sFInRf{1^38cg;Q6P^3hlCOZ(m;ul?I?_|DnT)MGHk@C0{L`_lJ$TL z$sbkJ$H*}5yi!8bD9XfBh7B7iB^?|^n^(zjiM%qM8>33Lr)5a)KpE}Bs4@>zieJeq zXOPD*o}Q(I#({FC?U+hnRjJlCUIi;+3>$H}lyqRAf}KC68iOe#pX9CNG>zesJVY`%d~vlW*4=Z|Z)VNR&IT3~}4j%jV@ zxv@g~vHE=-#NGL5(^&ed$l(ssfrDsvR{N^xsSfSmKA_D@>8pkKPVz}xB~E?&YG2bb z$_2?u%Qg))k#CvKwZ4Xl~(aeNCC#x zRzsuOx12%jKka5-uTh&;Zor%Hq%M4=QCIG2Xh8S!^35VNbh#SM6MFe9epRnHV8~Rf zQ_)X!Uf+DfRncY=^3Up6-!j!@?D$b7u$0lzzPbce{{fAy3fAiE8#Q6`yn|!g8n&7z zQ2a8_hO~`WcY7wVLVIcixe{V;H)CGZO)UQ472=*=Lue`N6EVbTgzL7NCfcb+UFm34 z?5!|cmb_}wU4@?x0`_~gx^s&sUX>h%jhR4h+`M$AQuVp99w^^(yz{N8RHsY&dI0kLL37#<S94S@bjMh3 zo;)uX!b^+CEiLK>aXd{Ozp6y5;;y-VYQAxb0p$$VcK!vm;Lc_+5UgF97gBSfO*c*} z&HK6CFFh?=_x1yKn_tIjS%t65c71qc!-M=K<;QErP%_&S78f(?*3WrpT50zByD zsE)sQnf88S%wft7(i=C}^6^7E{HAAZw31K{ciUL+!q<%c6qNc0(DRe)%4n!|^63~Qjr!s8WFB32 z8AB`R5Wm8-p)6=I#-(eRztUDu_NS^m8!8EtV-Cxz*6Qi- zw(y%KTnh)Gn&2)5f=_g#dZJd5rr}oGZEkF=%wUO?!r6f;ltoRsEZ(Yf>F@-~R5GYq z7u+eYxVi6gZKJGn&gB6m`;j1%jx0sm+tVIh!-H!}v<&HL)t+v_73UcZCGu9X1zqB@m1Q*GjY}?BJYiX-y4Y*J$+6(Zefx|M{5Hie zA9%$Fhcr)p*zQJ`l(P>%*s2k@61DE&`JV>LOUwWx!euUysQ z?yB}ne^tKeE^52&I%nmqX3dtqjvB$$$=QK*j^Dn3q8wmwc75x+q3w=GPeCg_g(!@U zRVqQOz*3#7`fuuuoFBS4RdBAhXt@zCuH-_8xJ@DA-9NA-9o>Kb)O@-9xRFvUB znBm^7W{T;n78&#SzZ@}Y_0sPq#D5|kBn_2H4vnHcMSTm1^;_FIGABdSZLgHMP{pv|MEUP~Cw+DD%x>?s%V-$4BgV@BrGP8mDuMe9Hb&|5}-8JVTdq z#yfHE`gYR<)*{Nes7HH$8Z=GhY@%Fv_6L0G>UrM!zMB^$jg~FXahQcqH(pwyw6ATN zpX_1~b$NBJl}D4SlBZAel^i8qebZWzU-rl!Z6t1wG$tif)$3aC$!$9=)KVk=7`cA% zI(N_VTB8_UpBo5XufMi7Eur(0{i9VZ*_IBEAH({4)R0mJyv znO&t>13jA`vMUdrf5^|?RHXjcUR!T(!ESh@|J-Aq_Ti%sTyi=Mq9;j0dyUPAv`1I= zB<$4T>xW)w?xYA!U;F^fGZ15z3C1=x-jA;w5;!n!tPTgN-^_`G~24* z+4mh)mt3+id(s+s-Kxc4<`avybeKsS)qs;1^1D2_m(eBIZh5^FH@JDT}peiU3o5L zAmZ~P)xSQp^?nHKDK4Is&-=gsr54N7HrG&sn$|+Oyb{G3UU8I`f&yt+L#g2w#>A!6 zlRQyUZ!61ZYA{iB3EI@NO;tZf#n;4KiPJQwz#SY=Dm^Px?}AYV$%-n4;EA?zRLvxU zPm45H)*gzPc9*r|>YdB%Z^W?5hX#?tso9%)b=h|HQuFf58e^CZZpR4MkCzwN(Xk=j z&R;*oRhXu=t9nfxTRJdovc8J08sA+cHDRjvcm#5zow; z(B2NG-h&u-?n>iphZ!W*CO3~deL~c2hW4+?`t$ue2up{wR0qiJ7Fj8)Ik9nT-8=niuQ)cD;)0reGknt zH~QcvIiBH~SJ(wN&)tJy+uvr4%*$@lgvk1)6IPEb)@}4n*p{&IWR$Aqrpfca7>;gK z`=DsmDbf@9uW!D#?-92@vI&U~*_3+Yk@CTte&Q9coknrnZBN-LWGzubKiphy7e1iS zwfVIrhnHl3?T~*iyR+rRGUJ8!=LQ7a*5|%{7WeB!(&qFw>!S?!LE4s+J-k} zEU8+*vsmHH9GE!4Q;8bhN49cQc1w09>YS#1&B z)j{N5cxlQyf{oVd++82yZrQM~Hlv}F`nJ2?v2cxs%&Ln)o$GZk4stwwO5JGdTCV_m zOvAR&ttL@hJ#2?%uDuOg^^)fX%(=gXR57r1KC$MOJ5NFJ%Q(t>m#gooA%%^HW42jU z1lGptmr<@>-DXW7ijpNEx}#RxgL>-x^Uj7uKhby5o$Lt^Ooz06Rl*E>Ob)C#+tp6k z-|b#|F>*~!Xo67;?DeHA7`;DqoArL0N!E_`nd#6IrV2ZPq*`5F_6mf$>>Z&-o`PNW z)!vJVJ0qX34qMtM7?xafkAC|kd=SeDs%7=Y3iNtTdSBlgb)t9o8`YB zFWShHFy6%Ut~KSUj^l?hhb~G{hHa_Q#ljx1@-w^H-ILJ{)2t(p1jNES^YG=z|IB`K zZI9cRc`=H&2!lE?edSc4P^~Cp2*^=cUqEb7XtyAX*H85vQYM{?AXr`RU$Ss8j;OPvy_mXBi9JE`)Q(GsY`E}+&J0D+50v&G0=R`$lO@ALT0V z7&g0GUgprGL$_;-yUcIWSbiQZ_()Np@srQ~jr{OJcUkzD2dmB*n`^PQ# z2p`m4U08)7Y+DMR%i|l%i|(o3BldxnUDbTQIDN&Oi@e`nZ-tjTXl(d2k{A8P@Wk?f zcEyOJ?w-Z_ScDa?ch(4IfNWfzKabb2bK&!!e*f}z&(gnCM`4{liu$~a2C=kbLZyNmHDn(hU?K&{OHt>Bdn`fW~N~qd)hTOV_oe+g|ne6Q6BDDhV1q6M%u|| zn8K{}Ei>^Zy4|Rt##aqJs}(xPMrgr-R~z~_Czy#ho{LVDsn%W5pI=B5_h z=I5Z6X2R>HGriTEO&`xcj#6sA{8J)#&rQvygzQasW)f{rMPAH4`Qq@z;-m%lD3=PZ zzi$0_JIU$ksG#qYWm~jnGGD!UxZ)%>9k)E$)i~<%ScFxl?(+!`7yBztb^API<|1!Z zf6~(BOLqv4 zrszYJ*LLN;-6=HNj|@d!KUejzR~AT!`g7#^u_N5w*}I-be}1y($O|cF>x0yIbglaD zE6<211EDdujBgxk9O2MEi_6ozc?OaBY&e>hXmji464$|>PoUCC zuAev4*YOW^9rZuwUqngi$lbqdd3svX0<97M3kMF}@t9NBaqI3^>w|F>0S##rHoOVb zZO1QIX0F|I`=RywLzi1JqrcT%Xcv^iJs?Q!UUu9x1y>k~{w+C+i#rkng^w7SX7eWk)+izyI{-AT76lZOgL+o6k2dx90BO)ABq$@AIuOTHc{cEia_Me7-Z? zns@YN%gg2OKi`A(@(0yhrz&h_#x%C%pP1h|U7I&^pRQMMCZP4zhF@kLT5Kt}AZ~r# z@_yzqU$5};+SWH)ZN5wfZ7ICAr}gcwyf06MdPO%cwZ7Z`%a`YwTZ-!%a!!LS!WXssr{zA{HXzj1<1V z56%ekm9q)YhWzkau>U4l{`#RGgMVZO|1tb~1HK1=x6M)JP!|8M-$cj!_q@N1`R{(O z(!Y-^{r_AHNDUQAU^tiMIZvX9uB7abtFaL1) zE_^;e2>i=GK776v-#Llz&gUobU6N+QoSpv>pC=D=`&Yb25QvtagU@U!v+-VhejXo0 zfZy3dz%QTgD*u{I>Rcc{@(Tjl+}wQp^27!HDPA1^&{QA4&{WF+*CZ>K6oF4zSg?n0 zXp)bAs9(NIir4pFkUlih$1i9$Pd*0x_eGkkT~DZ)r;ej0@9{L z7Wjn<3gzX+%HtyB>HnGEKS+`i>)~D)C%=x+pzrkvKwg14sMo@u|1JE7^Y7&g^1*fF z`zMJlKst_Jikk&!i-k*uzju; zD1rR)q4)65jSuIVCx8^e^7NL4;L8?7xVg^?jq~5~B+o1VWP^X_|I%jXJdkOA=6|Wr zb2j~K)w4&l^6L4m?;|+ej5xsrZgeP pEKVXZ69PM6o<1O=&K3dCtsUE(ZBCJs<^T8x?6!bT9=z)IKLE!`dGP=M literal 0 HcmV?d00001 diff --git a/datasetup/usdata/state_fips_abbr.parquet b/datasetup/usdata/state_fips_abbr.parquet new file mode 100644 index 0000000000000000000000000000000000000000..f53ffa4c3f130833522d1bf92c9364f21a87729d GIT binary patch literal 1935 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 L!7wT1CItQuTAx&W literal 0 HcmV?d00001 diff --git a/flepimop/R_packages/flepicommon/DESCRIPTION b/flepimop/R_packages/flepicommon/DESCRIPTION index e7c0a9daf..341ef760b 100644 --- a/flepimop/R_packages/flepicommon/DESCRIPTION +++ b/flepimop/R_packages/flepicommon/DESCRIPTION @@ -17,7 +17,6 @@ Imports: stringr, data.table, rlang, - cdlTools, ggraph, doParallel, foreach, diff --git a/flepimop/R_packages/flepicommon/NAMESPACE b/flepimop/R_packages/flepicommon/NAMESPACE index c6ded5703..c8f936299 100644 --- a/flepimop/R_packages/flepicommon/NAMESPACE +++ b/flepimop/R_packages/flepicommon/NAMESPACE @@ -27,7 +27,6 @@ export(load_geodata_file) export(prettyprint_optlist) export(read_file_of_type) export(run_id) -import(cdlTools) import(covidcast) import(doParallel) import(dplyr) diff --git a/flepimop/R_packages/flepicommon/R/DataUtils.R b/flepimop/R_packages/flepicommon/R/DataUtils.R index 267ffc65b..95b54af06 100755 --- a/flepimop/R_packages/flepicommon/R/DataUtils.R +++ b/flepimop/R_packages/flepicommon/R/DataUtils.R @@ -37,7 +37,7 @@ load_geodata_file <- function(filename, } if(state_name) { - geodata <- tigris::fips_codes %>% + geodata <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") %>% dplyr::distinct(state, state_name) %>% dplyr::rename(USPS = state) %>% dplyr::rename(state = state_name) %>% @@ -88,68 +88,14 @@ read_file_of_type <- function(extension,...){ read_file_of_type(extension)(filename) }) } - if(extension == 'shp'){ - return(sf::st_read) - } + # if(extension == 'shp'){ + # return(sf::st_read) + # } stop(paste("read_file_of_type cannot read files of type",extension)) } - -##' -##' Download USAFacts data -##' -##' Downloads the USAFacts case and death count data -##' -##' @param filename where case data will be stored -##' @param url URL to CSV on USAFacts website -##' @param value_col_name Confirmed or Deaths -##' @param incl_unassigned Includes data unassigned to counties (default is FALSE) -##' @return data frame -##' @importFrom magrittr %>% -##' @import cdlTools -##' -download_USAFacts_data <- function(filename, url, value_col_name, incl_unassigned = FALSE){ - - dir.create(dirname(filename), showWarnings = FALSE, recursive = TRUE) - message(paste("Downloading", url, "to", filename)) - download.file(url, filename, "auto") - - usafacts_data <- readr::read_csv(filename) - names(usafacts_data) <- stringr::str_to_lower(names(usafacts_data)) - usafacts_data <- dplyr::select(usafacts_data, -statefips,-`county name`) %>% # drop statefips columns - dplyr::rename(FIPS=countyfips, source=state) - if (!incl_unassigned){ - usafacts_data <- dplyr::filter(usafacts_data, FIPS!=0 & FIPS!=1) # Remove "Statewide Unallocated" cases - } else{ - cw <- data.frame(source = sort(unique(usafacts_data$source)), - FIPS = cdlTools::fips(sort(unique(usafacts_data$source))) - ) %>% - dplyr::mutate(FIPS = as.numeric(paste0(FIPS, "000"))) %>% - dplyr::distinct(source, FIPS) - assigned <- dplyr::filter(usafacts_data, FIPS!=0 & FIPS!=1) - unassigned <- dplyr::filter(usafacts_data, FIPS==0 | FIPS==1) %>% - dplyr::select(-FIPS) %>% - dplyr::left_join(cw, by = c("source")) - usafacts_data <- dplyr::bind_rows(assigned, unassigned) - } - col_names <- names(usafacts_data) - date_cols <- col_names[grepl("^\\d+[-/]\\d+[-/]\\d+$", col_names)] - date_func <- ifelse(any(grepl("^\\d\\d\\d\\d",col_names)),lubridate::ymd, lubridate::mdy) - usafacts_data <- tidyr::pivot_longer(usafacts_data, tidyselect::all_of(date_cols), names_to="Update", values_to=value_col_name) - usafacts_data <- dplyr::mutate(usafacts_data, Update=date_func(Update), FIPS=sprintf("%05d", FIPS)) - - validation_date <- Sys.getenv("VALIDATION_DATE") - if ( validation_date != '' ) { - print(paste("(DataUtils.R) Limiting USAFacts data to:", validation_date, sep=" ")) - usafacts_data <- dplyr::filter(usafacts_data, Update < validation_date ) - } - - return(usafacts_data) -} - - ##' ##' Pulls island area data from NY Times ##' @@ -399,64 +345,6 @@ aggregate_counties_to_state <- function(df, state_fips){ } -##' -##' Pull case and death count data from USAFacts -##' -##' Pulls the USAFacts cumulative case count and death data. Calculates incident counts. -##' USAFacts does not include data for all the territories (aka island areas). These data are pulled from NYTimes. -##' -##' Returned data preview: -##' tibble [352,466 × 7] (S3: grouped_df/tbl_df/tbl/data.frame) -##' $ FIPS : chr [1:352466] "00001" "00001" "00001" "00001" ... -##' $ source : chr [1:352466] "NY" "NY" "NY" "NY" ... -##' $ Update : Date[1:352466], format: "2020-01-22" "2020-01-23" ... -##' $ Confirmed : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' $ Deaths : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidI : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidDeath : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' -##' @param case_data_filename Filename where case data are stored -##' @param death_data_filename Filename where death data are stored -##' @param incl_unassigned Includes data unassigned to counties (default is FALSE) -##' @return the case and deaths data frame -##' -##' -##' @export -##' -get_USAFacts_data <- function(case_data_filename = "data/case_data/USAFacts_case_data.csv", - death_data_filename = "data/case_data/USAFacts_death_data.csv", - incl_unassigned = FALSE){ - - USAFACTS_CASE_DATA_URL <- "https://usafactsstatic.blob.core.windows.net/public/data/covid-19/covid_confirmed_usafacts.csv" - USAFACTS_DEATH_DATA_URL <- "https://usafactsstatic.blob.core.windows.net/public/data/covid-19/covid_deaths_usafacts.csv" - usafacts_case <- download_USAFacts_data(case_data_filename, USAFACTS_CASE_DATA_URL, "Confirmed", incl_unassigned) - usafacts_death <- download_USAFacts_data(death_data_filename, USAFACTS_DEATH_DATA_URL, "Deaths", incl_unassigned) - - usafacts_data <- dplyr::full_join(usafacts_case, usafacts_death) - usafacts_data <- dplyr::select(usafacts_data, Update, source, FIPS, Confirmed, Deaths) - usafacts_data <- rbind(usafacts_data, get_islandareas_data()) # Append island areas - usafacts_data <- dplyr::arrange(usafacts_data, source, FIPS, Update) - - # Create columns incidI and incidDeath - usafacts_data <- dplyr::group_modify( - dplyr::group_by( - usafacts_data, - FIPS - ), - function(.x,.y){ - .x$incidI = c(.x$Confirmed[1],diff(.x$Confirmed)) - .x$incidDeath = c(.x$Deaths[1],diff(.x$Deaths,)) - return(.x) - } - ) - - # Fix incidence counts that go negative and NA values or missing dates - usafacts_data <- fix_negative_counts(usafacts_data, "Confirmed", "incidI") - usafacts_data <- fix_negative_counts(usafacts_data, "Deaths", "incidDeath") - - return(usafacts_data) -} - ##' @@ -718,161 +606,6 @@ get_CSSE_global_data <- function(case_data_filename = "data/case_data/jhucsse_ca -##' -##' Download Reich Lab data -##' -##' Downloads the Reich Lab's US case and death count data -##' -##' @param filename where case data will be stored -##' @param value_col_name -##' @param url URL to CSV on Reich Lab website -##' -##' @return data frame -##' -##' @importFrom magrittr %>% -##' @export -##' -download_reichlab_data <- function(filename, url, value_col_name){ - - dir.create(dirname(filename), showWarnings = FALSE, recursive = TRUE) - message(paste("Downloading", url, "to", filename)) - download.file(url, filename, "auto") - - reichlab_data <- readr::read_csv(filename, col_types = list("location" = readr::col_character())) - reichlab_data <- tibble::as_tibble(reichlab_data) - reichlab_data <- dplyr::mutate(reichlab_data, Update = as.Date(date), - source = cdlTools::fips(stringr::str_sub(location, 1, 2), to = "Abbreviation"), - scale = ifelse(nchar(location)==2, "state", "county")) - reichlab_data <- dplyr::filter(reichlab_data, location != "US") - reichlab_data <- dplyr::rename(reichlab_data, !!value_col_name := value, - FIPS = location) - reichlab_data <- dplyr::mutate(reichlab_data, FIPS = ifelse(stringr::str_length(FIPS)<=2, paste0(FIPS, "000"), stringr::str_pad(FIPS, 5, pad = "0"))) - reichlab_data <- dplyr::select(reichlab_data, FIPS, source, scale, Update, !!value_col_name) - - validation_date <- Sys.getenv("VALIDATION_DATE") - if ( validation_date != '' ) { - print(paste("(DataUtils.R) Limiting Reich Lab data to:", validation_date, sep=" ")) - reichlab_data <- dplyr::filter(reichlab_data, Update < validation_date) - } - - return(reichlab_data) -} - - - - -##' -##' Pull state-level case and death count data from Reich Lab -##' -##' Pulls the Reich Lab incident and cumulative case count and death data. -##' -##' Returned data preview: -##' tibble -##' $ Update : Date "2020-01-22" "2020-01-23" ... -##' $ Confirmed : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ Deaths : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidI : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidDeath : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ FIPS : chr "01000" "01000" "01000" ... -##' $ source : chr "NY" "NY" "NY" "NY" ... -##' -##' @param cum_case_filename -##' @param cum_death_filename -##' @param inc_case_filename -##' @param inc_death_filename -##' -##' @return the case and deaths data frame -##' -##' -##' @export -##' -get_reichlab_st_data <- function(cum_case_filename = "data/case_data/rlab_cum_case_data.csv", - cum_death_filename = "data/case_data/rlab_cum_death_data.csv", - inc_case_filename = "data/case_data/rlab_inc_case_data.csv", - inc_death_filename = "data/case_data/rlab_inc_death_data.csv"){ - - REICHLAB_CUM_CASE_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Cumulative%20Cases.csv" - REICHLAB_CUM_DEATH_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Cumulative%20Deaths.csv" - REICHLAB_INC_CASE_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Incident%20Cases.csv" - REICHLAB_INC_DEATH_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Incident%20Deaths.csv" - - rlab_cum_case <- download_reichlab_data(cum_case_filename, REICHLAB_CUM_CASE_DATA_URL, "Confirmed") - rlab_cum_death <- download_reichlab_data(cum_death_filename, REICHLAB_CUM_DEATH_DATA_URL, "Deaths") - rlab_inc_case <- download_reichlab_data(inc_case_filename, REICHLAB_INC_CASE_DATA_URL, "incidI") - rlab_inc_death <- download_reichlab_data(inc_death_filename, REICHLAB_INC_DEATH_DATA_URL, "incidDeath") - - rlab_st_data <- dplyr::full_join(rlab_cum_case, rlab_cum_death) - rlab_st_data <- dplyr::full_join(rlab_st_data, rlab_inc_case) - rlab_st_data <- dplyr::full_join(rlab_st_data, rlab_inc_death) - rlab_st_data <- dplyr::filter(rlab_st_data, scale == "state") - rlab_st_data <- dplyr::select(rlab_st_data, Update, Confirmed, Deaths, incidI, incidDeath, FIPS, source) - rlab_st_data <- dplyr::mutate(rlab_st_data, incidDeath = ifelse(is.na(incidDeath), 0, incidDeath), - incidI = ifelse(is.na(incidI), 0, incidI), - Confirmed = ifelse(is.na(Confirmed) & Update < "2020-02-01", 0, Confirmed), - Deaths = ifelse(is.na(Deaths) & Update < "2020-02-01", 0, Deaths)) - rlab_st_data <- dplyr::arrange(rlab_st_data, source, FIPS, Update) - - return(rlab_st_data) - -} - - - -##' -##' Pull county-level case and death count data from Reich Lab -##' -##' Pulls the Reich Lab incident and cumulative case count and death data. -##' -##' Returned data preview: -##' tibble -##' $ Update : Date "2020-01-22" "2020-01-23" ... -##' $ Confirmed : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ Deaths : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidI : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidDeath : num 0 0 0 0 0 0 0 0 0 0 ... -##' $ FIPS : chr "01001" "01001" "01001" ... -##' $ source : chr "NY" "NY" "NY" "NY" ... -##' -##' @param cum_case_filename -##' @param cum_death_filename -##' @param inc_case_filename -##' @param inc_death_filename -##' -##' @return the case and deaths data frame -##' -##' -##' @export -##' -get_reichlab_cty_data <- function(cum_case_filename = "data/case_data/rlab_cum_case_data.csv", - cum_death_filename = "data/case_data/rlab_cum_death_data.csv", - inc_case_filename = "data/case_data/rlab_inc_case_data.csv", - inc_death_filename = "data/case_data/rlab_inc_death_data.csv"){ - - REICHLAB_CUM_CASE_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Cumulative%20Cases.csv" - REICHLAB_CUM_DEATH_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Cumulative%20Deaths.csv" - REICHLAB_INC_CASE_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Incident%20Cases.csv" - REICHLAB_INC_DEATH_DATA_URL <- "https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-truth/truth-Incident%20Deaths.csv" - - rlab_cum_case <- download_reichlab_data(cum_case_filename, REICHLAB_CUM_CASE_DATA_URL, "Confirmed") - rlab_cum_death <- download_reichlab_data(cum_death_filename, REICHLAB_CUM_DEATH_DATA_URL, "Deaths") - rlab_inc_case <- download_reichlab_data(inc_case_filename, REICHLAB_INC_CASE_DATA_URL, "incidI") - rlab_inc_death <- download_reichlab_data(inc_death_filename, REICHLAB_INC_DEATH_DATA_URL, "incidDeath") - - rlab_cty_data <- dplyr::full_join(rlab_cum_case, rlab_cum_death) - rlab_cty_data <- dplyr::full_join(rlab_cty_data, rlab_inc_case) - rlab_cty_data <- dplyr::full_join(rlab_cty_data, rlab_inc_death) - rlab_cty_data <- dplyr::filter(rlab_cty_data, scale == "county") - rlab_cty_data <- dplyr::select(rlab_cty_data, Update, Confirmed, Deaths, incidI, incidDeath, FIPS, source) - rlab_cty_data <- dplyr::mutate(rlab_cty_data, incidDeath = ifelse(is.na(incidDeath), 0, incidDeath), - incidI = ifelse(is.na(incidI), 0, incidI), - Confirmed = ifelse(is.na(Confirmed) & Update < "2020-02-01", 0, Confirmed), - Deaths = ifelse(is.na(Deaths) & Update < "2020-02-01", 0, Deaths)) - rlab_cty_data <- dplyr::arrange(rlab_cty_data, source, FIPS, Update) - - return(rlab_cty_data) - -} - #' get_covidcast_data #' #' @param geo_level diff --git a/postprocessing/plot_predictions.R b/postprocessing/plot_predictions.R index 6e3160403..e695fd9e4 100644 --- a/postprocessing/plot_predictions.R +++ b/postprocessing/plot_predictions.R @@ -13,21 +13,21 @@ center_line_var <- ifelse(point_est==0.5, "point", "point-mean") proj_data <- data_comb -#### Which valid locations are missing from our submission? +#### Which valid locations are missing from our submission? # locs <- read_csv("https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-locations/locations.csv") # mismatched <- unique(proj_data$location)[which(!(unique(proj_data$location) %in% locs$location))] # missing_from_fc <- unique(locs$location)[which(!(locs$location %in% unique(proj_data$location)))] -# -# locs %>% filter(location %in% missing_from_fc) +# +# locs %>% filter(location %in% missing_from_fc) # STATE DATA -------------------------------------------------------------- # State Data # -state_cw <- cdlTools::census2010FIPS %>% - distinct(State, State.ANSI) %>% - dplyr::rename(USPS = State, location = State.ANSI) %>% +state_cw <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") %>% + dplyr::distinct(state, state_code) %>% + dplyr::select(USPS = state, location = state_code) %>% dplyr::mutate(location = str_pad(location, 2, side = "left", pad = "0")) %>% distinct(location, USPS) %>% dplyr::mutate(location = as.character(location), USPS = as.character(USPS)) %>% @@ -37,7 +37,7 @@ state_cw <- cdlTools::census2010FIPS %>% # GROUND TRUTH ------------------------------------------------------------ -gt_data <- gt_data %>% +gt_data <- gt_data %>% mutate(time = lubridate::as_date(time)) %>% mutate(date = time) colnames(gt_data) <- gsub("incidI", "incidC", colnames(gt_data)) gt_outcomes <- outcomes_[outcomes_ != "I" & sapply(X = paste0("incid", outcomes_), FUN = function(x=X, y) any(grepl(pattern = x, x = y)), y = colnames(gt_data)) ] @@ -56,8 +56,8 @@ gt_cl <- NULL if (any(outcomes_time_=="weekly")) { # Incident gt_data_st_week <- get_weekly_incid(gt_data %>% dplyr::select(time, geoid, USPS, paste0("incid", outcomes_gt_[outcomes_time_gt_=="weekly"])) %>% mutate(sim_num = 0), - outcomes = outcomes_gt_[outcomes_time_gt_=="weekly"]) - + outcomes = outcomes_gt_[outcomes_time_gt_=="weekly"]) + # Cumulative weekly_cum_outcomes_ <- outcomes_gt_[outcomes_cum_gt_ & outcomes_time_gt_=="weekly"] if (length(weekly_cum_outcomes_)>0) { @@ -65,16 +65,16 @@ if (any(outcomes_time_=="weekly")) { mutate(agestrat="age0to130") %>% rename(outcome = outcome_name, value = outcome) %>% filter(outcome %in% paste0("incid", weekly_cum_outcomes_)), - obs_data = gt_data_2, + obs_data = gt_data_2, gt_cum_vars = paste0("cum", outcomes_gt_[outcomes_cumfromgt_gt_]), # variables to get cum from GT forecast_date = lubridate::as_date(forecast_date), aggregation="week", - loc_column = "USPS", + loc_column = "USPS", use_obs_data = use_obs_data_forcum) %>% rename(outcome_name = outcome, outcome = value) %>% select(-agestrat) - - gt_data_st_week <- gt_data_st_week %>% + + gt_data_st_week <- gt_data_st_week %>% bind_rows(gt_data_st_weekcum) } gt_cl <- gt_cl %>% bind_rows(gt_data_st_week %>% mutate(time_aggr = "weekly")) @@ -82,8 +82,8 @@ if (any(outcomes_time_=="weekly")) { if (any(outcomes_time_=="daily")) { # Incident gt_data_st_day <- get_daily_incid(gt_data %>% dplyr::select(time, geoid, USPS, paste0("incid", outcomes_gt_[outcomes_time_gt_=="daily"])) %>% mutate(sim_num = 0), - outcomes = outcomes_gt_[outcomes_time_gt_=="daily"]) - + outcomes = outcomes_gt_[outcomes_time_gt_=="daily"]) + # Cumulative daily_cum_outcomes_ <- outcomes_gt_[outcomes_cum_gt_ & outcomes_time_gt_=="daily"] if (length(daily_cum_outcomes_)>0){ @@ -91,11 +91,11 @@ if (any(outcomes_time_=="daily")) { mutate(agestrat="age0to130") %>% rename(outcome = outcome_name, value = outcome) %>% filter(outcome %in% paste0("incid", daily_cum_outcomes_)), - obs_data = gt_data_2, + obs_data = gt_data_2, gt_cum_vars = paste0("cum", outcomes_gt_[outcomes_cumfromgt_gt_]), # variables to get cum from GT forecast_date = lubridate::as_date(forecast_date), aggregation="day", - loc_column = "USPS", + loc_column = "USPS", use_obs_data = use_obs_data_forcum) %>% rename(outcome_name = outcome, outcome = value) %>% select(-agestrat) @@ -115,12 +115,12 @@ gt_cl <- gt_cl %>% rename(date = time) # inc_dat_st_vars <- inc_dat_st_vars %>% filter(date != max(date)) # } -dat_st_cl2 <- gt_cl %>% +dat_st_cl2 <- gt_cl %>% select(date, USPS, target = outcome_name, time_aggr, value = outcome) %>% mutate(incid_cum = ifelse(grepl("inc", target), "inc", "cum")) %>% mutate(aggr_target = !grepl('_', target)) %>% mutate(outcome = substr(gsub("cum|incid", "", target), 1,1)) %>% - mutate(pre_gt_end = date<=validation_date) + mutate(pre_gt_end = date<=validation_date) @@ -132,7 +132,7 @@ dat_st_cl2 <- gt_cl %>% forecast_st <- proj_data %>% filter(nchar(location)==2 & (quantile %in% sort(unique(c(quant_values, 0.5))) | is.na(quantile))) %>% - left_join(state_cw, by = c("location")) + left_join(state_cw, by = c("location")) # filter out incid or cum if (!plot_incid) { forecast_st <- forecast_st %>% filter(!grepl(" inc ", target)) } @@ -148,16 +148,16 @@ if(any(outcomes_cum_)){ forecast_st <- forecast_st %>% filter(grepl(paste0(c(paste0("inc ", outcomes_name), cum_outcomes_name), collapse = "|"), target)) # create cat variables -forecast_st_plt <- forecast_st %>% +forecast_st_plt <- forecast_st %>% mutate(incid_cum = ifelse(grepl("inc ", target), "inc", "cum")) %>% mutate(outcome = stringr::word(target, 5)) %>% mutate(outcome = recode(outcome, "inf"="I", "case"="C", "hosp"="H", "death"="D")) %>% - dplyr::mutate(quantile_cln = ifelse(!is.na(quantile), paste0("q", paste0(as.character(quantile*100), "%")), - ifelse(type=="point-mean", paste0("mean"), + dplyr::mutate(quantile_cln = ifelse(!is.na(quantile), paste0("q", paste0(as.character(quantile*100), "%")), + ifelse(type=="point-mean", paste0("mean"), ifelse(type=="point", paste0("median"), NA)))) %>% mutate(target_type = paste0(incid_cum, outcome)) -pltdat_truth <- dat_st_cl2 %>% +pltdat_truth <- dat_st_cl2 %>% filter(aggr_target) %>% rename(gt = value) %>% mutate(target = gsub("incid", "inc", target)) %>% rename(target_type = target) %>% @@ -170,12 +170,12 @@ if(center_line == "mean"){ forecast_st_plt <- forecast_st_plt %>% mutate(quantile_cln = gsub("q50%", "ctr", quantile_cln)) } -forecast_st_plt <- forecast_st_plt %>% +forecast_st_plt <- forecast_st_plt %>% select(scenario_name, scenario_id, target = target_type, incid_cum, outcome, date = target_end_date, USPS, quantile_cln, value) %>% pivot_wider(names_from = quantile_cln, values_from = value) %>% mutate(type = "projection") %>% - full_join(pltdat_truth %>% - mutate(type = "gt", scenario_name = ifelse(pre_gt_end, "gt-pre-projection", "gt-post-projection")) %>% + full_join(pltdat_truth %>% + mutate(type = "gt", scenario_name = ifelse(pre_gt_end, "gt-pre-projection", "gt-post-projection")) %>% select(date, USPS, target = target_type, incid_cum, type, scenario_name, ctr=gt)) %>% filter(date >= trunc_date & date <= sim_end_date) @@ -198,15 +198,15 @@ stplot_fname_nosqrt <- paste0(stplot_fname, ".pdf") pdf(stplot_fname_nosqrt, width=7, height=11) for(usps in unique(forecast_st_plt$USPS)){ - + print(paste0("Plotting: ", usps)) cols_tmp <- cols[names(cols) %in% unique(forecast_st_plt$scenario_name)] - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("inc","",unique(forecast_st_plt$target)),outcomes_)]), " incident ", gsub("inc","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - inc_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + inc_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="inc") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -229,15 +229,15 @@ for(usps in unique(forecast_st_plt$USPS)){ theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) plot(inc_st_plt) - - + + if (plot_cum) { - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("cum","",unique(forecast_st_plt$target)),outcomes_)]), " cumulative ", gsub("cum","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - cum_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + cum_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="cum") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -258,7 +258,7 @@ for(usps in unique(forecast_st_plt$USPS)){ labeller = as_labeller(target_labs)) + theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) - + plot(cum_st_plt) } } @@ -270,15 +270,15 @@ scale_y_funct <- scale_y_sqrt pdf(stplot_fname_sqrt, width=7, height=11) for(usps in unique(forecast_st_plt$USPS)){ - + print(paste0("Plotting: ", usps)) cols_tmp <- cols[names(cols) %in% unique(forecast_st_plt$scenario_name)] - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("inc","",unique(forecast_st_plt$target)),outcomes_)]), " incident ", gsub("inc","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - inc_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + inc_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="inc") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -300,14 +300,14 @@ for(usps in unique(forecast_st_plt$USPS)){ theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) plot(inc_st_plt) - + if (plot_cum) { - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("cum","",unique(forecast_st_plt$target)),outcomes_)]), " cumulative ", gsub("cum","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - cum_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + cum_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="cum") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -328,7 +328,7 @@ for(usps in unique(forecast_st_plt$USPS)){ labeller = as_labeller(target_labs)) + theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) - + plot(cum_st_plt) } } diff --git a/postprocessing/sim_processing_source.R b/postprocessing/sim_processing_source.R index 2d9179ef4..f5d14d682 100644 --- a/postprocessing/sim_processing_source.R +++ b/postprocessing/sim_processing_source.R @@ -597,7 +597,8 @@ reichify_cum_ests <- function(cum_ests, cum_var="cumH", filter(time>opt$forecast_date) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% + dplyr::select(-location) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location = ifelse(USPS=="US", "US", location)) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% rename(value=!!sym(cum_var)) %>% @@ -671,8 +672,7 @@ reichify_inc_ests <- function(weekly_inc_outcome, opt){ pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - forecast_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -711,8 +711,7 @@ format_daily_outcomes <- function(daily_inc_outcome, point_est=0.5, opt){ pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date = opt$forecast_date) %>% rename(target_end_date = time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead = round(as.numeric(target_end_date - forecast_date))) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -758,8 +757,7 @@ format_weekly_outcomes <- function(weekly_inc_outcome, point_est=0.5, opt){ pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - forecast_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -816,8 +814,7 @@ get_weekly_incid2 <- function(res_state, point_est=0.5, outcome_var="incidI", op pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = !!sym(outcome_var)) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - forecast_date)/7))%>% mutate(target=sprintf(paste0("%d wk ahead inc ", outcome_short), ahead)) %>% @@ -1515,8 +1512,7 @@ process_sims <- function( scenario_id = scenario_id, scenario_name=scenario_name) %>% mutate(model_projection_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - model_projection_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -1572,8 +1568,7 @@ process_sims <- function( scenario_id = scenario_id, scenario_name=scenario_name) %>% mutate(model_projection_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - model_projection_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -1601,8 +1596,7 @@ process_sims <- function( unnest(x) %>% pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date=opt$forecast_date) %>% - mutate(location=as.character(cdlTools::fips(USPS))) %>% - mutate(location = ifelse(USPS=="US", "US", location)) %>% + dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% mutate(target = paste0("peak size ", target)) %>% From 5e9defe818c126ad2487bbde989df3d85f0e6028 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 14:26:57 -0500 Subject: [PATCH 06/15] Remove tigris R package dependence --- build/conda_environment.yml | 1 - build/environment_rockfish.yml | 1 - build/renv/renv.lock | 20 ------------- flepimop/R_packages/config.writer/DESCRIPTION | 1 - .../config.writer/R/process_npi_list.R | 2 +- flepimop/gempyor_pkg/docs/Rinterface.Rmd | 1 - flepimop/gempyor_pkg/docs/Rinterface.html | 29 +++++++++---------- 7 files changed, 15 insertions(+), 40 deletions(-) diff --git a/build/conda_environment.yml b/build/conda_environment.yml index f5da549af..d89cf4254 100644 --- a/build/conda_environment.yml +++ b/build/conda_environment.yml @@ -478,7 +478,6 @@ dependencies: - conda-forge/linux-64::r-readxl==1.4.1=r42h3ebcfa7_1 - conda-forge/noarch::r-reprex==2.0.2=r42hc72bb7e_1 - conda-forge/linux-64::r-tidyr==1.3.0=r42h38f115c_0 - - conda-forge/noarch::r-tigris==2.0.1=r42hc72bb7e_0 - conda-forge/noarch::r-waldo==0.4.0=r42hc72bb7e_1 - conda-forge/noarch::r-broom==1.0.3=r42hc72bb7e_0 - conda-forge/linux-64::r-gdtools==0.3.0=r42he0ce631_0 diff --git a/build/environment_rockfish.yml b/build/environment_rockfish.yml index de65f406e..04896701d 100644 --- a/build/environment_rockfish.yml +++ b/build/environment_rockfish.yml @@ -416,7 +416,6 @@ dependencies: - r-tidyr=1.3.0=r42h38f115c_0 - r-tidyselect=1.2.0=r42hc72bb7e_0 - r-tidyverse=1.3.2=r42hc72bb7e_1 - - r-tigris=2.0.1=r42hc72bb7e_0 - r-timechange=0.2.0=r42h38f115c_0 - r-tinytex=0.44=r42hc72bb7e_0 - r-triebeard=0.3.0=r42h7525677_1005 diff --git a/build/renv/renv.lock b/build/renv/renv.lock index 0a074793a..8f0a506a4 100644 --- a/build/renv/renv.lock +++ b/build/renv/renv.lock @@ -434,7 +434,6 @@ "magrittr", "readr", "tidyverse", - "tigris", "yaml" ] }, @@ -2151,25 +2150,6 @@ "xml2" ] }, - "tigris": { - "Package": "tigris", - "Version": "1.6", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "d049cc17ef0d4a82f8d2ffb4ba520a76", - "Requirements": [ - "dplyr", - "httr", - "magrittr", - "maptools", - "rappdirs", - "rgdal", - "sf", - "sp", - "stringr", - "uuid" - ] - }, "timechange": { "Package": "timechange", "Version": "0.2.0", diff --git a/flepimop/R_packages/config.writer/DESCRIPTION b/flepimop/R_packages/config.writer/DESCRIPTION index 0af825e9d..f2551cb89 100644 --- a/flepimop/R_packages/config.writer/DESCRIPTION +++ b/flepimop/R_packages/config.writer/DESCRIPTION @@ -5,7 +5,6 @@ Imports: tidyverse (>= 1.3.1), readr (>= 2.0.0), lubridate, - tigris, magrittr, yaml, MMWRweek, diff --git a/flepimop/R_packages/config.writer/R/process_npi_list.R b/flepimop/R_packages/config.writer/R/process_npi_list.R index 7313d23fe..2ce0af42b 100644 --- a/flepimop/R_packages/config.writer/R/process_npi_list.R +++ b/flepimop/R_packages/config.writer/R/process_npi_list.R @@ -51,7 +51,7 @@ load_geodata_file <- function(filename, } if(state_name) { - geodata <- tigris::fips_codes %>% + geodata <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") %>% dplyr::distinct(state, state_name) %>% dplyr::rename(USPS = state) %>% dplyr::rename(state = state_name) %>% diff --git a/flepimop/gempyor_pkg/docs/Rinterface.Rmd b/flepimop/gempyor_pkg/docs/Rinterface.Rmd index 16145addb..afd8eb67e 100644 --- a/flepimop/gempyor_pkg/docs/Rinterface.Rmd +++ b/flepimop/gempyor_pkg/docs/Rinterface.Rmd @@ -263,7 +263,6 @@ conda activate flepimop-env python -m ipykernel install --user --name flepimop-env --display-name "Python (flepimop-env)" export R_PROFILE=$FLEPI_PATH/slurm_batch/Rprofile conda install r-devtools r-gridExtra r-ggfortify r-flextable r-doparallel r-reticulate r-truncnorm r-arrow -conda install r-tigris conda install r-tidycensus r-optparse conda env export --from-history > environment_cross.yml conda env export > environment.yml diff --git a/flepimop/gempyor_pkg/docs/Rinterface.html b/flepimop/gempyor_pkg/docs/Rinterface.html index bdbeb1978..27434f0fd 100644 --- a/flepimop/gempyor_pkg/docs/Rinterface.html +++ b/flepimop/gempyor_pkg/docs/Rinterface.html @@ -240,7 +240,7 @@

Import

library(ggplot2)
 library(tibble)
 # reticulate::use_python(Sys.which('python'),require=TRUE)
-reticulate::use_condaenv('flepimop-env')   
+reticulate::use_condaenv('flepimop-env')
 gempyor <- reticulate::import("gempyor")
@@ -254,7 +254,7 @@

Building a simulator

first_sim_index=1, npi_scenario="inference", # NPIs scenario to use outcome_scenario="med", # Outcome scenario to use - stoch_traj_flag=FALSE, + stoch_traj_flag=FALSE, spatial_path_prefix = '../tests/npi/' # prefix where to find the folder indicated in spatial_setup )

Here we specify that the data folder specified in the config lies in the test/npi/ folder, not in the current directory. The only mandatory arguments is the config_path. The default values of the other arguments are

@@ -266,7 +266,7 @@

Building a simulator

stoch_traj_flag=False, rng_seed=None, nslots=1, - initialize=True, + initialize=True, out_run_id=None, # if out_run_id should be different from in_run_id, put it here out_prefix=None, # if out_prefix should be different from in_prefix, put it here spatial_path_prefix="", # in case the data folder is on another directory @@ -285,7 +285,7 @@

Exploration methods

Parameters

It is possible to draw the parameters of the disease dynamics. The following line draw from config (hence each call will return a different draw from the prior), but the syntax would be the same with load_ID, bypass_FN, bypass_DF, where a spar file would be loaded.

-
# this variation returns a dataframe. 
+
# this variation returns a dataframe.
 params_draw_df = gempyor_simulator$get_seir_parametersDF()   # could also accept (load_ID=True, sim_id2load=XXX) or (bypass_DF=<some_spar_df>) or (bypass_FN=<some_spar_filename>)
 
 ## This return an array, which is useful together with a NPI to get the reduce parameter (cf. later in the tutorial)
@@ -316,10 +316,10 @@ 

NPIs

reduc <- npi_seir$getReduction(param = 'r0')
 
 
-reduc <- reduc %>% rownames_to_column(var = 'geoid') 
-reduc <- reduc %>% 
-  pivot_longer(cols=colnames(reduc %>% select(-geoid)), names_to = "date", values_to = "reduction") %>% 
-  mutate(date=as.Date(date))  
+reduc <- reduc %>% rownames_to_column(var = 'geoid')
+reduc <- reduc %>%
+  pivot_longer(cols=colnames(reduc %>% select(-geoid)), names_to = "date", values_to = "reduction") %>%
+  mutate(date=as.Date(date))
 # let's plot it:
 reduc %>% ggplot(aes(x=date, y=reduction)) + geom_path() + facet_wrap(~ geoid)

@@ -333,8 +333,8 @@

NPIs

as it is only inciditoc_all here, we can plot it

reduc <- npi_outcome$getReduction(param = 'inciditoc_all')
 # There is a bit of R to get it to something usable, it's probably a very ugly way to do this:
-reduc <- reduc %>% rownames_to_column(var = 'geoid') 
-reduc <- reduc %>% 
+reduc <- reduc %>% rownames_to_column(var = 'geoid')
+reduc <- reduc %>%
   pivot_longer(cols=colnames(reduc %>% select(-geoid)), names_to = "date", values_to = "reduction") %>%
   mutate(date=as.Date(date))
 
@@ -343,10 +343,10 @@ 

NPIs

SEIR Parameters, but reduced

We can also plot the pameters after reduction with the npi. We just have to provided the npi object. The reduction contains all parameter. Here we build it and plot R0 in time (not that the trends are inverted from the getReduction above ^)

-
# This will draw new parameters from config and applies the already defined NPI. If load_ID, bypass_DF or bypass_FN 
+
# This will draw new parameters from config and applies the already defined NPI. If load_ID, bypass_DF or bypass_FN
 param_reduc = gempyor_simulator$get_seir_parameter_reduced(npi_seir=npi_seir) # could also accept (load_ID=True, sim_id2load=XXX) or (bypass_DF=<some_spar_df>) or (bypass_FN=<some_spar_filename>)
 
-# We can also provide an array as returned by gempyor_simulator$get_seir_parameters() 
+# We can also provide an array as returned by gempyor_simulator$get_seir_parameters()
 param_reduc_from = gempyor_simulator$get_seir_parameter_reduced(npi_seir=npi_seir, p_draw=params_draw_arr)
 param_reduc <- param_reduc %>% pivot_longer(cols = param_reduc %>% select(-date, -geoid) %>% colnames(), names_to = 'parameter')
 
@@ -360,8 +360,8 @@ 

SEIR Parameters, but reduced

Get compartment graph image

We can plot the compartment transition graph with this config. There is a possibility to apply filters in order to have tractable graph. The graph is plotted as a separate pdf file.

gempyor_simulator$plot_transition_graph(output_file="full_graph")
-gempyor_simulator$plot_transition_graph(output_file="readable_graph", 
-                                        source_filters= list(list("age0to17"), list("OMICRON", "WILD")), 
+gempyor_simulator$plot_transition_graph(output_file="readable_graph",
+                                        source_filters= list(list("age0to17"), list("OMICRON", "WILD")),
                                         destination_filters= list(list("OMICRON", "WILD")))

here if source_filters is [[“age0to17”], [“OMICRON”, “WILD”]], it means filter (keep) all transitions that have as source: age0to17 AND (OMICRON OR WILD).

@@ -441,7 +441,6 @@

Conda (complicated)

python -m ipykernel install --user --name flepimop-env --display-name "Python (flepimop-env)" export R_PROFILE=$FLEPI_PATH/slurm_batch/Rprofile conda install r-devtools r-gridExtra r-ggfortify r-flextable r-doparallel r-reticulate r-truncnorm r-arrow -conda install r-tigris conda install r-tidycensus r-optparse conda env export --from-history > environment_cross.yml conda env export > environment.yml
From f3cc3099f0f42d018d8199820865e141e84f32b1 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 14:39:35 -0500 Subject: [PATCH 07/15] remove tidycensus r package --- build/conda_environment.yml | 1 - build/environment_rockfish.yml | 1 - datasetup/build_US_setup.R | 42 ++++++++-------- datasetup/build_covid_data.R | 2 +- datasetup/build_flu_data.R | 48 +++++++++---------- flepimop/R_packages/flepicommon/NAMESPACE | 4 -- flepimop/R_packages/flepicommon/R/DataUtils.R | 27 +---------- .../{ => archive}/test-get_USAFacts_data.R | 0 flepimop/gempyor_pkg/docs/Rinterface.Rmd | 2 +- flepimop/gempyor_pkg/docs/Rinterface.html | 2 +- 10 files changed, 49 insertions(+), 80 deletions(-) rename flepimop/R_packages/flepicommon/tests/testthat/{ => archive}/test-get_USAFacts_data.R (100%) diff --git a/build/conda_environment.yml b/build/conda_environment.yml index d89cf4254..2f9d992a9 100644 --- a/build/conda_environment.yml +++ b/build/conda_environment.yml @@ -485,7 +485,6 @@ dependencies: - conda-forge/linux-64::r-haven==2.5.1=r42h7525677_0 - conda-forge/linux-64::r-profvis==0.3.7=r42h06615bd_1 - conda-forge/linux-64::r-testthat==3.1.6=r42h38f115c_0 - - conda-forge/noarch::r-tidycensus==1.3.2=r42hc72bb7e_0 - conda-forge/noarch::r-devtools==2.4.5=r42hc72bb7e_1 - conda-forge/noarch::r-flextable==0.8.5=r42hc72bb7e_0 - conda-forge/noarch::r-modelr==0.1.10=r42hc72bb7e_0 diff --git a/build/environment_rockfish.yml b/build/environment_rockfish.yml index 04896701d..14b8cdc26 100644 --- a/build/environment_rockfish.yml +++ b/build/environment_rockfish.yml @@ -411,7 +411,6 @@ dependencies: - r-testthat=3.1.6=r42h38f115c_0 - r-textshaping=0.3.6=r42hbb20487_4 - r-tibble=3.1.8=r42h06615bd_1 - - r-tidycensus=1.3.2=r42hc72bb7e_0 - r-tidygraph=1.2.3=r42h38f115c_0 - r-tidyr=1.3.0=r42h38f115c_0 - r-tidyselect=1.2.0=r42hc72bb7e_0 diff --git a/datasetup/build_US_setup.R b/datasetup/build_US_setup.R index 974052c61..6c2ecfce2 100644 --- a/datasetup/build_US_setup.R +++ b/datasetup/build_US_setup.R @@ -37,7 +37,7 @@ library(dplyr) library(tidyr) -library(tidycensus) +# library(tidycensus) option_list = list( @@ -64,38 +64,38 @@ dir.create(outdir, showWarnings = FALSE, recursive = TRUE) # census_data <- arrow::read_parquet(file.path(opt$p,"datasetup", "usdata","united-states-commutes","census_tracts_2010.gz.parquet")) -# Get census key -census_key = Sys.getenv("CENSUS_API_KEY") -if(length(config$importation$census_api_key) != 0){ - census_key = config$importation$census_api_key -} -if(census_key == ""){ - stop("no census key found -- please set CENSUS_API_KEY environment variable or specify importation::census_api_key in config file") -} -tidycensus::census_api_key(key = census_key) - +# # Get census key +# census_key = Sys.getenv("CENSUS_API_KEY") +# if(length(config$importation$census_api_key) != 0){ +# census_key = config$importation$census_api_key +# } +# if(census_key == ""){ +# stop("no census key found -- please set CENSUS_API_KEY environment variable or specify importation::census_api_key in config file") +# } +# tidycensus::census_api_key(key = census_key) +# # GEODATA (CENSUS DATA) ------------------------------------------------------------- -census_data <- tidycensus::get_acs(geography="county", state=filterUSPS, - variables="B01003_001", year=config$spatial_setup$census_year, - keep_geo_vars=TRUE, geometry=FALSE, show_call=TRUE) -census_data <- census_data %>% - dplyr::rename(population=estimate, geoid=GEOID) %>% - dplyr::select(geoid, population) %>% - dplyr::mutate(geoid = substr(geoid,1,5)) +# census_data <- tidycensus::get_acs(geography="county", state=filterUSPS, +# variables="B01003_001", year=config$spatial_setup$census_year, +# keep_geo_vars=TRUE, geometry=FALSE, show_call=TRUE) +census_data <- arrow::read_parquet("datasetup/usdata/us_county_census_2019.parquet") %>% + dplyr::rename(population=estimate, subpop=GEOID) %>% + dplyr::select(subpop, population) %>% + dplyr::mutate(subpop = substr(subpop,1,5)) # Add USPS column -data(fips_codes) +fips_codes <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") fips_geoid_codes <- dplyr::mutate(fips_codes, geoid=paste0(state_code,county_code)) %>% dplyr::group_by(geoid) %>% dplyr::summarize(USPS=unique(state)) -census_data <- dplyr::left_join(census_data, fips_geoid_codes, by="geoid") - +census_data <- dplyr::left_join(census_data, fips_subpop_codes, by="subpop") %>% + dplyr::filter(USPS %in% filterUSPS) # Make each territory one county. # Puerto Rico is the only one in the 2018 ACS estimates right now. Aggregate it. diff --git a/datasetup/build_covid_data.R b/datasetup/build_covid_data.R index 5cb71c415..68b2a74a5 100644 --- a/datasetup/build_covid_data.R +++ b/datasetup/build_covid_data.R @@ -6,7 +6,7 @@ library(dplyr) library(tidyr) -library(tidycensus) +# library(tidycensus) library(readr) library(lubridate) library(flepicommon) diff --git a/datasetup/build_flu_data.R b/datasetup/build_flu_data.R index 34fb5d59c..6df529081 100644 --- a/datasetup/build_flu_data.R +++ b/datasetup/build_flu_data.R @@ -4,7 +4,7 @@ library(dplyr) library(tidyr) -library(tidycensus) +# library(tidycensus) library(readr) library(lubridate) @@ -65,11 +65,11 @@ locs <- read_csv(file.path(config$data_path, config$spatial_setup$geodata)) us_data <- us_data %>% mutate(location = stringr::str_pad(location, width = 2, side = "left", pad = "0")) -us_data <- us_data %>% +us_data <- us_data %>% filter(location != "US") %>% mutate(location = stringr::str_pad(location, width=5, side="right", pad="0")) %>% left_join(locs, by = c("location"="geoid")) %>% - rename(FIPS = location, + rename(FIPS = location, incidH = value, source = USPS) %>% select(-location_name, -pop2019est) @@ -98,24 +98,24 @@ variant_props_file <- config$seeding$variant_filename adjust_for_variant <- !is.null(variant_props_file) # if (adjust_for_variant){ -# +# # # Variant Data (need to automate this data pull still) # #variant_data <- read_csv(file.path(config$data_path, "variant/WHO_NREVSS_Clinical_Labs.csv"), skip = 1) # variant_data <- cdcfluview::who_nrevss(region="state", years = 2022)$clinical_labs -# +# # # location data # loc_data <- read_csv("data-locations/locations.csv") -# -# +# +# # # CLEAN DATA -# +# # variant_data <- variant_data %>% # select(state = region, # week = week, # year = year, # FluA = total_a, # FluB = total_b) %>% -# # select(state = REGION, +# # select(state = REGION, # # week = WEEK, # # year = YEAR, # # FluA = `TOTAL A`, @@ -145,14 +145,14 @@ adjust_for_variant <- !is.null(variant_props_file) # mutate(prop = ifelse(is.na(prop), 0, prop)) %>% # filter(!is.na(week_end)) %>% # filter(week_end <= as_date(end_date_)) -# +# # variant_data <- variant_data %>% # left_join(loc_data %>% select(state = location_name, source = abbreviation)) %>% # mutate(week = epiweek(week_end), year = epiyear(week_end)) -# +# # if(end_date_ != max(variant_data$week_end)){ # # Extend to dates of groundtruth -# var_max_dates <- variant_data %>% +# var_max_dates <- variant_data %>% # group_by(source, state) %>% # filter(week_end == max(week_end)) %>% # ungroup() %>% @@ -164,50 +164,50 @@ adjust_for_variant <- !is.null(variant_props_file) # ungroup() # var_max_dates <- var_max_dates %>% # rename(max_current = week_end) %>% -# mutate(week_end = strsplit(as.character(weeks_missing), ",")) %>% +# mutate(week_end = strsplit(as.character(weeks_missing), ",")) %>% # unnest(week_end) %>% # select(state, week, year, variant, prop, week_end, source) %>% # mutate(week_end = as_date(week_end)) # variant_data <- variant_data %>% # bind_rows(var_max_dates) # } -# +# # variant_data <- variant_data %>% # mutate(week = epiweek(week_end), year = epiyear(week_end)) -# +# # variant_data <- variant_data %>% # expand_grid(day = 1:7) %>% # mutate(date = as_date(MMWRweek::MMWRweek2Date(year, week, day))) %>% # select(c(variant, prop, source, date)) -# -# variant_data <- variant_data %>% +# +# variant_data <- variant_data %>% # filter(date >= as_date(config$start_date) & date <= as_date(config$end_date_groundtruth)) -# +# # write_csv(variant_data, variant_props_file) # } -# +# # APPLY VARIANTS ---------------------------------------------------------- if (adjust_for_variant) { - + us_data <- read_csv(config$inference$gt_data_path) - + tryCatch({ us_data <- flepicommon::do_variant_adjustment(us_data, variant_props_file) - us_data <- us_data %>% + us_data <- us_data %>% filter(date >= as_date(config$start_date) & date <= as_date(config$end_date_groundtruth)) write_csv(us_data, config$inference$gt_data_path) }, error = function(e) { - stop(paste0("Could not use variant file |", variant_props_file, + stop(paste0("Could not use variant file |", variant_props_file, "|, with error message", e$message)) }) } -cat(paste0("Ground truth data saved\n", +cat(paste0("Ground truth data saved\n", " -- file: ", config$inference$gt_data_path,".\n", " -- outcomes: ", paste(grep("incid", colnames(us_data), value = TRUE), collapse = ", "))) diff --git a/flepimop/R_packages/flepicommon/NAMESPACE b/flepimop/R_packages/flepicommon/NAMESPACE index c8f936299..50ed4cd5b 100644 --- a/flepimop/R_packages/flepicommon/NAMESPACE +++ b/flepimop/R_packages/flepicommon/NAMESPACE @@ -10,18 +10,14 @@ export(create_file_name) export(create_prefix) export(do_variant_adjustment) export(download_CSSE_global_data) -export(download_reichlab_data) export(fix_negative_counts) export(fix_negative_counts_global) export(fix_negative_counts_single_geoid) export(get_CSSE_US_data) export(get_CSSE_US_matchGlobal_data) export(get_CSSE_global_data) -export(get_USAFacts_data) export(get_covidcast_data) export(get_groundtruth_from_source) -export(get_reichlab_cty_data) -export(get_reichlab_st_data) export(load_config) export(load_geodata_file) export(prettyprint_optlist) diff --git a/flepimop/R_packages/flepicommon/R/DataUtils.R b/flepimop/R_packages/flepicommon/R/DataUtils.R index 95b54af06..2b0faa55e 100755 --- a/flepimop/R_packages/flepicommon/R/DataUtils.R +++ b/flepimop/R_packages/flepicommon/R/DataUtils.R @@ -789,32 +789,7 @@ get_groundtruth_from_source <- function( variant_props_file = "data/variant/variant_props_long.csv" ) { - if(source == "reichlab" & scale == "US county"){ - - rc <- get_reichlab_cty_data() - rc <- dplyr::select(rc, Update, FIPS, source, !!variables) - - } else if(source == "reichlab" & scale == "US state"){ - - rc <- get_reichlab_st_data() - rc <- dplyr::select(rc, Update, FIPS, source, !!variables) - - } else if(source == "usafacts" & scale == "US county"){ - - rc <- get_USAFacts_data(tempfile(), tempfile(), incl_unassigned = incl_unass) %>% - dplyr::select(Update, FIPS, source, !!variables) %>% - dplyr::ungroup() - - } else if(source == "usafacts" & scale == "US state"){ - - rc <- get_USAFacts_data(tempfile(), tempfile(), incl_unassigned = incl_unass) %>% - dplyr::select(Update, FIPS, source, !!variables) %>% - dplyr::mutate(FIPS = paste0(stringr::str_sub(FIPS, 1, 2), "000")) %>% - dplyr::group_by(Update, FIPS, source) %>% - dplyr::summarise_if(is.numeric, sum) %>% - dplyr::ungroup() - - } else if(source == "csse" & scale == "US county"){ + if (source == "csse" & scale == "US county"){ rc <- get_CSSE_US_data(tempfile(), tempfile(), incl_unassigned = incl_unass) %>% dplyr::select(Update, FIPS, source, !!variables) diff --git a/flepimop/R_packages/flepicommon/tests/testthat/test-get_USAFacts_data.R b/flepimop/R_packages/flepicommon/tests/testthat/archive/test-get_USAFacts_data.R similarity index 100% rename from flepimop/R_packages/flepicommon/tests/testthat/test-get_USAFacts_data.R rename to flepimop/R_packages/flepicommon/tests/testthat/archive/test-get_USAFacts_data.R diff --git a/flepimop/gempyor_pkg/docs/Rinterface.Rmd b/flepimop/gempyor_pkg/docs/Rinterface.Rmd index afd8eb67e..2c022dd07 100644 --- a/flepimop/gempyor_pkg/docs/Rinterface.Rmd +++ b/flepimop/gempyor_pkg/docs/Rinterface.Rmd @@ -263,7 +263,7 @@ conda activate flepimop-env python -m ipykernel install --user --name flepimop-env --display-name "Python (flepimop-env)" export R_PROFILE=$FLEPI_PATH/slurm_batch/Rprofile conda install r-devtools r-gridExtra r-ggfortify r-flextable r-doparallel r-reticulate r-truncnorm r-arrow -conda install r-tidycensus r-optparse +conda install r-optparse conda env export --from-history > environment_cross.yml conda env export > environment.yml ``` diff --git a/flepimop/gempyor_pkg/docs/Rinterface.html b/flepimop/gempyor_pkg/docs/Rinterface.html index 27434f0fd..858c5d62c 100644 --- a/flepimop/gempyor_pkg/docs/Rinterface.html +++ b/flepimop/gempyor_pkg/docs/Rinterface.html @@ -441,7 +441,7 @@

Conda (complicated)

python -m ipykernel install --user --name flepimop-env --display-name "Python (flepimop-env)" export R_PROFILE=$FLEPI_PATH/slurm_batch/Rprofile conda install r-devtools r-gridExtra r-ggfortify r-flextable r-doparallel r-reticulate r-truncnorm r-arrow -conda install r-tidycensus r-optparse +conda install r-optparse conda env export --from-history > environment_cross.yml conda env export > environment.yml
From 9c87ad39c43d42630e2beabdb485a6c25d230bc6 Mon Sep 17 00:00:00 2001 From: Shaun Truelove Date: Mon, 4 Dec 2023 14:46:34 -0500 Subject: [PATCH 08/15] remove RSocrata from local_install.R --- build/local_install.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/local_install.R b/build/local_install.R index 5870e7d9a..1ef0cd924 100644 --- a/build/local_install.R +++ b/build/local_install.R @@ -8,7 +8,7 @@ local({r <- getOption("repos") library(devtools) -install.packages(c("covidcast","data.table","vroom","dplyr","RSocrata"), quiet=TRUE) +install.packages(c("covidcast","data.table","vroom","dplyr"), quiet=TRUE) # devtools::install_github("hrbrmstr/cdcfluview") # To run if operating in the container ----- From 5b8ec85bc812b45394266ebd648f7be6e38865d5 Mon Sep 17 00:00:00 2001 From: saraloo <45245630+saraloo@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:00:32 +0100 Subject: [PATCH 09/15] fix flepi path in us setup --- datasetup/build_US_setup.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datasetup/build_US_setup.R b/datasetup/build_US_setup.R index 6c2ecfce2..353da592b 100644 --- a/datasetup/build_US_setup.R +++ b/datasetup/build_US_setup.R @@ -83,13 +83,13 @@ dir.create(outdir, showWarnings = FALSE, recursive = TRUE) # census_data <- tidycensus::get_acs(geography="county", state=filterUSPS, # variables="B01003_001", year=config$spatial_setup$census_year, # keep_geo_vars=TRUE, geometry=FALSE, show_call=TRUE) -census_data <- arrow::read_parquet("datasetup/usdata/us_county_census_2019.parquet") %>% +census_data <- arrow::read_parquet(paste0(opt$p,"/datasetup/usdata/us_county_census_2019.parquet")) %>% dplyr::rename(population=estimate, subpop=GEOID) %>% dplyr::select(subpop, population) %>% dplyr::mutate(subpop = substr(subpop,1,5)) # Add USPS column -fips_codes <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") +fips_codes <- arrow::read_parquet(paste0(opt$p,"/datasetup/usdata/fips_us_county.parquet")) fips_geoid_codes <- dplyr::mutate(fips_codes, geoid=paste0(state_code,county_code)) %>% dplyr::group_by(geoid) %>% dplyr::summarize(USPS=unique(state)) From 6ef76f7b9cbf6ff9304b2436ef6c4c07a1940ad6 Mon Sep 17 00:00:00 2001 From: saraloo <45245630+saraloo@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:32:13 +0100 Subject: [PATCH 10/15] add flepi path to new data files for load_geodata_file --- flepimop/R_packages/config.writer/R/process_npi_list.R | 5 +++-- .../R_packages/config.writer/tests/testthat/test-gen_npi.R | 2 +- flepimop/R_packages/flepicommon/R/DataUtils.R | 5 +++-- flepimop/main_scripts/create_seeding.R | 4 +++- flepimop/main_scripts/create_seeding_added.R | 4 +++- flepimop/main_scripts/inference_slot.R | 1 + 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/flepimop/R_packages/config.writer/R/process_npi_list.R b/flepimop/R_packages/config.writer/R/process_npi_list.R index 2ce0af42b..b5b9cb5fa 100644 --- a/flepimop/R_packages/config.writer/R/process_npi_list.R +++ b/flepimop/R_packages/config.writer/R/process_npi_list.R @@ -36,7 +36,8 @@ NULL load_geodata_file <- function(filename, geoid_len = 0, geoid_pad = "0", - state_name = TRUE) { + state_name = TRUE, + flepi_path = "flepiMoP") { if(!file.exists(filename)){stop(paste(filename,"does not exist in",getwd()))} geodata <- readr::read_csv(filename) %>% @@ -51,7 +52,7 @@ load_geodata_file <- function(filename, } if(state_name) { - geodata <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") %>% + geodata <- arrow::read_parquet(file.path(flepi_path,"datasetup/usdata/fips_us_county.parquet")) %>% dplyr::distinct(state, state_name) %>% dplyr::rename(USPS = state) %>% dplyr::rename(state = state_name) %>% diff --git a/flepimop/R_packages/config.writer/tests/testthat/test-gen_npi.R b/flepimop/R_packages/config.writer/tests/testthat/test-gen_npi.R index 193bb40ce..0960cc832 100644 --- a/flepimop/R_packages/config.writer/tests/testthat/test-gen_npi.R +++ b/flepimop/R_packages/config.writer/tests/testthat/test-gen_npi.R @@ -10,7 +10,7 @@ generate_processed <- function(geodata_path, outcomes_path ){ temp_name <- tempfile() - geodata <- load_geodata_file(filename = geodata_path) + geodata <- load_geodata_file(filename = geodata_path, flepi_path = "../flepiMoP") npi_dat <- process_npi_shub(intervention_path = intervention_path, geodata) diff --git a/flepimop/R_packages/flepicommon/R/DataUtils.R b/flepimop/R_packages/flepicommon/R/DataUtils.R index 2b0faa55e..6be2c91fa 100755 --- a/flepimop/R_packages/flepicommon/R/DataUtils.R +++ b/flepimop/R_packages/flepicommon/R/DataUtils.R @@ -21,7 +21,8 @@ load_geodata_file <- function(filename, geoid_len = 0, geoid_pad = "0", - state_name = TRUE + state_name = TRUE, + flepi_path = "flepiMoP" ) { if(!file.exists(filename)){stop(paste(filename,"does not exist in",getwd()))} @@ -37,7 +38,7 @@ load_geodata_file <- function(filename, } if(state_name) { - geodata <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") %>% + geodata <- arrow::read_parquet(file.path(flepi_path,"datasetup/usdata/fips_us_county.parquet")) %>% dplyr::distinct(state, state_name) %>% dplyr::rename(USPS = state) %>% dplyr::rename(state = state_name) %>% diff --git a/flepimop/main_scripts/create_seeding.R b/flepimop/main_scripts/create_seeding.R index a085af059..2ed7d6002 100644 --- a/flepimop/main_scripts/create_seeding.R +++ b/flepimop/main_scripts/create_seeding.R @@ -45,6 +45,7 @@ library(purrr) option_list <- list( optparse::make_option(c("-c", "--config"), action = "store", default = Sys.getenv("CONFIG_PATH"), type = "character", help = "path to the config file"), + optparse::make_option(c("-p", "--flepi_path"), action="store", type='character', help="path to the flepiMoP directory", default = Sys.getenv("FLEPI_PATH", "flepiMoP/")), optparse::make_option(c("-k", "--keep_all_seeding"), action="store",default=TRUE,type='logical',help="Whether to filter away seeding prior to the start date of the simulation.") ) @@ -269,7 +270,8 @@ geodata <- flepicommon::load_geodata_file( file.path(config$data_path, config$spatial_setup$geodata), 5, "0", - TRUE + TRUE, + flepi_path = opt$flepi_path ) all_geoids <- geodata[[config$spatial_setup$nodenames]] diff --git a/flepimop/main_scripts/create_seeding_added.R b/flepimop/main_scripts/create_seeding_added.R index efcff2b01..dd7110301 100644 --- a/flepimop/main_scripts/create_seeding_added.R +++ b/flepimop/main_scripts/create_seeding_added.R @@ -44,6 +44,7 @@ library(purrr) option_list <- list( optparse::make_option(c("-c", "--config"), action = "store", default = Sys.getenv("CONFIG_PATH"), type = "character", help = "path to the config file"), + optparse::make_option(c("-p", "--flepi_path"), action="store", type='character', help="path to the flepiMoP directory", default = Sys.getenv("FLEPI_PATH", "flepiMoP/")), optparse::make_option(c("-k", "--keep_all_seeding"), action="store",default=TRUE,type='logical',help="Whether to filter away seeding prior to the start date of the simulation.") ) opt <- optparse::parse_args(optparse::OptionParser(option_list = option_list)) @@ -267,7 +268,8 @@ geodata <- flepicommon::load_geodata_file( file.path(config$data_path, config$spatial_setup$geodata), 5, "0", - TRUE + TRUE, + flepi_path = opt$flepi_path ) all_geoids <- geodata[[config$spatial_setup$nodenames]] diff --git a/flepimop/main_scripts/inference_slot.R b/flepimop/main_scripts/inference_slot.R index 887c0f48f..3edb45a1e 100644 --- a/flepimop/main_scripts/inference_slot.R +++ b/flepimop/main_scripts/inference_slot.R @@ -108,6 +108,7 @@ suppressMessages( config$data_path, config$spatial_setup$geodata, sep = "/" ), + flepi_path = opt$flepi_path, geoid_len = opt$geoid_len ) ) From 8a42fa75e48109c138fa677fb3814bf0adc9a2fb Mon Sep 17 00:00:00 2001 From: saraloo <45245630+saraloo@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:54:29 -0500 Subject: [PATCH 11/15] change subpop to geoid for FCH configs --- datasetup/build_US_setup.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/datasetup/build_US_setup.R b/datasetup/build_US_setup.R index 353da592b..0ae03a63f 100644 --- a/datasetup/build_US_setup.R +++ b/datasetup/build_US_setup.R @@ -84,9 +84,9 @@ dir.create(outdir, showWarnings = FALSE, recursive = TRUE) # variables="B01003_001", year=config$spatial_setup$census_year, # keep_geo_vars=TRUE, geometry=FALSE, show_call=TRUE) census_data <- arrow::read_parquet(paste0(opt$p,"/datasetup/usdata/us_county_census_2019.parquet")) %>% - dplyr::rename(population=estimate, subpop=GEOID) %>% - dplyr::select(subpop, population) %>% - dplyr::mutate(subpop = substr(subpop,1,5)) + dplyr::rename(population=estimate, geoid=GEOID) %>% + dplyr::select(geoid, population) %>% + dplyr::mutate(geoid = substr(geoid,1,5)) # Add USPS column fips_codes <- arrow::read_parquet(paste0(opt$p,"/datasetup/usdata/fips_us_county.parquet")) @@ -94,7 +94,7 @@ fips_geoid_codes <- dplyr::mutate(fips_codes, geoid=paste0(state_code,county_cod dplyr::group_by(geoid) %>% dplyr::summarize(USPS=unique(state)) -census_data <- dplyr::left_join(census_data, fips_subpop_codes, by="subpop") %>% +census_data <- dplyr::left_join(census_data, fips_geoid_codes, by="geoid") %>% dplyr::filter(USPS %in% filterUSPS) # Make each territory one county. From 424419fe3bf7b34d2cb080649f1fcfe01a651e60 Mon Sep 17 00:00:00 2001 From: saraloo Date: Mon, 11 Mar 2024 10:38:14 -0400 Subject: [PATCH 12/15] fix typos in file paths for usdata files --- postprocessing/plot_predictions.R | 2 +- postprocessing/sim_processing_source.R | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/postprocessing/plot_predictions.R b/postprocessing/plot_predictions.R index e695fd9e4..6954d1c35 100644 --- a/postprocessing/plot_predictions.R +++ b/postprocessing/plot_predictions.R @@ -25,7 +25,7 @@ proj_data <- data_comb # STATE DATA -------------------------------------------------------------- # State Data # -state_cw <- arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") %>% +state_cw <- arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/fips_us_county.parquet")) %>% dplyr::distinct(state, state_code) %>% dplyr::select(USPS = state, location = state_code) %>% dplyr::mutate(location = str_pad(location, 2, side = "left", pad = "0")) %>% diff --git a/postprocessing/sim_processing_source.R b/postprocessing/sim_processing_source.R index f5d14d682..f26f1f630 100644 --- a/postprocessing/sim_processing_source.R +++ b/postprocessing/sim_processing_source.R @@ -598,7 +598,8 @@ reichify_cum_ests <- function(cum_ests, cum_var="cumH", mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% dplyr::select(-location) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% mutate(location = ifelse(USPS=="US", "US", location)) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% rename(value=!!sym(cum_var)) %>% @@ -672,7 +673,8 @@ reichify_inc_ests <- function(weekly_inc_outcome, opt){ pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - forecast_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -711,7 +713,8 @@ format_daily_outcomes <- function(daily_inc_outcome, point_est=0.5, opt){ pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date = opt$forecast_date) %>% rename(target_end_date = time) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead = round(as.numeric(target_end_date - forecast_date))) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -757,7 +760,8 @@ format_weekly_outcomes <- function(weekly_inc_outcome, point_est=0.5, opt){ pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - forecast_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -814,7 +818,8 @@ get_weekly_incid2 <- function(res_state, point_est=0.5, outcome_var="incidI", op pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = !!sym(outcome_var)) %>% mutate(forecast_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - forecast_date)/7))%>% mutate(target=sprintf(paste0("%d wk ahead inc ", outcome_short), ahead)) %>% @@ -1512,7 +1517,8 @@ process_sims <- function( scenario_id = scenario_id, scenario_name=scenario_name) %>% mutate(model_projection_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - model_projection_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -1568,7 +1574,8 @@ process_sims <- function( scenario_id = scenario_id, scenario_name=scenario_name) %>% mutate(model_projection_date=opt$forecast_date) %>% rename(target_end_date=time) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(ahead=round(as.numeric(target_end_date - model_projection_date)/7)) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% @@ -1596,7 +1603,8 @@ process_sims <- function( unnest(x) %>% pivot_wider(names_from = quantile, names_prefix = "quant_", values_from = outcome) %>% mutate(forecast_date=opt$forecast_date) %>% - dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + # dplyr::left_join(arrow::read_parquet("datasetup/usdata/state_fips_abbr.parquet")) %>% + dplyr::left_join(arrow::read_parquet(paste0(source_loc, "/datasetup/usdata/state_fips_abbr.parquet"))) %>% mutate(location=stringr::str_pad(location, width=2, side="left", pad="0")) %>% mutate(target = recode(outcome_name, "incidI"="inf", "incidC"="case", "incidH"="hosp", "incidD"="death")) %>% mutate(target = paste0("peak size ", target)) %>% From d8663b316181d0ec1fe0db820256456efd05a44c Mon Sep 17 00:00:00 2001 From: saraloo <45245630+saraloo@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:41:49 -0400 Subject: [PATCH 13/15] Merge main --- build/conda_environment.yml | 1 - build/local_install.R | 2 +- datasetup/build_US_setup.R | 1 - flepimop/R_packages/flepicommon/NAMESPACE | 1 - flepimop/R_packages/flepicommon/R/DataUtils.R | 58 ------------- .../flepiconfig/R/process_npi_list.R | 30 +++++++ flepimop/main_scripts/create_seeding_added.R | 1 + flepimop/main_scripts/inference_slot.R | 38 ++++++++- postprocessing/plot_predictions.R | 84 +++++++++---------- 9 files changed, 111 insertions(+), 105 deletions(-) diff --git a/build/conda_environment.yml b/build/conda_environment.yml index 24d3752d9..2f9d992a9 100644 --- a/build/conda_environment.yml +++ b/build/conda_environment.yml @@ -478,7 +478,6 @@ dependencies: - conda-forge/linux-64::r-readxl==1.4.1=r42h3ebcfa7_1 - conda-forge/noarch::r-reprex==2.0.2=r42hc72bb7e_1 - conda-forge/linux-64::r-tidyr==1.3.0=r42h38f115c_0 - - conda-forge/noarch::r-tigris==2.0.1=r42hc72bb7e_0 - conda-forge/noarch::r-waldo==0.4.0=r42hc72bb7e_1 - conda-forge/noarch::r-broom==1.0.3=r42hc72bb7e_0 - conda-forge/linux-64::r-gdtools==0.3.0=r42he0ce631_0 diff --git a/build/local_install.R b/build/local_install.R index 8381977fa..1ef0cd924 100644 --- a/build/local_install.R +++ b/build/local_install.R @@ -8,7 +8,7 @@ local({r <- getOption("repos") library(devtools) -install.packages(c("covidcast","data.table","vroom","dplyr"), quiet=TRUE, dependencies = TRUE) +install.packages(c("covidcast","data.table","vroom","dplyr"), quiet=TRUE) # devtools::install_github("hrbrmstr/cdcfluview") # To run if operating in the container ----- diff --git a/datasetup/build_US_setup.R b/datasetup/build_US_setup.R index 0ba217a5e..952ca73e7 100644 --- a/datasetup/build_US_setup.R +++ b/datasetup/build_US_setup.R @@ -70,7 +70,6 @@ state_level <- ifelse(!is.null(config$subpop_setup$state_level) && config$subpop # tidycensus::census_api_key(key = census_key) - filterUSPS <- c("WY","VT","DC","AK","ND","SD","DE","MT","RI","ME","NH","HI","ID","WV","NE","NM", "KS","NV","MS","AR","UT","IA","CT","OK","OR","KY","LA","AL","SC","MN","CO","WI", "MD","MO","IN","TN","MA","AZ","WA","VA","NJ","MI","NC","GA","OH","IL","PA","NY","FL","TX","CA") diff --git a/flepimop/R_packages/flepicommon/NAMESPACE b/flepimop/R_packages/flepicommon/NAMESPACE index 276edf955..3dc3c78d9 100644 --- a/flepimop/R_packages/flepicommon/NAMESPACE +++ b/flepimop/R_packages/flepicommon/NAMESPACE @@ -17,7 +17,6 @@ export(fix_negative_counts_single_subpop) export(get_CSSE_US_data) export(get_CSSE_US_matchGlobal_data) export(get_CSSE_global_data) -export(get_USAFacts_data) export(get_covidcast_data) export(get_groundtruth_from_source) export(load_config) diff --git a/flepimop/R_packages/flepicommon/R/DataUtils.R b/flepimop/R_packages/flepicommon/R/DataUtils.R index 26cb4f6b9..b059ac88a 100755 --- a/flepimop/R_packages/flepicommon/R/DataUtils.R +++ b/flepimop/R_packages/flepicommon/R/DataUtils.R @@ -426,64 +426,6 @@ aggregate_counties_to_state <- function(df, state_fips){ } -##' -##' Pull case and death count data from USAFacts -##' -##' Pulls the USAFacts cumulative case count and death data. Calculates incident counts. -##' USAFacts does not include data for all the territories (aka island areas). These data are pulled from NYTimes. -##' -##' Returned data preview: -##' tibble [352,466 × 7] (S3: grouped_df/tbl_df/tbl/data.frame) -##' $ FIPS : chr [1:352466] "00001" "00001" "00001" "00001" ... -##' $ source : chr [1:352466] "NY" "NY" "NY" "NY" ... -##' $ Update : Date[1:352466], format: "2020-01-22" "2020-01-23" ... -##' $ Confirmed : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' $ Deaths : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidI : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' $ incidDeath : num [1:352466] 0 0 0 0 0 0 0 0 0 0 ... -##' -##' @param case_data_filename Filename where case data are stored -##' @param death_data_filename Filename where death data are stored -##' @param incl_unassigned Includes data unassigned to counties (default is FALSE) -##' @return the case and deaths data frame -##' -##' -##' @export -##' -get_USAFacts_data <- function(case_data_filename = "data/case_data/USAFacts_case_data.csv", - death_data_filename = "data/case_data/USAFacts_death_data.csv", - incl_unassigned = FALSE){ - - USAFACTS_CASE_DATA_URL <- "https://usafactsstatic.blob.core.windows.net/public/data/covid-19/covid_confirmed_usafacts.csv" - USAFACTS_DEATH_DATA_URL <- "https://usafactsstatic.blob.core.windows.net/public/data/covid-19/covid_deaths_usafacts.csv" - usafacts_case <- download_USAFacts_data(case_data_filename, USAFACTS_CASE_DATA_URL, "Confirmed", incl_unassigned) - usafacts_death <- download_USAFacts_data(death_data_filename, USAFACTS_DEATH_DATA_URL, "Deaths", incl_unassigned) - - usafacts_data <- dplyr::full_join(usafacts_case, usafacts_death) - usafacts_data <- dplyr::select(usafacts_data, Update, source, FIPS, Confirmed, Deaths) - usafacts_data <- rbind(usafacts_data, get_islandareas_data()) # Append island areas - usafacts_data <- dplyr::arrange(usafacts_data, source, FIPS, Update) - - # Create columns incidI and incidDeath - usafacts_data <- dplyr::group_modify( - dplyr::group_by( - usafacts_data, - FIPS - ), - function(.x,.y){ - .x$incidI = c(.x$Confirmed[1],diff(.x$Confirmed)) - .x$incidDeath = c(.x$Deaths[1],diff(.x$Deaths,)) - return(.x) - } - ) - - # Fix incidence counts that go negative and NA values or missing dates - usafacts_data <- fix_negative_counts(usafacts_data, "Confirmed", "incidI") - usafacts_data <- fix_negative_counts(usafacts_data, "Deaths", "incidDeath") - - return(usafacts_data) -} - ##' diff --git a/flepimop/R_packages/flepiconfig/R/process_npi_list.R b/flepimop/R_packages/flepiconfig/R/process_npi_list.R index b1ea1512a..1b00920c4 100644 --- a/flepimop/R_packages/flepiconfig/R/process_npi_list.R +++ b/flepimop/R_packages/flepiconfig/R/process_npi_list.R @@ -16,6 +16,36 @@ NULL +load_geodata_file <- function(filename, + geoid_len = 0, + geoid_pad = "0", + state_name = TRUE) { + + if(!file.exists(filename)){stop(paste(filename,"does not exist in",getwd()))} + geodata <- readr::read_csv(filename) %>% + dplyr::mutate(geoid = as.character(geoid)) + + if (!("geoid" %in% names(geodata))) { + stop(paste(filename, "does not have a column named geoid")) + } + + if (geoid_len > 0) { + geodata$geoid <- stringr::str_pad(geodata$geoid, geoid_len, pad = geoid_pad) + } + + if(state_name) { + utils::data(fips_us_county, package = "flepicommon") # arrow::read_parquet("datasetup/usdata/fips_us_county.parquet") + geodata <- fips_us_county %>% + dplyr::distinct(state, state_name) %>% + dplyr::rename(USPS = state) %>% + dplyr::rename(state = state_name) %>% + dplyr::mutate(state = dplyr::recode(state, "U.S. Virgin Islands" = "Virgin Islands")) %>% + dplyr::right_join(geodata) + } + + return(geodata) +} + ##' find_truncnorm_mean_parameter ##' ##' Convenience function that estimates the mean value for a truncnorm distribution given a, b, and sd that will have the expected value of the input mean. diff --git a/flepimop/main_scripts/create_seeding_added.R b/flepimop/main_scripts/create_seeding_added.R index 0ae4b1462..25637e497 100644 --- a/flepimop/main_scripts/create_seeding_added.R +++ b/flepimop/main_scripts/create_seeding_added.R @@ -44,6 +44,7 @@ library(purrr) option_list <- list( optparse::make_option(c("-c", "--config"), action = "store", default = Sys.getenv("CONFIG_PATH"), type = "character", help = "path to the config file"), + optparse::make_option(c("-p", "--flepi_path"), action="store", type='character', help="path to the flepiMoP directory", default = Sys.getenv("FLEPI_PATH", "flepiMoP/")), optparse::make_option(c("-k", "--keep_all_seeding"), action="store",default=TRUE,type='logical',help="Whether to filter away seeding prior to the start date of the simulation.") ) opt <- optparse::parse_args(optparse::OptionParser(option_list = option_list)) diff --git a/flepimop/main_scripts/inference_slot.R b/flepimop/main_scripts/inference_slot.R index a0bb3610a..19f2cf408 100644 --- a/flepimop/main_scripts/inference_slot.R +++ b/flepimop/main_scripts/inference_slot.R @@ -52,9 +52,12 @@ option_list = list( optparse::make_option(c("-H","--save_hosp"), action = "store", default = Sys.getenv("SAVE_HOSP", TRUE), type = 'logical', help = 'Should the HOSP output files be saved for each iteration'), optparse::make_option(c("-M", "--memory_profiling"), action = "store", default = Sys.getenv("FLEPI_MEM_PROFILE", FALSE), type = 'logical', help = 'Should the memory profiling be run during iterations'), optparse::make_option(c("-P", "--memory_profiling_iters"), action = "store", default = Sys.getenv("FLEPI_MEM_PROF_ITERS", 100), type = 'integer', help = 'If doing memory profiling, after every X iterations run the profiler'), - optparse::make_option(c("-g", "--subpop_len"), action="store", default=Sys.getenv("SUBPOP_LENGTH", 5), type='integer', help = "number of digits in subpop") + optparse::make_option(c("-g", "--subpop_len"), action="store", default=Sys.getenv("SUBPOP_LENGTH", 5), type='integer', help = "number of digits in subpop"), + optparse::make_option(c("-a", "--incl_aggr_likelihood"), action = "store", default = Sys.getenv("INCL_AGGR_LIKELIHOOD", TRUE), type = 'logical', help = 'Should the likelihood be calculated with the aggregate estiamtes.') ) + + parser=optparse::OptionParser(option_list=option_list) opt = optparse::parse_args(parser) @@ -90,6 +93,13 @@ if (opt$config == ""){ } config = flepicommon::load_config(opt$config) + +if (!is.null(config$inference$incl_aggr_likelihood)){ + print("Using config option for `incl_aggr_likelihood`.") + opt$incl_aggr_likelihood <- config$inference$incl_aggr_likelihood +} + + ## Check for errors in config --------------------------------------------------------------------- ## seeding section @@ -262,6 +272,20 @@ if (config$inference$do_inference){ dplyr::right_join(tidyr::expand_grid(subpop = unique(.$subpop), date = unique(.$date))) %>% dplyr::mutate_if(is.numeric, dplyr::coalesce, 0) + # add aggregate groundtruth to the obs data for the likelihood calc + if (opt$incl_aggr_likelihood){ + obs <- obs %>% + dplyr::bind_rows( + obs %>% + dplyr::select(date, where(is.numeric)) %>% + dplyr::group_by(date) %>% + summarise(across(everything(), sum)) %>% # no likelihood is calculated for time periods with missing data for any subpop + mutate(source = "Total", + FIPS = "Total", + geoid = "Total") + ) + } + subpopnames <- unique(obs[[obs_subpop]]) @@ -606,6 +630,18 @@ for(seir_modifiers_scenario in seir_modifiers_scenarios) { if (config$inference$do_inference){ sim_hosp <- flepicommon::read_file_of_type(gsub(".*[.]","",this_global_files[['hosp_filename']]))(this_global_files[['hosp_filename']]) %>% dplyr::filter(time >= min(obs$date),time <= max(obs$date)) + + # add aggregate groundtruth to the obs data for the likelihood calc + if (opt$incl_aggr_likelihood){ + sim_hosp <- sim_hosp %>% + dplyr::bind_rows( + sim_hosp %>% + dplyr::select(-tidyselect::all_of(obs_nodename), -starts_with("date")) %>% + dplyr::group_by(time) %>% + dplyr::summarise(dplyr::across(tidyselect::everything(), sum)) %>% # no likelihood is calculated for time periods with missing data for any subpop + dplyr::mutate(!!obs_nodename := "Total") + ) + } lhs <- unique(sim_hosp[[obs_subpop]]) rhs <- unique(names(data_stats)) diff --git a/postprocessing/plot_predictions.R b/postprocessing/plot_predictions.R index 5397966f5..037a0b957 100644 --- a/postprocessing/plot_predictions.R +++ b/postprocessing/plot_predictions.R @@ -13,13 +13,13 @@ center_line_var <- ifelse(point_est==0.5, "point", "point-mean") proj_data <- data_comb -#### Which valid locations are missing from our submission? +#### Which valid locations are missing from our submission? # locs <- read_csv("https://raw.githubusercontent.com/reichlab/covid19-forecast-hub/master/data-locations/locations.csv") # mismatched <- unique(proj_data$location)[which(!(unique(proj_data$location) %in% locs$location))] # missing_from_fc <- unique(locs$location)[which(!(locs$location %in% unique(proj_data$location)))] -# -# locs %>% filter(location %in% missing_from_fc) +# +# locs %>% filter(location %in% missing_from_fc) # STATE DATA -------------------------------------------------------------- @@ -38,7 +38,7 @@ state_cw <- fips_us_county %>% # GROUND TRUTH ------------------------------------------------------------ -gt_data <- gt_data %>% +gt_data <- gt_data %>% mutate(time = lubridate::as_date(time)) %>% mutate(date = time) colnames(gt_data) <- gsub("incidI", "incidC", colnames(gt_data)) gt_outcomes <- outcomes_[outcomes_ != "I" & sapply(X = paste0("incid", outcomes_), FUN = function(x=X, y) any(grepl(pattern = x, x = y)), y = colnames(gt_data)) ] @@ -67,16 +67,16 @@ if (any(outcomes_time_=="weekly")) { mutate(agestrat="age0to130") %>% rename(outcome = outcome_name, value = outcome) %>% filter(outcome %in% paste0("incid", weekly_cum_outcomes_)), - obs_data = gt_data_2, + obs_data = gt_data_2, gt_cum_vars = paste0("cum", outcomes_gt_[outcomes_cumfromgt_gt_]), # variables to get cum from GT forecast_date = lubridate::as_date(forecast_date), aggregation="week", - loc_column = "USPS", + loc_column = "USPS", use_obs_data = use_obs_data_forcum) %>% rename(outcome_name = outcome, outcome = value) %>% select(-agestrat) - - gt_data_st_week <- gt_data_st_week %>% + + gt_data_st_week <- gt_data_st_week %>% bind_rows(gt_data_st_weekcum) } gt_cl <- gt_cl %>% bind_rows(gt_data_st_week %>% mutate(time_aggr = "weekly")) @@ -93,11 +93,11 @@ if (any(outcomes_time_=="daily")) { mutate(agestrat="age0to130") %>% rename(outcome = outcome_name, value = outcome) %>% filter(outcome %in% paste0("incid", daily_cum_outcomes_)), - obs_data = gt_data_2, + obs_data = gt_data_2, gt_cum_vars = paste0("cum", outcomes_gt_[outcomes_cumfromgt_gt_]), # variables to get cum from GT forecast_date = lubridate::as_date(forecast_date), aggregation="day", - loc_column = "USPS", + loc_column = "USPS", use_obs_data = use_obs_data_forcum) %>% rename(outcome_name = outcome, outcome = value) %>% select(-agestrat) @@ -117,12 +117,12 @@ gt_cl <- gt_cl %>% rename(date = time) # inc_dat_st_vars <- inc_dat_st_vars %>% filter(date != max(date)) # } -dat_st_cl2 <- gt_cl %>% +dat_st_cl2 <- gt_cl %>% select(date, USPS, target = outcome_name, time_aggr, value = outcome) %>% mutate(incid_cum = ifelse(grepl("inc", target), "inc", "cum")) %>% mutate(aggr_target = !grepl('_', target)) %>% mutate(outcome = substr(gsub("cum|incid", "", target), 1,1)) %>% - mutate(pre_gt_end = date<=validation_date) + mutate(pre_gt_end = date<=validation_date) @@ -134,7 +134,7 @@ dat_st_cl2 <- gt_cl %>% forecast_st <- proj_data %>% filter(nchar(location)==2 & (quantile %in% sort(unique(c(quant_values, 0.5))) | is.na(quantile))) %>% - left_join(state_cw, by = c("location")) + left_join(state_cw, by = c("location")) # filter out incid or cum if (!plot_incid) { forecast_st <- forecast_st %>% filter(!grepl(" inc ", target)) } @@ -150,12 +150,12 @@ if(any(outcomes_cum_)){ forecast_st <- forecast_st %>% filter(grepl(paste0(c(paste0("inc ", outcomes_name), cum_outcomes_name), collapse = "|"), target)) # create cat variables -forecast_st_plt <- forecast_st %>% +forecast_st_plt <- forecast_st %>% mutate(incid_cum = ifelse(grepl("inc ", target), "inc", "cum")) %>% mutate(outcome = stringr::word(target, 5)) %>% mutate(outcome = recode(outcome, "inf"="I", "case"="C", "hosp"="H", "death"="D")) %>% - dplyr::mutate(quantile_cln = ifelse(!is.na(quantile), paste0("q", paste0(as.character(quantile*100), "%")), - ifelse(type=="point-mean", paste0("mean"), + dplyr::mutate(quantile_cln = ifelse(!is.na(quantile), paste0("q", paste0(as.character(quantile*100), "%")), + ifelse(type=="point-mean", paste0("mean"), ifelse(type=="point", paste0("median"), NA)))) %>% mutate(target_type = paste0(incid_cum, outcome)) @@ -173,12 +173,12 @@ if(center_line == "mean"){ forecast_st_plt <- forecast_st_plt %>% mutate(quantile_cln = gsub("q50%", "ctr", quantile_cln)) } -forecast_st_plt <- forecast_st_plt %>% +forecast_st_plt <- forecast_st_plt %>% select(scenario_name, scenario_id, target = target_type, incid_cum, outcome, date = target_end_date, USPS, quantile_cln, value) %>% pivot_wider(names_from = quantile_cln, values_from = value) %>% mutate(type = "projection") %>% - full_join(pltdat_truth %>% - mutate(type = "gt", scenario_name = ifelse(pre_gt_end, "gt-pre-projection", "gt-post-projection")) %>% + full_join(pltdat_truth %>% + mutate(type = "gt", scenario_name = ifelse(pre_gt_end, "gt-pre-projection", "gt-post-projection")) %>% select(date, USPS, target = target_type, incid_cum, type, scenario_name, ctr=gt)) %>% filter(date >= trunc_date & date <= sim_end_date) @@ -201,15 +201,15 @@ stplot_fname_nosqrt <- paste0(stplot_fname, ".pdf") pdf(stplot_fname_nosqrt, width=7, height=11) for(usps in unique(forecast_st_plt$USPS)){ - + print(paste0("Plotting: ", usps)) cols_tmp <- cols[names(cols) %in% unique(forecast_st_plt$scenario_name)] - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("inc","",unique(forecast_st_plt$target)),outcomes_)]), " incident ", gsub("inc","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - inc_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + inc_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="inc") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -232,15 +232,15 @@ for(usps in unique(forecast_st_plt$USPS)){ theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) plot(inc_st_plt) - - + + if (plot_cum) { - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("cum","",unique(forecast_st_plt$target)),outcomes_)]), " cumulative ", gsub("cum","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - cum_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + cum_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="cum") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -261,7 +261,7 @@ for(usps in unique(forecast_st_plt$USPS)){ labeller = as_labeller(target_labs)) + theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) - + plot(cum_st_plt) } } @@ -273,15 +273,15 @@ scale_y_funct <- scale_y_sqrt pdf(stplot_fname_sqrt, width=7, height=11) for(usps in unique(forecast_st_plt$USPS)){ - + print(paste0("Plotting: ", usps)) cols_tmp <- cols[names(cols) %in% unique(forecast_st_plt$scenario_name)] - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("inc","",unique(forecast_st_plt$target)),outcomes_)]), " incident ", gsub("inc","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - inc_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + inc_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="inc") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -303,14 +303,14 @@ for(usps in unique(forecast_st_plt$USPS)){ theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) plot(inc_st_plt) - + if (plot_cum) { - + target_labs <- paste0(str_to_title(outcomes_time_[match(gsub("cum","",unique(forecast_st_plt$target)),outcomes_)]), " cumulative ", gsub("cum","",unique(forecast_st_plt$target))) names(target_labs) <- unique(forecast_st_plt$target) - - cum_st_plt <- forecast_st_plt %>% - filter(USPS == usps) %>% + + cum_st_plt <- forecast_st_plt %>% + filter(USPS == usps) %>% filter(incid_cum=="cum") %>% mutate(scenario_name = factor(scenario_name)) %>% ggplot(aes(x = date)) + @@ -331,7 +331,7 @@ for(usps in unique(forecast_st_plt$USPS)){ labeller = as_labeller(target_labs)) + theme(legend.position = "bottom", legend.text = element_text(size=10), axis.text.x = element_text(size=6, angle = 45)) - + plot(cum_st_plt) } } From 5a993d031ed7794e644e3752c2a2d1afa258f3d6 Mon Sep 17 00:00:00 2001 From: saraloo <45245630+saraloo@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:50:15 -0400 Subject: [PATCH 14/15] small typo --- flepimop/main_scripts/create_seeding_added.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flepimop/main_scripts/create_seeding_added.R b/flepimop/main_scripts/create_seeding_added.R index c62e5360e..25637e497 100644 --- a/flepimop/main_scripts/create_seeding_added.R +++ b/flepimop/main_scripts/create_seeding_added.R @@ -268,8 +268,7 @@ geodata <- flepicommon::load_geodata_file( file.path(config$subpop_setup$geodata), 5, "0", - TRUE, - flepi_path = opt$flepi_path + TRUE ) all_subpop <- geodata[[config$subpop_setup$subpop]] From 027848e1e840f76b97b9cdd01c79464491f98664 Mon Sep 17 00:00:00 2001 From: saraloo <45245630+saraloo@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:03:41 -0400 Subject: [PATCH 15/15] remove flepipath in geodata file --- flepimop/R_packages/flepiconfig/R/process_npi_list.R | 3 +-- flepimop/R_packages/flepiconfig/tests/testthat/test-gen_npi.R | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/flepimop/R_packages/flepiconfig/R/process_npi_list.R b/flepimop/R_packages/flepiconfig/R/process_npi_list.R index f4ce40bed..1b00920c4 100644 --- a/flepimop/R_packages/flepiconfig/R/process_npi_list.R +++ b/flepimop/R_packages/flepiconfig/R/process_npi_list.R @@ -19,8 +19,7 @@ NULL load_geodata_file <- function(filename, geoid_len = 0, geoid_pad = "0", - state_name = TRUE, - flepi_path = "flepiMoP") { + state_name = TRUE) { if(!file.exists(filename)){stop(paste(filename,"does not exist in",getwd()))} geodata <- readr::read_csv(filename) %>% diff --git a/flepimop/R_packages/flepiconfig/tests/testthat/test-gen_npi.R b/flepimop/R_packages/flepiconfig/tests/testthat/test-gen_npi.R index 5416e86d8..947d11b5b 100644 --- a/flepimop/R_packages/flepiconfig/tests/testthat/test-gen_npi.R +++ b/flepimop/R_packages/flepiconfig/tests/testthat/test-gen_npi.R @@ -10,7 +10,7 @@ generate_processed <- function(geodata_path, outcomes_path ){ temp_name <- tempfile() - geodata <- load_geodata_file(filename = geodata_path, flepi_path = "../flepiMoP") + geodata <- load_geodata_file(filename = geodata_path) npi_dat <- process_npi_shub(intervention_path = intervention_path, geodata)