diff --git a/config.toml b/config.toml index 91cf26117..c63b3f4cd 100644 --- a/config.toml +++ b/config.toml @@ -38,29 +38,49 @@ relativeURLs = false weight = 2 [[menu.main]] parent = "covidcast" - name = "Map" + name = "Map Overview" url = "/covidcast" weight = 1 +[[menu.main]] + parent = "covidcast" + name = "Timelapse" + url = "/covidcast/timelapse" + weight = 2 +[[menu.main]] + parent = "covidcast" + name = "Top 10" + url = "/covidcast/top10" + weight = 3 +[[menu.main]] + parent = "covidcast" + name = "Single Region" + url = "/covidcast/single" + weight = 4 [[menu.main]] parent = "covidcast" name = "Surveys" url = "/covidcast/surveys" - weight = 2 + weight = 5 [[menu.main]] parent = "covidcast" name = "Survey Results" url = "/covidcast/survey-results" - weight = 3 + weight = 6 +[[menu.main]] + parent = "covidcast" + name = "Export Data" + url = "/covidcast/export" + weight = 7 [[menu.main]] parent = "covidcast" name = "Release Log" url = "/covidcast/release-log" - weight = 4 + weight = 8 [[menu.main]] parent = "covidcast" name = "Terms Of Use" url = "/covidcast/terms-of-use" - weight = 5 + weight = 9 [[menu.main]] identifier = "flu" name = "Flu and Other Diseases" diff --git a/content/blog/2020-09-21-forecast-demo.Rmd b/content/blog/2020-09-21-forecast-demo.Rmd index d10b13a81..b26c4315a 100644 --- a/content/blog/2020-09-21-forecast-demo.Rmd +++ b/content/blog/2020-09-21-forecast-demo.Rmd @@ -111,20 +111,16 @@ We evaluate the following four models: $$ \begin{aligned} -&\text{Cases:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) \\ -&\text{Cases + Facebook:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) \\ +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + \sum_{j=0}^2 \gamma_j h(F_{\ell,t-7j}) \\ -&\text{Cases + Google:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + \sum_{j=0}^2 \gamma_j h(G_{\ell,t-7j}) \\ -&\text{Cases + Facebook + Google:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + \sum_{j=0}^2 \gamma_j h(F_{\ell,t-7j}) + \sum_{j=0}^2 \tau_j h(G_{\ell,t-7j}). \end{aligned} @@ -134,14 +130,15 @@ Here $d=7$ or $d=14$, depending on the target value (number of days we predict ahead), and $h$ is a transformation to be specified later. -Informally, the first model bases its predictions of future case rates -on the following three features: +Informally, the first model, which we'll call the "Cases" model, +bases its predictions of future case rates on the following three features: current COVID-19 case rates, and those 1 and 2 weeks back. -The second model additionally incorporates the current Facebook signal, -and the Facebook signal from 1 and 2 weeks back. -The third model is exactly same but substitutes the Google signal -instead of the Facebook one. -Finally, the fourth model uses both Facebook and Google signals. +The second model, "Cases + Facebook", additionally incorporates the +current Facebook signal, and the Facebook signal from 1 and 2 weeks back. +The third model, "Cases + Google", is exactly the same but substitutes the +Google signal instead of the Facebook one. +Finally, the fourth model, "Cases + Facebook + Google", +uses both Facebook and Google signals. For each model, in order to make a forecast at time $t_0$ (to predict case rates at time $t_0+d$), we fit a linear model using least absolute deviations (LAD) regression, @@ -293,8 +290,8 @@ is much bigger but still below 0.01. test](https://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test) (for paired data, as we have here) is more popular, because it tends to be more powerful than the sign test. - Applied here, it does indeed give smaller p-values pretty much across the board. - However, it assumes symmetry of the distribution in question + Applied here, it does indeed give smaller p-values pretty much across the + board. However, it assumes symmetry of the distribution in question (in our case, the difference in scaled errors), whereas the sign test does not, and thus we show results from the latter. diff --git a/content/blog/2020-09-21-forecast-demo.html b/content/blog/2020-09-21-forecast-demo.html index 632638cf3..265b19e13 100644 --- a/content/blog/2020-09-21-forecast-demo.html +++ b/content/blog/2020-09-21-forecast-demo.html @@ -14,14 +14,14 @@ summary: | Building on our previous two posts (on our COVID-19 symptom surveys through Facebook and Google) - this post offers a deeper dive into empirical analysis, examining whether the - % CLI-in-community indicators from our two surveys can be used to improve + this post offers a deeper dive into empirical analysis, examining whether the + % CLI-in-community indicators from our two surveys can be used to improve the accuracy of short-term forecasts of county-level COVID-19 case rates. acknowledgements: | Delphi's forecasting effort involves many people from our - modeling team, from forecaster design, to implementation, to evaluation. The + modeling team, from forecaster design, to implementation, to evaluation. The broader insights on forecasting shared in this post certainly cannot be - attributable to Ryan's work alone, and are a reflection of the work carried out + attributable to Ryan's work alone, and are a reflection of the work carried out by all these team members. related: - 2020-09-18-google-survey @@ -120,20 +120,16 @@

Problem Setup

We evaluate the following four models:

\[ \begin{aligned} -&\text{Cases:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) \\ -&\text{Cases + Facebook:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) \\ +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + \sum_{j=0}^2 \gamma_j h(F_{\ell,t-7j}) \\ -&\text{Cases + Google:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + \sum_{j=0}^2 \gamma_j h(G_{\ell,t-7j}) \\ -&\text{Cases + Facebook + Google:} \\ -& h(Y_{\ell,t+d}) -\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + +h(Y_{\ell,t+d}) +&\approx \alpha + \sum_{j=0}^2 \beta_j h(Y_{\ell,t-7j}) + \sum_{j=0}^2 \gamma_j h(F_{\ell,t-7j}) + \sum_{j=0}^2 \tau_j h(G_{\ell,t-7j}). \end{aligned} @@ -141,14 +137,15 @@

Problem Setup

Here \(d=7\) or \(d=14\), depending on the target value (number of days we predict ahead), and \(h\) is a transformation to be specified later.

-

Informally, the first model bases its predictions of future case rates -on the following three features: +

Informally, the first model, which we’ll call the “Cases” model, +bases its predictions of future case rates on the following three features: current COVID-19 case rates, and those 1 and 2 weeks back. -The second model additionally incorporates the current Facebook signal, -and the Facebook signal from 1 and 2 weeks back. -The third model is exactly same but substitutes the Google signal -instead of the Facebook one. -Finally, the fourth model uses both Facebook and Google signals. +The second model, “Cases + Facebook”, additionally incorporates the +current Facebook signal, and the Facebook signal from 1 and 2 weeks back. +The third model, “Cases + Google”, is exactly the same but substitutes the +Google signal instead of the Facebook one. +Finally, the fourth model, “Cases + Facebook + Google”, +uses both Facebook and Google signals. For each model, in order to make a forecast at time \(t_0\) (to predict case rates at time \(t_0+d\)), we fit a linear model using least absolute deviations (LAD) regression, @@ -217,17 +214,17 @@

Forecasting Code

as.Date(max(time_value)), by = "day")) %>% ungroup() df = full_join(df, df_all, by = c("geo_value", "time_value")) - + # Group by geo value, sort rows by increasing time - df = df %>% group_by(geo_value) %>% arrange(time_value) - + df = df %>% group_by(geo_value) %>% arrange(time_value) + # Load over shifts, and add lag value or lead value for (shift in shifts) { fun = ifelse(shift < 0, lag, lead) varname = sprintf("value%+d", shift) df = mutate(df, !!varname := fun(value, n = abs(shift))) } - + # Ungroup and return return(ungroup(df)) } @@ -261,40 +258,40 @@

Forecasting Code

case_num = 200 geo_values = covidcast_signal("jhu-csse", "confirmed_cumulative_num", "2020-05-14", "2020-05-14") %>% - filter(value >= case_num) %>% pull(geo_value) + filter(value >= case_num) %>% pull(geo_value) # Fetch county-level Google and Facebook % CLI-in-community signals, and JHU # confirmed case incidence proportion start_day = "2020-04-11" end_day = "2020-09-01" g = covidcast_signal("google-survey", "smoothed_cli") %>% - filter(geo_value %in% geo_values) %>% - select(geo_value, time_value, value) -f = covidcast_signal("fb-survey", "smoothed_hh_cmnty_cli", + filter(geo_value %in% geo_values) %>% + select(geo_value, time_value, value) +f = covidcast_signal("fb-survey", "smoothed_hh_cmnty_cli", start_day, end_day) %>% - filter(geo_value %in% geo_values) %>% - select(geo_value, time_value, value) + filter(geo_value %in% geo_values) %>% + select(geo_value, time_value, value) c = covidcast_signal("jhu-csse", "confirmed_7dav_incidence_prop", start_day, end_day) %>% - filter(geo_value %in% geo_values) %>% + filter(geo_value %in% geo_values) %>% select(geo_value, time_value, value) -# Find "complete" counties, present in all three data signals at all times +# Find "complete" counties, present in all three data signals at all times geo_values_complete = intersect(intersect(g$geo_value, f$geo_value), c$geo_value) -# Filter to complete counties, transform the signals, append 1-2 week lags to +# Filter to complete counties, transform the signals, append 1-2 week lags to # all three, and also 1-2 week leads to case rates -lags = 1:2 * -7 +lags = 1:2 * -7 leads = 1:2 * 7 -g = g %>% filter(geo_value %in% geo_values_complete) %>% - mutate(value = trans(value * rescale_g)) %>% - append_shifts(shifts = lags) -f = f %>% filter(geo_value %in% geo_values_complete) %>% - mutate(value = trans(value * rescale_f)) %>% - append_shifts(shifts = lags) +g = g %>% filter(geo_value %in% geo_values_complete) %>% + mutate(value = trans(value * rescale_g)) %>% + append_shifts(shifts = lags) +f = f %>% filter(geo_value %in% geo_values_complete) %>% + mutate(value = trans(value * rescale_f)) %>% + append_shifts(shifts = lags) c = c %>% filter(geo_value %in% geo_values_complete) %>% - mutate(value = trans(value * rescale_c)) %>% + mutate(value = trans(value * rescale_c)) %>% append_shifts(shifts = c(lags, leads)) # Rename columns @@ -310,47 +307,47 @@

Forecasting Code

# Use quantgen for LAD regression (this package supports quantile regression and # more; you can find it on GitHub here: https://github.com/ryantibs/quantgen) -library(quantgen) +library(quantgen) res_list = vector("list", length = length(leads)) # Loop over lead, forecast dates, build models and record errors (warning: this # computation takes a while) -for (i in 1:length(leads)) { +for (i in 1:length(leads)) { lead = leads[i]; if (verbose) cat("***", lead, "***\n") - + # Create a data frame to store our forecast results. Code below populates its - # rows in a way that breaks from typical dplyr operations, done for efficiency - res_list[[i]] = z %>% - filter(between(time_value, as.Date(start_day) - min(lags) + lead, + # rows in a way that breaks from typical dplyr operations, done for efficiency + res_list[[i]] = z %>% + filter(between(time_value, as.Date(start_day) - min(lags) + lead, as.Date(end_day) - lead)) %>% select(geo_value, time_value) %>% - mutate(err0 = as.double(NA), err1 = as.double(NA), err2 = as.double(NA), - err3 = as.double(NA), err4 = as.double(NA), lead = lead) + mutate(err0 = as.double(NA), err1 = as.double(NA), err2 = as.double(NA), + err3 = as.double(NA), err4 = as.double(NA), lead = lead) valid_dates = unique(res_list[[i]]$time_value) - + for (k in 1:length(valid_dates)) { date = valid_dates[k]; if (verbose) cat(format(date), "... ") - + # Filter down to training set and test set z_tr = z %>% filter(between(time_value, date - lead - n, date - lead)) z_te = z %>% filter(time_value == date) inds = which(res_list[[i]]$time_value == date) - + # Create training and test responses y_tr = z_tr %>% pull(paste0("case+", lead)) y_te = z_te %>% pull(paste0("case+", lead)) - + # Strawman model if (verbose) cat("0") y_hat = z_te %>% pull(case) res_list[[i]][inds,]$err0 = abs(inv_trans(y_hat) - inv_trans(y_te)) - + # Cases only model if (verbose) cat("1") x_tr_case = z_tr %>% select(starts_with("case") & !contains("+")) x_te_case = z_te %>% select(starts_with("case") & !contains("+")) - x_tr = x_tr_case; x_te = x_te_case # For symmetry wrt what follows + x_tr = x_tr_case; x_te = x_te_case # For symmetry wrt what follows ok = complete.cases(x_tr, y_tr) if (sum(ok) > 0) { obj = quantile_lasso(as.matrix(x_tr[ok,]), y_tr[ok], tau = 0.5, @@ -358,7 +355,7 @@

Forecasting Code

y_hat = as.numeric(predict(obj, newx = as.matrix(x_te))) res_list[[i]][inds,]$err1 = abs(inv_trans(y_hat) - inv_trans(y_te)) } - + # Cases and Facebook model if (verbose) cat("2") x_tr_fb = z_tr %>% select(starts_with("fb")) @@ -386,7 +383,7 @@

Forecasting Code

y_hat = as.numeric(predict(obj, newx = as.matrix(x_te))) res_list[[i]][inds,]$err3 = abs(inv_trans(y_hat) - inv_trans(y_te)) } - + # Cases, Facebook, and Google model if (verbose) cat("4\n") x_tr = cbind(x_tr_case, x_tr_fb, x_tr_goog) @@ -401,7 +398,7 @@

