Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plot Step Ahead Projection function #2

Merged
merged 56 commits into from
Nov 14, 2023
Merged

Plot Step Ahead Projection function #2

merged 56 commits into from
Nov 14, 2023

Conversation

LucieContamin
Copy link
Contributor

@LucieContamin LucieContamin commented Aug 1, 2023

Still to do:

  • The function output Plotly type object, we should add a "interactive" parameter that when set to TRUE returns Plotly object, but returns ggplot2 type object when set to FALSE.
  • Add Testing

- from the example-complex-scenario-hub. "example_worfklow.Rmd"
- add `use_median_as_point` parameter
- force forecast_data as "model_output_df"
- fix input of plotly function
- update plot if no "point" value
to show or not the output plot
- output for 1 model_id in ens_name (truth data and fix error by default)
- return plot_model object
- allow none, one or multiple intervals
- adapt transparency
- fix opacity
- reorganize function (create internal functions)
- add top_layer parameter
@LucieContamin
Copy link
Contributor Author

I committed a handful of minor changes to the vignette and one argument name change to the plot_step_ahead_forecasts() function. here are the three items that I see as perhaps things that should be fixed before committing this PR:

* [x]  consider a different name for the function, in the spirit of generalizing beyond forecasts. Like `plot_step_ahead_model_output()`?

* [x]  per comment on the PR, consider making the "fill_by" argument less restricted to model only.

* [x]  add `x_col_name` argument with a default of "target_date".

* [ ]  possibly consider using more centrally located example files, although I don't think we should let this be a blocker to merging this.

Thank you for the feedback, I implemented the required changes except for the example files. I am not sure what would be the best solution here.

Copy link
Contributor

@nickreich nickreich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am basically fine with the content of this PR as is. My final requests are:

  • some investigation into why the plotly figure heights are so tall and ideally fixing this so by default they are lower
  • adding at least some unit tests to the code

I do think we should enforce the unit tests piece prior to merging in the current PR, even though the functionality appears to be there. I don't have the time to do this now, but I would be happy at a future time to help brainstorm a list of possible tests that could be put in place here. E.g. one that came to me on this latest re-review is making sure we throw an error if the column specified by x_col_name argument is not a date.

@saraloo
Copy link

saraloo commented Oct 5, 2023

Found this generally very clear and easy to follow.

I find the attached plot (faceted by model) a bit confusing, with the legend only showing the one model (the first panel). In the interactive plotly, if you then click to not display the model, only the first panel projection disappears (pictured). I would propose to have the default of this plot to not have a legend, but not sure exactly how to set this up practically (tricky with the option default being true for other plots). Another option might be to allow each of the panels to still be a different colour so the legend corresponds to something meaningful?

Screen Shot 2023-10-05 at 12 23 19 PM

@LucieContamin LucieContamin linked an issue Oct 10, 2023 that may be closed by this pull request
@LucieContamin LucieContamin linked an issue Oct 11, 2023 that may be closed by this pull request
Copy link
Contributor

@annakrystalli annakrystalli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a number of comments but most are just suggestions. Overall I found nothing critical to resolve but think following some suggestions might make the code easier to follow and reason about.

