Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
3c4a17e
based on env var, read in subset of data and show subset of targets
nmdefries Mar 24, 2023
d9165a5
set dashboard type param in makefile
nmdefries Mar 24, 2023
a88b909
set shell and working dir for testing convenience
nmdefries Mar 24, 2023
b553e3d
download predictions cards obj too
nmdefries Mar 24, 2023
0196aca
match ensemble spec in ui
nmdefries Mar 24, 2023
fe4f6b7
only load relevant data files; select/rename fields in one step
nmdefries Mar 24, 2023
3f178af
programmatically create archive and current output panels
nmdefries Mar 28, 2023
84e83e0
render all output for both current and archive tabs
nmdefries Mar 28, 2023
3555876
switch target var selection based on tab
nmdefries Mar 28, 2023
29bbbb4
fetch all data together for convenience
nmdefries Mar 28, 2023
0b0d167
specify archive vs current output via suffix via input$tabset
nmdefries Mar 28, 2023
5c604ab
make _archive html tags match formatting
nmdefries Mar 28, 2023
241343e
styler
nmdefries Mar 30, 2023
2ca6ff3
remove dash env var from makefile
nmdefries Mar 30, 2023
6d6322b
use dash suffix to indicate output vars
nmdefries Mar 30, 2023
c00b0c9
remove dash env var from makefile
nmdefries Mar 30, 2023
238f52d
don't try to reset target var choices if tab is "about"
nmdefries Mar 31, 2023
38d1b33
Merge branch 'dev' into ndefries/new-archive-tab
nmdefries Mar 31, 2023
585b6c1
don't combine input dfs
nmdefries Mar 24, 2023
c6f21a7
load only relevant data when target var is selected
nmdefries Mar 28, 2023
308707c
use initial values to set forecasterChoices
nmdefries Mar 31, 2023
3d9285a
styler
nmdefries Mar 31, 2023
9a0d639
make colorPalette generation not depend on initial data load
nmdefries Mar 31, 2023
80dcf5d
set initial PREV_TARGET to hosp
nmdefries Mar 31, 2023
740c2a0
styler
nmdefries Mar 31, 2023
9cd9989
match fallback behavior to s3 data loader; define some target var con…
nmdefries Mar 31, 2023
ad91cab
bugfix: check if *all* aheads possible
nmdefries Mar 31, 2023
5d96d41
Merge branch 'ndefries/new-archive-tab' into ndefries/load-data-by-ta…
nmdefries Mar 31, 2023
3a48d26
include coverage cols + new names in keep cols vec for clarity
nmdefries Apr 5, 2023
86fe3d1
make select panel condition only check beginning of tab name
nmdefries Apr 6, 2023
7bbb8e0
styler
nmdefries Apr 6, 2023
6728a0a
Merge pull request #253 from cmu-delphi/ndefries/new-archive-tab
nmdefries Apr 6, 2023
15d7f0d
factor out score explanation display; display on tab switch
nmdefries Apr 6, 2023
712ae55
reduce duplication in tab-changing logic
nmdefries Apr 6, 2023
62e476e
set archive tab suffix in constants
nmdefries Apr 6, 2023
4202a75
allow week end date const to change with init target
nmdefries Apr 6, 2023
08a7116
do filtering on asof choices up front
nmdefries Apr 6, 2023
d75c55a
Merge pull request #258 from cmu-delphi/ndefries/score-explanations-o…
nmdefries Apr 6, 2023
788a449
let init_target set horizon display
nmdefries Apr 7, 2023
71e1fb4
explicitly set horizon info to hosp
nmdefries Apr 7, 2023
9d99fd8
cache s3contents and datafetcher fn
nmdefries Apr 7, 2023
bb1628f
Merge branch 'dev' into ndefries/load-data-by-targetvar
nmdefries Apr 7, 2023
2f3a15f
Merge pull request #257 from cmu-delphi/ndefries/load-data-by-targetvar
nmdefries Apr 7, 2023
65bc860
Merge pull request #259 from cmu-delphi/ndefries/hosp-starting-date-t…
nmdefries Apr 10, 2023
a02d845
set as-of date to most recent on init
nmdefries Apr 12, 2023
098a5ea
reference as_of_choices reactval to avoid re-sorting
nmdefries Apr 12, 2023
0ef2dbb
factor out tab-var combos; skip plot call if don't match
nmdefries Apr 12, 2023
7bb3bc9
increase priority of data-loading observe
nmdefries Apr 12, 2023
821f364
use dplyr if_else to set CURRENT_WEEK_END to date class
nmdefries Apr 12, 2023
dc4d538
styler
nmdefries Apr 12, 2023
1557ed4
return asof to being empty on init; define startup observe
nmdefries Apr 13, 2023
0762627
reuse global suffix var
nmdefries Apr 14, 2023
4ac4e05
styler
nmdefries Apr 14, 2023
b813799
Merge pull request #260 from cmu-delphi/ndefries/reduce-replots-on-va…
nmdefries Apr 14, 2023
c2d402a
remove and combine filter, select
nmdefries Mar 24, 2023
7258b33
clean up slow filters
nmdefries Apr 14, 2023
be55333
combine hosp aheads filters
nmdefries Apr 11, 2023
02f7946
drop unnecessary date cast
nmdefries Apr 12, 2023
23482eb
styler
nmdefries Apr 17, 2023
50fed17
define PREV_TARGET as session global to not leak behavior
nmdefries Apr 17, 2023
dcfcde4
use character-specific in operator for vectorized in checks
nmdefries Apr 14, 2023
ffb4484
Merge pull request #267 from cmu-delphi/ndefries/prev-target-session-var
nmdefries Apr 18, 2023
9e58614
put date conversion back; keep asofchoices from duplicating most rece…
nmdefries Apr 19, 2023
b6ef12a
use data.table weekday func
nmdefries Apr 19, 2023
49c1da0
comment weekday and rename changes
nmdefries Apr 19, 2023
49896af
Merge pull request #266 from cmu-delphi/ndefries/cleanup-dplyr-in-usage
nmdefries Apr 19, 2023
9e0a263
show main plot when changing score to/from coverage; update truth data
nmdefries Apr 24, 2023
eea79d6
regenerate truth plot if aggregating over shared states
nmdefries Apr 24, 2023
3f42eda
use var with same testing logic
nmdefries Apr 25, 2023
5178747
Merge pull request #271 from cmu-delphi/ndefries/update-truth-on-fore…
nmdefries Apr 26, 2023
3818144
Merge pull request #270 from cmu-delphi/ndefries/show-us-coverage-plot
nmdefries Apr 26, 2023
12059e8
chore: release 7.0.0
nmdefries Apr 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 6.1.0
current_version = 7.0.0
commit = False
tag = False