Forecasting Code

} } -# Bind results over different leads into one big data frame, and save +# Bind results over different leads into one big data frame, and save res = do.call(rbind, res_list) save(list = ls(), file = "demo.rda") @@ -1036,8 +1033,8 @@

Wrap-Up

test (for paired data, as we have here) is more popular, because it tends to be more powerful than the sign test. -Applied here, it does indeed give smaller p-values pretty much across the board. -However, it assumes symmetry of the distribution in question +Applied here, it does indeed give smaller p-values pretty much across the +board. However, it assumes symmetry of the distribution in question (in our case, the difference in scaled errors), whereas the sign test does not, and thus we show results from the latter.↩︎

  • Delphi’s “production” forecasters are still based on relatively simple diff --git a/content/covidcast/_index.md b/content/covidcast/_index.md index c3c2d7e58..20a090b1f 100644 --- a/content/covidcast/_index.md +++ b/content/covidcast/_index.md @@ -1,6 +1,10 @@ --- title: COVIDcast -layout: covidcast_app description: COVIDcast tracks and forecasts the spread of COVID-19. By Carnegie Mellon's Delphi Research Group. +layout: covidcast_app +app_mode: overview +order: 1 +modeTitle: Map Overview +icon: solid/map heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg --- diff --git a/content/covidcast/export.md b/content/covidcast/export.md index db607385d..5be2cc2bc 100644 --- a/content/covidcast/export.md +++ b/content/covidcast/export.md @@ -3,5 +3,8 @@ title: COVIDCast Export Data linkTitle: Export Data description: Use COVIDcast data in your own analysis layout: covidcast_app +app_mode: export +order: 6 +icon: solid/download heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg --- diff --git a/content/covidcast/release-log/headless/v1.12.1.md b/content/covidcast/release-log/headless/v1.12.1.md new file mode 100644 index 000000000..493d5cf5c --- /dev/null +++ b/content/covidcast/release-log/headless/v1.12.1.md @@ -0,0 +1,10 @@ +--- +title: v1.12.1 +date: 2020-12-29 +--- + +Bugfix release for improved mobile experience + +#### Improvements + +- [#700](https://github.com/cmu-delphi/www-covidcast/pull/700) Improve mobile experience diff --git a/content/covidcast/single.md b/content/covidcast/single.md index 23ad81bb1..5f1ae272a 100644 --- a/content/covidcast/single.md +++ b/content/covidcast/single.md @@ -3,5 +3,8 @@ title: COVIDCast Region Details linkTitle: Region Details description: COVIDcast tracks and forecasts the spread of COVID-19. By Carnegie Mellon's Delphi Research Group. layout: covidcast_app +app_mode: single +order: 4 +icon: location-solid heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg --- diff --git a/content/covidcast/survey-results.md b/content/covidcast/survey-results.md index b0f78bc9c..79d6d4a27 100644 --- a/content/covidcast/survey-results.md +++ b/content/covidcast/survey-results.md @@ -4,5 +4,8 @@ linkTitle: Survey Results description: | In collaboration with Facebook Data for Good, along with a consortium of universities and public health officials, the Delphi Group at Carnegie Mellon University conducts research surveys to monitor the spread and impact of the COVID-19 pandemic in the United States. This survey is advertised through Facebook. It has run continuously since early April 2020. layout: covidcast_app +app_mode: survey-results +order: 5 +icon: solid/poll heroImage: /images/landing-page/hero-images/covidcast_survey.jpg --- diff --git a/content/covidcast/timelapse.md b/content/covidcast/timelapse.md index 8710382d4..eeeb3e3b8 100644 --- a/content/covidcast/timelapse.md +++ b/content/covidcast/timelapse.md @@ -3,5 +3,8 @@ title: COVIDCast Timelapse linkTitle: Timelapse description: COVIDcast tracks and forecasts the spread of COVID-19. By Carnegie Mellon's Delphi Research Group. layout: covidcast_app +app_mode: timelapse +order: 2 +icon: solid/clock heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg --- diff --git a/content/covidcast/top10.md b/content/covidcast/top10.md index 881909044..89b37babe 100644 --- a/content/covidcast/top10.md +++ b/content/covidcast/top10.md @@ -3,5 +3,8 @@ title: COVIDCast Top 10 linkTitle: Top 10 description: COVIDcast tracks and forecasts the spread of COVID-19. By Carnegie Mellon's Delphi Research Group. layout: covidcast_app +app_mode: top10 +order: 3 +icon: solid/list heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg --- diff --git a/package-lock.json b/package-lock.json index 952dc3d5e..535aa2224 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "www-main", - "version": "1.12.0", + "version": "1.12.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4267,8 +4267,8 @@ "dev": true }, "www-covidcast": { - "version": "https://github.com/cmu-delphi/www-covidcast/releases/download/v1.12.0/www-covidcast-1.12.0.tgz", - "integrity": "sha512-kqfEGujNz+lMat4GKSVBB015GiKWrEnqx1qVu1PxMWnSTaJo3vox+KX9fFP00wnTpSM/uCF2/f7AzRxGtBtdog==", + "version": "https://github.com/cmu-delphi/www-covidcast/releases/download/v1.12.1/www-covidcast-1.12.1.tgz", + "integrity": "sha512-mWV4WVcfCRpLct+gHkCCuw5NfuNVcJfhZ4Kr8jQczRtARRGm2xmUzGi9pDTdpo6mbrdyhXjOMx5icytjgLx7KA==", "requires": { "uikit": "^3.5.10" }, diff --git a/package.json b/package.json index 291ba6877..c216af964 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "www-main", - "version": "1.12.0", + "version": "1.12.1", "private": true, "dependencies": { "@fortawesome/fontawesome-free": "^5.15.1", "highlight.js": "^10.4.1", "katex": "^0.12.0", "uikit": "^3.5.9", - "www-covidcast": "https://github.com/cmu-delphi/www-covidcast/releases/download/v1.12.0/www-covidcast-1.12.0.tgz" + "www-covidcast": "https://github.com/cmu-delphi/www-covidcast/releases/download/v1.12.1/www-covidcast-1.12.1.tgz" }, "devDependencies": { "cpx": "^1.5.0", diff --git a/themes/delphi/assets/location-solid.svg b/themes/delphi/assets/location-solid.svg new file mode 100644 index 000000000..e8f31f53c --- /dev/null +++ b/themes/delphi/assets/location-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/themes/delphi/layouts/_default/baseof.html b/themes/delphi/layouts/_default/baseof.html index f2389b0a8..f70a407fa 100644 --- a/themes/delphi/layouts/_default/baseof.html +++ b/themes/delphi/layouts/_default/baseof.html @@ -8,7 +8,7 @@ {{ partial "nav.html" . }} - {{ block "breadcrumb" . }}{{ partial "menu/breadcrumb.html" . }}{{ end }} + {{ partial "menu/breadcrumb.html" . }} {{ block "main" . }} {{ end }} {{ partial "footer.html" . }} {{ partial "scripts.html" . }} diff --git a/themes/delphi/layouts/_default/covidcast_app.html b/themes/delphi/layouts/_default/covidcast_app.html index 1e4cbfd4b..a11b96586 100644 --- a/themes/delphi/layouts/_default/covidcast_app.html +++ b/themes/delphi/layouts/_default/covidcast_app.html @@ -7,9 +7,9 @@ {{ end }} -{{ define "breadcrumb" }}{{ end }} {{ define "body_class" }}covidcast_wrapper{{ end }} {{ define "main" }} + {{ partial "covidcast/modes.html" . }}