Also might be a good idea to run styler::tidyverse_style() on any R/*.R and testthat/*.R script to make them more readable.

tests/testthat/test-plot_step_ahead_model_output.R Outdated Show resolved Hide resolved
tests/testthat/test-plot_step_ahead_model_output.R Outdated Show resolved Hide resolved
- `example_round1.csv`: example of model output for a round associated with the
origin date: "2021-03-07" (called "round 1"), target: "incident case", for the
US national level, from the
[example-complex-scenario-hub](https://github.com/Infectious-Disease-Modeling-Hubs/example-complex-scenario-hub). The data set also contains an ensemble calculated by applying the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we answered this on slack. While the idea is generally good, in some packages we don't need a full example hub so to keep package sizes as small as possible, I think it is acceptable to only include small and specific test materials to the functionality being tested.

R/plot_step_ahead_model_output.R Outdated Show resolved Hide resolved
R/plot_step_ahead_model_output.R Outdated Show resolved Hide resolved
R/plot_step_ahead_model_output.R Outdated Show resolved Hide resolved
#' @param all_ens a list with two data frame: one for plain lines,
#' one for ribbons plotting (in a wide format) for a unique `model_id` value
#' associated with specific color (`ens_color`). NULL is no specific layout
#' required
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling a little to understand the difference between all_plot and all_ens

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly the same format. all_ens is only non NULL when the parameter to set the ensemble to a specific color and contains the data associated with the model_id designated as "ensemble". In this case, the model_id will be excluded from the all_plot object. I split them because they have slightly different plotting parameters and it was easy to set it like this.
However, I am happy to re-think in a future version and make it easier to understand (same as the idea to "modularize" the lapply/if part of the code)

R/plot_step_ahead_model_output.R Outdated Show resolved Hide resolved
Comment on lines 463 to 551
subplot <- lapply(facet_value, function(x) {
# Data preparation
df_point <- all_plot$median[which(all_plot$median[[facet]] == x), ]
df_ribbon <- all_plot[names(all_plot) %in% intervals]
df_ribbon <- setNames(lapply(df_ribbon, function(df_rib) {
df_rib[which(df_rib[[facet]] == x), ]
}), names(df_ribbon))
if (!is.null(all_ens)) {
df_point_ens <- all_ens$median[which(all_ens$median[[facet]] == x), ]
df_ribbon_ens <- all_ens[names(all_ens) %in% intervals]
df_ribbon_ens <- setNames(lapply(df_ribbon_ens, function(df_rib) {
df_rib[which(df_rib[[facet]] == x), ]
}), names(df_ribbon_ens))
}
if (plot_truth & facet %in% colnames(truth_data)) {
truth_data <- truth_data[which(truth_data[[facet]] == x), ]
}

# Plot
if (x == facet_value[1]) {
plot_model <- simple_model_plot(
plot_model, df_point, df_ribbon, plot_truth, truth_data,
opacity = fill_transparency, top_layer = top_layer,
interactive = TRUE, fill_by = fill_by, x_col_name = x_col_name,
x_truth_col_name = x_truth_col_name)
} else if (facet == fill_by) {
plot_model <- simple_model_plot(
plot_model, df_point, df_ribbon, plot_truth, truth_data,
opacity = fill_transparency, top_layer = top_layer,
show_truth_legend = FALSE, interactive = TRUE,
fill_by = fill_by, x_col_name = x_col_name,
x_truth_col_name = x_truth_col_name)
} else {
plot_model <- simple_model_plot(
plot_model, df_point, df_ribbon, plot_truth, truth_data,
opacity = fill_transparency, showlegend = FALSE,
top_layer = top_layer, show_truth_legend = FALSE,
interactive = TRUE, fill_by = fill_by, x_col_name = x_col_name,
x_truth_col_name = x_truth_col_name)
}
# Ensemble color
if (!is.null(all_ens)) {
if (x == facet_value[1]) {
plot_model <- simple_model_plot(
plot_model, df_point_ens, df_ribbon_ens, FALSE, truth_data,
line_color = ens_color, opacity = fill_transparency,
top_layer = top_layer, interactive = TRUE, fill_by = fill_by,
x_col_name = x_col_name, x_truth_col_name = x_truth_col_name)
} else if (facet == fill_by) {
if (facet == "model_id" & ens_name == x) {
plot_model <- simple_model_plot(
plot_model, df_point_ens, df_ribbon_ens, TRUE, truth_data,
line_color = ens_color, opacity = fill_transparency,
top_layer = top_layer, show_truth_legend = FALSE,
interactive = TRUE, fill_by = fill_by, x_col_name = x_col_name,
x_truth_col_name = x_truth_col_name)
}
} else {
plot_model <- simple_model_plot(
plot_model, df_point_ens, df_ribbon_ens, FALSE, truth_data,
line_color = ens_color, opacity = fill_transparency,
showlegend = FALSE, top_layer = top_layer,
show_truth_legend = FALSE, interactive = TRUE, fill_by = fill_by,
x_col_name = x_col_name, x_truth_col_name = x_truth_col_name)
}
}
if (!is.null(facet_title)) {
if (grepl("top", facet_title)) {
y_title <- 1
y_anchor <- "top"
} else if (grepl("bottom", facet_title)) {
y_title <- 0
y_anchor <- "bottom"
}
if (grepl("left", facet_title)) {
x_title <- 0
x_anchor <- "left"
} else if (grepl("right", facet_title)) {
x_title <- 1
x_anchor <- "right"
}
plot_model <- plotly::layout(
plot_model,
annotations = list(x = x_title, y = y_title, xref = "paper",
yref = "paper", xanchor = x_anchor,
yanchor = y_anchor, showarrow = FALSE, text = x))
}
return(plot_model)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section is quite nested and hard to follow. I wonder if some of it can be modularised and pulled out into smaller more concise functions?

Also although the unnamed function called in lapply only has x as an argument, there are a lot of objects used from the function environment which also make it a bit confusing and hard to follow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it will take me some time to test and implement. I am happy to think about it for a future version.

@LucieContamin LucieContamin mentioned this pull request Oct 31, 2023
@LucieContamin LucieContamin merged commit febce6f into main Nov 14, 2023
9 checks passed
@LucieContamin LucieContamin deleted the add_basic_fct branch November 14, 2023 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unit testing create plot_step_ahead_forecasts()
5 participants