Expand Down
6 changes: 4 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: forecasteval
Title: Forecast Evaluation Dashboard
Version: 6.1.0
Version: 7.0.0
Authors@R: c(person("Kate", "Harwood", email = "[email protected]", role = "cre"),
person("Chris", "Scott", role = "ctb"),
person("Jed", "Grabman", role = "ctb"))
Expand All @@ -24,7 +24,9 @@ Imports:
covidcast,
stringr,
markdown,
memoise
memoise,
purrr,
data.table
Suggests:
styler,
lintr,
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
SHELL:=/bin/bash
PWD=$(shell pwd)

.DEFAULT_GOAL:=build
S3_URL=https://forecast-eval.s3.us-east-2.amazonaws.com
S3_BUCKET=s3://forecast-eval
Expand All @@ -10,7 +13,7 @@ r_build:
%.rds: dist
test -f dist/$@ || curl -o dist/$@ $(S3_URL)/$@

pull_data: score_cards_state_deaths.rds score_cards_state_cases.rds score_cards_nation_cases.rds score_cards_nation_deaths.rds score_cards_state_hospitalizations.rds score_cards_nation_hospitalizations.rds datetime_created_utc.rds
pull_data: score_cards_state_deaths.rds score_cards_state_cases.rds score_cards_nation_cases.rds score_cards_nation_deaths.rds score_cards_state_hospitalizations.rds score_cards_nation_hospitalizations.rds datetime_created_utc.rds predictions_cards.rds

