-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from seroanalytics/shiny
shiny app for diagnostics
- Loading branch information
Showing
21 changed files
with
413 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,9 @@ Imports: | |
logger, | ||
mosaic, | ||
pkgload, | ||
plotly, | ||
R6, | ||
shiny, | ||
stats, | ||
stringr, | ||
tidybayes | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
inspect_model <- function(mod, private) { | ||
|
||
prior_inputs <- function(name, description) { | ||
mu <- paste("mu", name, sep = "_") | ||
sigma <- paste("sigma", name, sep = "_") | ||
shiny::div(shiny::fluidRow( | ||
shiny::column(4, | ||
description | ||
), | ||
shiny::column(4, | ||
shiny::fluidRow(class = "form-group", | ||
shiny::tags$label(paste0("mean (", mu, ")"), class = "col-sm-6 col-form-label text-right"), | ||
shiny::column(6, raw_numeric_input(mu, value = private$priors[[mu]])) | ||
) | ||
), | ||
shiny::column(4, | ||
shiny::fluidRow(class = "form-group", | ||
shiny::tags$label(paste0("SD (", sigma, ")"), class = "col-sm-6 col-form-label text-right"), | ||
shiny::column(6, raw_numeric_input(sigma, value = private$priors[[sigma]])), | ||
) | ||
)) | ||
) | ||
} | ||
|
||
all_covariates <- c("None", detect_covariates(private$data)) | ||
|
||
ui <- shiny::fluidPage(style = "margin: 0.5em", | ||
shiny::fluidRow( | ||
shiny::column(5, | ||
shiny::h3("Prior predictive check"), | ||
plotly::plotlyOutput("prior_predicted"), | ||
shiny::tags$pre(style = "overflow: hidden; text-wrap: auto; word-break: keep-all; white-space: pre-line; margin-top: 20px;", | ||
shiny::textOutput("prior_code", inline = TRUE) | ||
), | ||
prior_inputs("t0", "Baseline titre value"), | ||
prior_inputs("tp", "Time to peak titre"), | ||
prior_inputs("ts", "Time to start of waning"), | ||
prior_inputs("m1", "Boosting rate"), | ||
prior_inputs("m2", "Plateau rate"), | ||
prior_inputs("m3", "Waning rate") | ||
), | ||
shiny::column(7, | ||
shiny::h3("Model input data"), | ||
shiny::uiOutput( | ||
"data_plot" | ||
), | ||
shiny::div(style = "margin-top: 20px;", | ||
shiny::fluidRow(class = "form-group", | ||
shiny::column(2, | ||
shiny::numericInput("ncol", label = "Number of columns", value = 3) | ||
), | ||
shiny::column(3, | ||
shiny::selectInput("covariate", "Facet by", | ||
choices = all_covariates, | ||
selected = "None", | ||
selectize = FALSE) | ||
), | ||
shiny::column(7, | ||
shiny::div(class = "form-group", | ||
shiny:::shinyInputLabel("filter", "Filter by"), | ||
shiny::fluidRow( | ||
shiny::column(5, | ||
raw_select_input("filter", | ||
choices = all_covariates, | ||
selected = "None") | ||
), | ||
shiny::column(1, style = "padding-top: 5px;", "~="), | ||
shiny::column(5, | ||
raw_text_input("filter_value", placeholder = "regex") | ||
) | ||
) | ||
) | ||
) | ||
) | ||
) | ||
) | ||
), | ||
shiny::fluidRow(style = "margin-top: 20px;", | ||
shiny::column(12, | ||
shiny::h3(shiny::textOutput("fitted")) | ||
) | ||
) | ||
) | ||
|
||
server <- function(input, output, session) { | ||
# priors | ||
prior <- shiny::reactive( | ||
biokinetics_priors(mu_t0 = input$mu_t0, mu_tp = input$mu_tp, | ||
mu_ts = input$mu_ts, mu_m1 = input$mu_m1, | ||
mu_m2 = input$mu_m2, mu_m3 = input$mu_m3, | ||
sigma_t0 = input$sigma_t0, sigma_tp = input$sigma_tp, | ||
sigma_ts = input$sigma_ts, sigma_m1 = input$sigma_m1, | ||
sigma_m2 = input$sigma_m2, sigma_m3 = input$sigma_m3) | ||
) | ||
output$prior_code <- shiny::renderText({ | ||
prior_code(input) | ||
}) | ||
output$prior_predicted <- plotly::renderPlotly({ | ||
plotly::style(plotly::ggplotly(plot(prior(), | ||
data = private$data, | ||
upper_censoring_limit = private$stan_input_data$upper_censoring_limit, | ||
lower_censoring_limit = private$stan_input_data$lower_censoring_limit)), textposition = "right") | ||
}) | ||
|
||
# model inputs | ||
cols <- shiny::reactive({ | ||
if (is.na(input$ncol)) { | ||
return(NULL) | ||
} else { | ||
return(input$ncol) | ||
} | ||
}) | ||
|
||
selected_covariate <- shiny::reactive({ | ||
input$covariate | ||
}) | ||
|
||
filter <- shiny::reactive({ | ||
input$filter | ||
}) | ||
|
||
filter_value <- shiny::reactive({ | ||
input$filter_value | ||
}) | ||
|
||
data <- shiny::reactive({ | ||
if (filter_value() != "" && | ||
!is.null(filter()) && | ||
filter() != "None") { | ||
return(private$data[grepl(filter_value(), get(filter()), ignore.case = TRUE)]) | ||
} else { | ||
return(private$data) | ||
} | ||
}) | ||
|
||
plot_inputs <- shiny::reactive({ | ||
selected <- selected_covariate() | ||
if (is.null(selected) || selected == "None") { | ||
selected <- character(0) | ||
} | ||
plot_sero_data(data(), | ||
ncol = cols(), | ||
covariates = selected, | ||
upper_censoring_limit = private$stan_input_data$upper_censoring_limit, | ||
lower_censoring_limit = private$stan_input_data$lower_censoring_limit) + | ||
theme(plot.margin = unit(c(1, 0, 0, 0), "cm")) | ||
}) | ||
|
||
output$data <- plotly::renderPlotly({ | ||
if (nrow(data()) > 0) { | ||
gp <- plotly::style(plotly::ggplotly(plot_inputs()), textposition = "right") | ||
if (selected_covariate() != "None") { | ||
return(facet_strip_bigger(gp, 30)) | ||
} else { | ||
return(gp) | ||
} | ||
} | ||
}) | ||
|
||
output$data_plot <- shiny::renderUI({ | ||
if (nrow(data()) > 0) { | ||
plotly::plotlyOutput("data") | ||
} else { | ||
shiny::h3("No rows selected. Please change your filter.") | ||
} | ||
}) | ||
|
||
# model outputs | ||
output$fitted <- shiny::renderText({ | ||
if (is.null(private$fitted)) { | ||
"Model has not been fitted yet. Once fitted, inspect the model again to see posterior predictions." | ||
} | ||
}) | ||
} | ||
|
||
logger::log_info( | ||
"Starting Shiny app for model review; use Ctrl + C to quit" | ||
) | ||
shiny::runApp( | ||
shiny::shinyApp(ui, server), | ||
quiet = TRUE, | ||
launch.browser = shiny::paneViewer() | ||
) | ||
invisible() | ||
} | ||
|
||
# plotly can't handle multi-line facet titles, so manually make | ||
# the facet titles a little bigger when there are covariates | ||
facet_strip_bigger <- function(gp, size) { | ||
|
||
n_facets <- c(1:length(gp[["x"]][["layout"]][["shapes"]])) | ||
|
||
for (i in n_facets) { | ||
gp[["x"]][["layout"]][["shapes"]][[i]][["y0"]] <- +as.numeric(size) | ||
gp[["x"]][["layout"]][["shapes"]][[i]][["y1"]] <- 0 | ||
} | ||
|
||
return(gp) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
raw_numeric_input <- function(inputId, value, min = NA, max = NA, step = NA) { | ||
value <- shiny::restoreInput(id = inputId, default = value) | ||
inputTag <- shiny::tags$input(id = inputId, type = "number", class = "shiny-input-number form-control", value = shiny:::formatNoSci(value)) | ||
if (!is.na(min)) inputTag$attribs$min <- min | ||
if (!is.na(max)) inputTag$attribs$max <- max | ||
if (!is.na(step)) inputTag$attribs$step <- step | ||
inputTag | ||
} | ||
|
||
raw_text_input <- function(inputId, value = "", placeholder = NULL) { | ||
value <- shiny::restoreInput(id = inputId, default = value) | ||
shiny::tags$input(id = inputId, type = "text", class = "shiny-input-text form-control", value = value, placeholder = placeholder) | ||
} | ||
|
||
raw_select_input <- function(inputId, choices, selected = NULL, multiple = FALSE) { | ||
selected <- shiny::restoreInput(id = inputId, default = selected) | ||
choices <- shiny:::choicesWithNames(choices) | ||
if (is.null(selected)) { | ||
if (!multiple) selected <- shiny:::firstChoice(choices) | ||
} else selected <- as.character(selected) | ||
shiny::tags$select(id = inputId, class = "shiny-input-select", class = "form-control", shiny:::selectOptions(choices, selected, inputId)) | ||
} | ||
|
||
|
||
prior_code <- function(input) { | ||
deparse(substitute(biokinetics_priors(mu_t0 = a, mu_tp = b, | ||
mu_ts = c, mu_m1 = d, | ||
mu_m2 = e, mu_m3 = f, | ||
sigma_t0 = g, sigma_tp = h, | ||
sigma_ts = i, sigma_m1 = j, | ||
sigma_m2 = k, sigma_m3 = l), | ||
list(a = input$mu_t0, b = input$mu_tp, | ||
c = input$mu_ts, d = input$mu_m1, | ||
e = input$mu_m2, f = input$mu_m3, | ||
g = input$sigma_t0, h = input$sigma_tp, | ||
i = input$sigma_ts, j = input$sigma_m1, | ||
k = input$sigma_m2, l = input$sigma_m3)), width.cutoff = 500L) | ||
} | ||
|
||
detect_covariates <- function(data) { | ||
setdiff(colnames(data), c("pid", "day", "last_exp_day", | ||
"titre_type", "value", "censored", | ||
"obs_id", "time_since_last_exp")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.