diff --git a/DESCRIPTION b/DESCRIPTION index 157de409..88aa9c12 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,6 +19,8 @@ URL: https://mirai-solutions.ch, BugReports: https://github.com/miraisolutions/Covid19/issues Imports: config, + purrr, + stringr, golem, shiny, bsplus, diff --git a/NAMESPACE b/NAMESPACE index bbc3b780..597fc837 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -40,6 +40,7 @@ export(recovered_timeseries_csv_url) export(remove_legend) export(roundUp) export(run_app) +export(stackedbarplot_plot) export(time_evol_area_plot) export(time_evol_line_facet_plot) export(time_evol_line_plot) @@ -48,7 +49,9 @@ import(dplyr) import(ggplot2) import(leaflet) import(markdown) +import(purrr) import(shiny) +import(stringr) import(tidyr) importFrom(DT,DTOutput) importFrom(DT,datatable) diff --git a/NEWS.md b/NEWS.md index 169e143c..eda27cc3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,8 @@ - fix trend order of area plot both global and country tabs, added utils funtion (#74) - correct plot titles for modules mod_compare_nth_cases_plot and mod_growth_death_rate (#66) - Add angle to bar-plot labels (#68) +- Add status split stackedbarplot (#83) + ### Covid19 1.1.6 (2020-04-06) diff --git a/R/mod_country_comparison.R b/R/mod_country_comparison.R index 2714b5f6..aa913c86 100644 --- a/R/mod_country_comparison.R +++ b/R/mod_country_comparison.R @@ -25,6 +25,11 @@ mod_country_comparison_ui <- function(id){ withSpinner(uiOutput(ns("lines_points_plots"))) ) ), + fluidRow( + column(12, + withSpinner(uiOutput(ns("status_stackedbarplot"))) + ) + ), mod_add_table_ui(ns("add_table_countries")) ) } @@ -106,6 +111,11 @@ mod_country_comparison_server <- function(input, output, session, orig_data_aggr callModule(mod_compare_nth_cases_plot_server, "lines_points_plots", countries_data, n = n, n_highligth = length(input$select_countries), istop = F) + output$status_stackedbarplot <- renderUI({ + mod_stackedbarplot_ui(ns("status_stackedbarplot")) + }) + callModule(mod_stackedbarplot_status_server, "status_stackedbarplot", countries_data, n = n, n_highligth = length(input$select_countries), istop = F) + # tables ---- callModule(mod_add_table_server, "add_table_countries", countries_data, maxrowsperpage = 10) }) diff --git a/R/mod_global.R b/R/mod_global.R index a3f347aa..0f71bb66 100644 --- a/R/mod_global.R +++ b/R/mod_global.R @@ -38,6 +38,12 @@ mod_global_ui <- function(id){ ) ), hr(), + fluidRow( + column(12, + mod_stackedbarplot_ui(ns("plot_stackedbarplot_status")) + ) + ), + hr(), mod_add_table_ui(ns("add_table_world")) ) } @@ -138,6 +144,9 @@ mod_global_server <- function(input, output, session, orig_data, orig_data_aggre # > growth_death_rate callModule(mod_growth_death_rate_server, "plot_growth_death_rate", orig_data_aggregate) + # > stacked barplot with status split + callModule(mod_stackedbarplot_status_server, "plot_stackedbarplot_status", orig_data_aggregate) + # tables ---- callModule(mod_add_table_server, "add_table_world", world) diff --git a/R/mod_stackedbarplot_status.R b/R/mod_stackedbarplot_status.R new file mode 100644 index 00000000..35f78301 --- /dev/null +++ b/R/mod_stackedbarplot_status.R @@ -0,0 +1,104 @@ +#' stackedbarplot_status UI Function +#' +#' @description A shiny Module. +#' +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' +#' @importFrom shiny NS tagList +#' @importFrom shinycssloaders withSpinner +mod_stackedbarplot_ui <- function(id, n_highligth = 5){ + ns <- NS(id) + tagList( + fluidRow( + column(6, + uiOutput(ns("title_stackedbarplot_status")), + withSpinner(uiOutput(ns("plot_stackedbarplot_status"), height = 400)), + ) + ) + ) +} +#' stackedbarplot_status Server Function +#' +#' @param countries_data reactive data.frame for multiple countries +#' +#' @import dplyr +#' @import tidyr +#' @import ggplot2 +#' @import purrr +#' @importFrom plotly ggplotly layout +#' @noRd +mod_stackedbarplot_status_server <- function(input, output, session, df, n = 1000, w = 7, n_highligth = 5, istop = T){ + ns <- session$ns + # titles + if (istop) { + output$title_stackedbarplot_status <- renderUI(div(h4(paste0("Current top ", n_highligth, " status split")), align = "center", style = "margin-top:20px; margin-bottom:20px;")) + } else { + output$title_stackedbarplot_status <- renderUI(div(h4("Status split by country"), align = "center", style = "margin-top:20px; margin-bottom:20px;")) + } + + prep_data <- function(orig_data_aggregate, n, w){ + df1 <- orig_data_aggregate %>% + Covid19:::select_countries_n_cases_w_days(n = n, w = w) %>% + filter( date == max(date)) %>% + Covid19:::align_country_names_pop() %>% + mutate(country_name = Country.Region) %>% + Covid19:::get_pop_data() %>% + filter(population > 10^6) %>% # dropping countries with less than 1 M pop, needed? + Covid19:::align_country_names_pop_reverse() + df1 + } + df_pop <- reactive({prep_data(df(), n,w)}) + #df_pop = prep_data(df, n = 1000, w = 7) + + statuses <- c("deaths", "active", "recovered") + + pick_status <- function(df, stat){ + df <- df %>% + bind_cols(df[, stat] %>% setNames("Value")) + df + } + df_status = reactive({pick_status(df_pop(), "confirmed") %>% + arrange(desc(Value)) %>% + top_n(n_highligth, wt = Value) %>% + select(Country.Region,!!statuses)}) + + + # gather status and compute ratios + df_status_stack = reactive({df_status() %>% + gather("status", "countstatus", -Country.Region) %>% + group_by(Country.Region) %>% + mutate(n.pop = sum(countstatus), + ratio.over.cases = countstatus/n.pop) %>% + group_by(status) %>% + mutate(tot.status = sum(countstatus), + ratio.status = countstatus/tot.status) %>% + ungroup() %>% + mutate(Country.Region = as.factor(Country.Region), + status = factor(status, levels = statuses)) %>% + arrange(status)}) + + caption_explain <- "Status split per country as of today." + + output$plot_stackedbarplot_status <- renderUI({ + tagList( + plotlyOutput(ns("plot_stackedbarplot_status_draw"), height = 400), + div(p(caption_explain), align = "center") + ) + }) + + output$plot_stackedbarplot_status_draw <- renderPlotly({ + p = df_status_stack() %>% + stackedbarplot_plot() %>% fix_colors() + p <- p %>% + ggplotly(tooltip = c("text", "fill")) %>% + layout(legend = list(orientation = "v", y = 1, yanchor = "left")) + + p$x$data <- + p$x$data %>% + purrr::map(clean_plotly_leg, "[^\\(][^,]*") + + p + }) +} diff --git a/R/plots.R b/R/plots.R index 7869901c..c15da820 100644 --- a/R/plots.R +++ b/R/plots.R @@ -1,3 +1,36 @@ +#' stacked barplot status +#' +#' @param df data.frame +#' @param percent logical to make the y axis in percent +#' +#' @import ggplot2 +#' +#' @return ggplot plot +#' @export +stackedbarplot_plot <- function(df, percent = T) { + suffix = NULL + if (percent) { + df$ratio.over.cases <- 100*df$ratio.over.cases + suffix = "%" + } + p <- df %>% + ggplot(aes(x = Country.Region, y = ratio.over.cases, fill = status, + text = paste0("percentage: ", round(ratio.over.cases, 1), suffix,"
", + label = paste("count: ", + formatC(countstatus, format = "f", big.mark = ",", digits = 0)))))+ + basic_plot_theme() + + geom_col(position = position_stack(reverse = TRUE)) + + theme( + axis.text.x = element_text(angle = 30) + ) + if (percent) { + p <- p + scale_y_continuous(labels = function(x) paste0(x, "%")) + } + # p = p %>% + # fix_colors() + p +} + #' Time evolution as line plot #' #' @rdname time_evol_line_plot diff --git a/R/utils.R b/R/utils.R index 3c2183e0..1fce9c1f 100644 --- a/R/utils.R +++ b/R/utils.R @@ -195,3 +195,16 @@ align_country_names_pop_reverse <- function(data){ ) data } +#' clean ggplotly legend +#' @param .plotly_x ggplotly object +#' @param .extract_str regular expression +#' @import stringr +clean_plotly_leg <- function(.plotly_x, .extract_str) { + # Inpects an x$data list in a plotly object, cleans up legend values where appropriate + if ("legendgroup" %in% names(.plotly_x)) { + # The list includes a legend group + .plotly_x$legendgroup <- stringr::str_extract(.plotly_x$legendgroup, .extract_str) + .plotly_x$name <- stringr::str_extract(.plotly_x$name, .extract_str) + } + .plotly_x +} diff --git a/man-roxygen/ex-mod_stackedbarplot_status.R b/man-roxygen/ex-mod_stackedbarplot_status.R new file mode 100644 index 00000000..8c9a6b5c --- /dev/null +++ b/man-roxygen/ex-mod_stackedbarplot_status.R @@ -0,0 +1,27 @@ +if (interactive()) { + library(shiny) + library(Covid19) + library(dplyr) + library(tidyr) + library(ggplot2) + library(plotly) + + ui <- fluidPage( + tagList( + Covid19:::golem_add_external_resources(), + Covid19:::mod_stackedbarplot_ui("plot", 5) + ) + ) + server <- function(input, output, session) { + + orig_data_aggregate = reactive({get_timeseries_full_data() %>% + get_timeseries_by_contagion_day_data() %>% + aggregate_province_timeseries_data() %>% + arrange(Country.Region)}) + + callModule(Covid19:::mod_stackedbarplot_status_server, "plot", orig_data_aggregate) + + } + runApp(shinyApp(ui = ui, server = server), launch.browser = TRUE) +} + diff --git a/man/clean_plotly_leg.Rd b/man/clean_plotly_leg.Rd new file mode 100644 index 00000000..57f63bd7 --- /dev/null +++ b/man/clean_plotly_leg.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{clean_plotly_leg} +\alias{clean_plotly_leg} +\title{clean ggplotly legend} +\usage{ +clean_plotly_leg(.plotly_x, .extract_str) +} +\arguments{ +\item{.plotly_x}{ggplotly object} + +\item{.extract_str}{regular expression} +} +\description{ +clean ggplotly legend +} diff --git a/man/sort_type_hardcoded.Rd b/man/sort_type_hardcoded.Rd new file mode 100644 index 00000000..d3a21e35 --- /dev/null +++ b/man/sort_type_hardcoded.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{sort_type_hardcoded} +\alias{sort_type_hardcoded} +\title{Sort type harcoded} +\usage{ +sort_type_hardcoded() +} +\description{ +Sort type harcoded +} diff --git a/man/stackedbarplot_plot.Rd b/man/stackedbarplot_plot.Rd new file mode 100644 index 00000000..09fbb9a1 --- /dev/null +++ b/man/stackedbarplot_plot.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plots.R +\name{stackedbarplot_plot} +\alias{stackedbarplot_plot} +\title{stacked barplot status} +\usage{ +stackedbarplot_plot(df, percent = T) +} +\arguments{ +\item{df}{data.frame} + +\item{percent}{logical to make the y axis in percent} +} +\value{ +ggplot plot +} +\description{ +stacked barplot status +}