dist:
mkdir $@
Expand Down
98 changes: 56 additions & 42 deletions app/R/data.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ library(aws.s3)
shinyOptions(cache = cachem::cache_mem(max_size = 1000 * 1024^2, evict = "lru"))
cache <- getShinyOption("cache")

# Since covidcast data updates about once a day. Add date arg to
# Since covidcast data updates about once a day, add date arg to
# covidcast_signal so caches aren't used after that.
covidcast_signal_mem <- function(..., date = Sys.Date()) {
return(covidcast_signal(...))
Expand Down Expand Up @@ -67,65 +67,74 @@ getCreationDate <- function(loadFile) {
}


getAllData <- function(loadFile) {
dfStateCases <- loadFile("score_cards_state_cases.rds")
dfStateDeaths <- loadFile("score_cards_state_deaths.rds")
dfStateHospitalizations <- loadFile("score_cards_state_hospitalizations.rds")
dfNationCases <- loadFile("score_cards_nation_cases.rds")
dfNationDeaths <- loadFile("score_cards_nation_deaths.rds")
dfNationHospitalizations <- loadFile("score_cards_nation_hospitalizations.rds")
getAllData <- function(loadFile, targetVariable) {
df <- switch(targetVariable,
"Deaths" = bind_rows(
loadFile("score_cards_state_deaths.rds"),
loadFile("score_cards_nation_deaths.rds")
),
"Cases" = bind_rows(
loadFile("score_cards_state_cases.rds"),
loadFile("score_cards_nation_cases.rds")
),
"Hospitalizations" = bind_rows(
loadFile("score_cards_state_hospitalizations.rds"),
loadFile("score_cards_nation_hospitalizations.rds")
)
)

# Pick out expected columns only
covCols <- paste0("cov_", COVERAGE_INTERVALS)
expectedCols <- c(
# The names of the `covCols` elements become the new names of those columns
# when we use this vector in the `select` below.
covCols <- setNames(paste0("cov_", COVERAGE_INTERVALS), COVERAGE_INTERVALS)
keepCols <- c(
"ahead", "geo_value", "forecaster", "forecast_date",
"data_source", "signal", "target_end_date", "incidence_period",
"actual", "wis", "sharpness", "ae", "value_50",
covCols
)

df <- bind_rows(
dfStateCases %>% select(all_of(expectedCols)),
dfStateDeaths %>% select(all_of(expectedCols)),
dfStateHospitalizations %>% select(all_of(expectedCols)),
dfNationCases %>% select(all_of(expectedCols)),
dfNationDeaths %>% select(all_of(expectedCols)),
dfNationHospitalizations %>% select(all_of(expectedCols))
)
df <- df %>% rename(
"10" = cov_10, "20" = cov_20, "30" = cov_30,
"40" = cov_40, "50" = cov_50, "60" = cov_60, "70" = cov_70,
"80" = cov_80, "90" = cov_90, "95" = cov_95, "98" = cov_98
)
df <- select(df, all_of(keepCols))

return(df)
}

createS3DataLoader <- function() {
# Cached connection info
s3bucket <- getS3Bucket()
df <- data.frame()
s3DataFetcher <- createS3DataFactory(s3bucket)
s3Contents <- s3bucket[attr(s3bucket, "names", exact = TRUE)]

# Cached data
df_list <- list()
dataCreationDate <- as.Date(NA)

getRecentData <- function() {
newS3bucket <- getS3Bucket()
getRecentData <- function(targetVariable = TARGET_OPTIONS) {
targetVariable <- match.arg(targetVariable)

s3Contents <- s3bucket[attr(s3bucket, "names", exact = TRUE)]
newS3bucket <- getS3Bucket()
newS3Contents <- newS3bucket[attr(newS3bucket, "names", exact = TRUE)]
s3BucketHasChanged <- !identical(s3Contents, newS3Contents)

# Fetch new score data if contents of S3 bucket has changed (including file
# Fetch new data if contents of S3 bucket has changed (including file
# names, sizes, and last modified timestamps). Ignores characteristics of
# bucket and request, including bucket region, name, content type, request
# date, request ID, etc.
if (nrow(df) == 0 || !identical(s3Contents, newS3Contents)) {
# Save new data and new bucket connection info to vars in env of
# `getRecentDataHelper`. They persist between calls to `getRecentData` a
# la https://stackoverflow.com/questions/1088639/static-variables-in-r
# bucket and request, including bucket region, name, content type,
# request date, request ID, etc.
#
# Save new score data and new bucket connection info to vars in env of
# `createS3DataLoader`. They persist between calls to `getRecentData` a
# la https://stackoverflow.com/questions/1088639/static-variables-in-r
if (s3BucketHasChanged) {
s3bucket <<- newS3bucket
df <<- getAllData(createS3DataFactory(s3bucket))
dataCreationDate <<- getCreationDate(createS3DataFactory(s3bucket))
s3DataFetcher <<- createS3DataFactory(newS3bucket)
s3Contents <<- newS3Contents
}
if (s3BucketHasChanged ||
!(targetVariable %chin% names(df_list)) ||
nrow(df_list[[targetVariable]]) == 0) {
df_list[[targetVariable]] <<- getAllData(s3DataFetcher, targetVariable)
dataCreationDate <<- getCreationDate(s3DataFetcher)
}

return(list(df = df, dataCreationDate = dataCreationDate))
return(list(df_list = df_list, dataCreationDate = dataCreationDate))
}

return(getRecentData)
Expand All @@ -134,12 +143,17 @@ createS3DataLoader <- function() {

#' create a data loader with fallback data only
createFallbackDataLoader <- function() {
df <- getAllData(getFallbackData)
df_list <- list()
for (targetVariable in TARGET_OPTIONS) {
df_list[[targetVariable]] <- getAllData(getFallbackData, targetVariable)
}
dataCreationDate <- getCreationDate(getFallbackData)

dataLoader <- function() {
df
return(list(df_list = df_list, dataCreationDate = dataCreationDate))
}
dataLoader

return(dataLoader)
}


Expand Down
55 changes: 19 additions & 36 deletions app/R/data_manipulation.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ renameScoreCol <- function(filteredScoreDf, scoreType, coverageInterval) {

filterOverAllLocations <- function(filteredScoreDf, scoreType, hasAsOfData = FALSE, filterDate) {
locationsIntersect <- list()
filteredScoreDf <- filteredScoreDf %>% filter(!is.na(Score) | target_end_date >= filterDate)
filteredScoreDf <- filter(filteredScoreDf, !is.na(Score) | target_end_date >= filterDate)
# Create df with col for all locations across each unique date, ahead and forecaster combo
locationDf <- filteredScoreDf %>%
group_by(forecaster, target_end_date, ahead) %>%
summarize(location_list = paste(sort(unique(geo_value)), collapse = ","))
locationDf <- locationDf %>% filter(location_list != c("us"))
locationDf <- filter(locationDf, location_list != c("us"))
# Create a list containing each row's location list
locationList <- sapply(locationDf$location_list, function(x) strsplit(x, ","))
locationList <- lapply(locationList, function(x) x[x != "us"])
# Get the intersection of all the locations in these lists
locationsIntersect <- unique(Reduce(intersect, locationList))
filteredScoreDf <- filteredScoreDf %>% filter(geo_value %in% locationsIntersect)
filteredScoreDf <- filter(filteredScoreDf, geo_value %chin% locationsIntersect)
if (scoreType == "coverage") {
if (hasAsOfData) {
filteredScoreDf <- filteredScoreDf %>%
Expand Down Expand Up @@ -56,40 +56,23 @@ filterOverAllLocations <- function(filteredScoreDf, scoreType, hasAsOfData = FAL
# Only use weekly aheads for hospitalizations
# May change in the future
filterHospitalizationsAheads <- function(scoreDf) {
scoreDf["weekday"] <- weekdays(as.Date(scoreDf$target_end_date))
scoreDf <- scoreDf %>% filter(weekday == HOSPITALIZATIONS_TARGET_DAY)
days_list <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
# Make sure to use `data.table`'s `wday`; `lubridate` has a function of the same name.
scoreDf["weekday"] <- days_list[data.table::wday(as.Date(scoreDf$target_end_date, "%Y-%m-%d"))]
scoreDf <- filter(scoreDf, weekday == HOSPITALIZATIONS_TARGET_DAY)
scoreDf$ahead_group <- case_when(
scoreDf$ahead >= HOSPITALIZATIONS_OFFSET & scoreDf$ahead < 7 + HOSPITALIZATIONS_OFFSET ~ 1L,
scoreDf$ahead >= 7 + HOSPITALIZATIONS_OFFSET & scoreDf$ahead < 14 + HOSPITALIZATIONS_OFFSET ~ 2L,
scoreDf$ahead >= 14 + HOSPITALIZATIONS_OFFSET & scoreDf$ahead < 21 + HOSPITALIZATIONS_OFFSET ~ 3L,
scoreDf$ahead >= 21 + HOSPITALIZATIONS_OFFSET & scoreDf$ahead < 28 + HOSPITALIZATIONS_OFFSET ~ 4L,
TRUE ~ NA_integer_
)

oneAheadDf <- scoreDf %>%
filter(ahead >= HOSPITALIZATIONS_OFFSET) %>%
filter(ahead < 7 + HOSPITALIZATIONS_OFFSET) %>%
group_by(target_end_date, forecaster) %>%
filter(ahead == min(ahead)) %>%
mutate(ahead = HOSPITALIZATIONS_AHEAD_OPTIONS[1])

return(bind_rows(
scoreDf %>%
filter(ahead >= HOSPITALIZATIONS_OFFSET) %>%
filter(ahead < 7 + HOSPITALIZATIONS_OFFSET) %>%
group_by(target_end_date, forecaster) %>%
filter(ahead == min(ahead)) %>%
mutate(ahead = HOSPITALIZATIONS_AHEAD_OPTIONS[1]),
scoreDf %>%
filter(ahead >= 7 + HOSPITALIZATIONS_OFFSET) %>%
filter(ahead < 14 + HOSPITALIZATIONS_OFFSET) %>%
group_by(target_end_date, forecaster) %>%
filter(ahead == min(ahead)) %>%
mutate(ahead = HOSPITALIZATIONS_AHEAD_OPTIONS[2]),
scoreDf %>%
filter(ahead >= 14 + HOSPITALIZATIONS_OFFSET) %>%
filter(ahead < 21 + HOSPITALIZATIONS_OFFSET) %>%
group_by(target_end_date, forecaster) %>%
filter(ahead == min(ahead)) %>%
mutate(ahead = HOSPITALIZATIONS_AHEAD_OPTIONS[3]),
return(
scoreDf %>%
filter(ahead >= 21 + HOSPITALIZATIONS_OFFSET) %>%
filter(ahead < 28 + HOSPITALIZATIONS_OFFSET) %>%
group_by(target_end_date, forecaster) %>%
filter(!is.na(ahead_group)) %>%
group_by(target_end_date, forecaster, ahead_group) %>%
filter(ahead == min(ahead)) %>%
mutate(ahead = HOSPITALIZATIONS_AHEAD_OPTIONS[4])
))
mutate(ahead = HOSPITALIZATIONS_AHEAD_OPTIONS[ahead_group])
)
}
18 changes: 7 additions & 11 deletions app/R/exportScores.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,20 @@ exportScoresUI <- function(id = "exportScores") {
}

createExportScoresDataFrame <- function(scoreDf, targetVariable, scoreType, forecasters, loc, coverageInterval) {
signalFilter <- CASE_FILTER
if (targetVariable == "Deaths") {
signalFilter <- DEATH_FILTER
} else if (targetVariable == "Hospitalizations") {
signalFilter <- HOSPITALIZATIONS_FILTER
}
scoreDf <- filter(
scoreDf[[targetVariable]],
forecaster %chin% forecasters
)
scoreDf <- renameScoreCol(scoreDf, scoreType, coverageInterval)
scoreDf <- scoreDf %>%
filter(signal == signalFilter) %>%
filter(forecaster %in% forecasters)

if (loc == TOTAL_LOCATIONS || scoreType == "coverage") {
if (signalFilter == HOSPITALIZATIONS_FILTER) {
if (targetVariable == "Hospitalizations") {
scoreDf <- filterHospitalizationsAheads(scoreDf)
}
scoreDf <- filterOverAllLocations(scoreDf, scoreType)
return(scoreDf[[1]])
} else {
scoreDf <- scoreDf %>% filter(geo_value == tolower(loc))
scoreDf <- filter(scoreDf, geo_value == tolower(loc))
scoreDf <- scoreDf[c(
"ahead", "geo_value", "forecaster", "forecast_date",
"data_source", "target_end_date", "Score", "actual"
Expand Down
25 changes: 19 additions & 6 deletions app/global.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@ library(shiny)
library(shinyjs)
library(plotly)
library(tidyr)
library(purrr)
library(dplyr, warn.conflicts = FALSE)
library(lubridate)
library(viridis)
library(tsibble)
library(covidcast)
library(data.table)

appVersion <- "6.1.0"
appVersion <- "7.0.0"

COVERAGE_INTERVALS <- c("10", "20", "30", "40", "50", "60", "70", "80", "90", "95", "98")
DEATH_FILTER <- "deaths_incidence_num"
CASE_FILTER <- "confirmed_incidence_num"
CASES_DEATHS_TARGET_DAY <- "Saturday"
HOSPITALIZATIONS_FILTER <- "confirmed_admissions_covid_1d"
HOSPITALIZATIONS_TARGET_DAY <- "Wednesday"
TOTAL_LOCATIONS <- "Totaled Over States*"
AHEAD_OPTIONS <- c(1, 2, 3, 4)

INIT_SCORE_TYPE <- "wis"
INIT_TARGET <- "Hospitalizations"
TARGET_OPTIONS <- c("Deaths", "Cases", "Hospitalizations")

# Num days to offset the forecast week by
# Example: if HOSPITALIZATIONS_TARGET_DAY is Wednesday and HOSPITALIZATIONS_OFFSET is 2,
# ahead 1 has to have forecast date of Monday or earlier,
Expand All @@ -29,8 +32,18 @@ HOSPITALIZATIONS_AHEAD_OPTIONS <- c(
HOSPITALIZATIONS_OFFSET + 14, HOSPITALIZATIONS_OFFSET + 21
)

# Sets the previous target to be the same as the first one, Deaths
PREV_TARGET <- "Deaths"
CURRENT_TAB_SUFFIX <- ""
ARCHIVE_TAB_SUFFIX <- "_archive"


TARGET_VARS_BY_TAB <- list()
TARGET_VARS_BY_TAB[[paste0("evaluations", CURRENT_TAB_SUFFIX)]] <- c(
"Hospital Admissions" = "Hospitalizations"
)
TARGET_VARS_BY_TAB[[paste0("evaluations", ARCHIVE_TAB_SUFFIX)]] <- c(
"Incident Deaths" = "Deaths",
"Incident Cases" = "Cases"
)

# When RE_RENDER_TRUTH = TRUE
# summaryPlot will be called only to update TruthPlot
Expand Down
Loading