---
title: "Panel test error adjustment" 
output: html_document
vignette: > 
  %\VignetteIndexEntry{Panel test error adjustment} 
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
devtools::load_all()
library(tidyverse)
library(testerror)
options(mc.cores = 2) #parallel::detectCores())
# rstan::rstan_options(auto_write = FALSE)
here::i_am("vignettes/testerror.Rmd")
source(here::here("vignettes/formatting.R"))
```

We define panel testing as testing for multiple subtypes of disease, where any
positive result for a subtype is regarded as a positive for the disease. As we
describe in our paper this leads to compound error, which is particularly
significant when the individual components are present at low prevalence and the
number of components involved is large.

To describe the impact of test error on this kind of panel we have constructed a
simulation based on a panel test with 20 components, with an overall panel
prevalence of 10%, distributed randomly between components. Each component test
has 99.5% specificity and 80% sensitivity.

```{r}
tmp = testerror:::panel_example(
  comp_spec = beta_dist(p = 0.995, n=2000)$r(20),
  comp_sens = beta_dist(p = 0.8, n=100)$r(20), 
  n_comp = 20, n_samples = 2000, n_controls = 800,exact = FALSE)
design = tmp$design %>% dplyr::filter(test != "Panel") %>% select(-n_components)
design %>% dplyr::mutate(across(c(-test), ~sprintf("%1.2f%%", .x*100))) %>%
  default_table()
```

The simulation generates a control data set based on pre-specified component
test performance and a set of simulated known negatives (specificity) and known
positives (sensitivity), and a observation data set based on component
prevalence, and test performance. The control data set is summarised in the
following table:

```{r}
tmp$performance %>% dplyr::transmute(
  test = test,
  specificity = sprintf("%d/%d = %s", false_pos_controls, n_controls, format(spec, "{sprintf('%1.1f%% [%1.1f%% \u2013 %1.1f%%]',median*100, lower*100,upper*100)}")),
  sensitivity = sprintf("%d/%d = %s", false_neg_diseased, n_diseased, format(sens, "{sprintf('%1.1f%% [%1.1f%% \u2013 %1.1f%%]',median*100, lower*100,upper*100)}"))
) %>% default_table()
```

The main simulation data of the test results observation is the following
format:

```{r}
testerror:::.input_data
```

```{r}
sim_result = tmp$samples %>% dplyr::select(id,test,result = observed)
sim_result %>% glimpse()
```

and the control group a set of counts:

```{r}
sim_control = tmp$performance %>% dplyr::select(test, false_pos_controls, n_controls, false_neg_diseased, n_diseased)
sim_control %>% glimpse()
```

given that we know the true prevalence, sensitivity and specificity of the
simulation we can calculate the theoretical true prevalence, sensitivity and
specificity of the panel

```{r}
# this is set in the simulation to be 0.1
testerror::panel_prevalence(p = design$design_prev)
# the specificity of the panel is the product of component specificites
testerror::panel_spec(spec = design$design_spec)
# the sensitivity of the panel is a complex relationship
testerror::panel_sens(p = design$design_prev, sens = design$design_sens, spec = design$design_spec)
```

In most cases we only have observed data and control groups to estimate the
panel test performance parameters. This introduces uncertainty into estimates of
the panel sensitivity and specificity, which is estimated using resampling. The
resulting empirical distributions are well represented by a Beta distribution,
for both sensitivity and specificity (dotted lines - matched by moments), unless
there had been a great deal of variation in the component panel performance
metrics.

```{r}
component_counts = sim_result %>%
  dplyr::summarise(pos = sum(result), n = dplyr::n(), .by=test)
sens_dist = testerror::uncertain_panel_sens_estimator(
  pos_obs = component_counts$pos,
  n_obs = component_counts$n,
  false_pos_controls = sim_control$false_pos_controls,
  n_controls = sim_control$n_controls,
  false_neg_diseased = sim_control$false_neg_diseased,
  n_diseased = sim_control$n_diseased
)
spec_dist = testerror::uncertain_panel_spec(
  false_pos_controls = sim_control$false_pos_controls,
  n_controls = sim_control$n_controls
)
ggplot()+
  ggplot2::geom_density(aes(x=sens),data=tibble::tibble(sens=sens_dist), colour="red")+
  ggplot2::geom_function(fun = beta_fit(sens_dist)$d, linetype="dotted", colour="red")+
  ggplot2::geom_density(aes(x=spec),data=tibble::tibble(spec=spec_dist), colour="blue")+
  ggplot2::geom_function(fun = beta_fit(spec_dist)$d, linetype="dotted", colour="blue")+
  ggplot2::labs(caption = sprintf("Red: panel sensitivity estimate %s\nBlue: panel specificity estimate %s", format(beta_fit(sens_dist)), format(beta_fit(spec_dist))))
```

Using the estimates of the panel sensitivity and specificity it is possible to
generate an estimate for true prevalence for the panel using a rogan-gladen
estimate

```{r}
panel_count = sim_result %>%
  dplyr::summarise(panel_result = any(result), .by = id) %>%
  dplyr::summarise(panel_pos = sum(panel_result), panel_n = dplyr::n()) %>%
  dplyr::mutate(panel_test_pos = sprintf("%1.2f%% (%d/%d)", panel_pos/panel_n*100, panel_pos, panel_n))
adj = panel_count %>% dplyr::mutate(
    uncertain_panel_rogan_gladen(
      panel_pos_obs = panel_pos,
      panel_n_obs = panel_n,
      pos_obs = component_counts$pos,
      n_obs = component_counts$n,
      false_pos_controls = sim_control$false_pos_controls,
      n_controls = sim_control$n_controls,
      false_neg_diseased = sim_control$false_neg_diseased,
      n_diseased = sim_control$n_diseased
    ) %>%
    dplyr::select(tidyselect::starts_with("prevalence"))
  )
adj %>% dplyr::select(`Panel test positives` = panel_test_pos, `Modelled prevalence` = prevalence.label, `Method` = prevalence.method) %>% default_table()
```

The `r adj$panel_test_pos` positives is a poor estimate of true prevalence which
is set in this scenario to 10%. The adjusted value `r adj$prevalence.label`
includes the true value in its confidence limits.

```{r}
corr = true_panel_prevalence(
   test_results = sim_result,
   false_pos_controls = sim_control$false_pos_controls,
   n_controls = sim_control$n_controls,
   false_neg_diseased = sim_control$false_neg_diseased,
   n_diseased = sim_control$n_diseased,
   method = "rogan-gladen"
)

corr2 = true_panel_prevalence(
   test_results = sim_result,
   false_pos_controls = sim_control$false_pos_controls,
   n_controls = sim_control$n_controls,
   false_neg_diseased = sim_control$false_neg_diseased,
   n_diseased = sim_control$n_diseased,
   method = "lang-reiczigel"
)
```

```{r}
corr3 = true_panel_prevalence(
  test_results = sim_result,
  false_pos_controls = sim_control$false_pos_controls,
  n_controls = sim_control$n_controls,
  false_neg_diseased = sim_control$false_neg_diseased,
  n_diseased = sim_control$n_diseased,
  method = "bayes",
  model_type = "logit"
)

testerror:::demo_bar_plot(tmp$summary %>% dplyr::inner_join(bind_rows(corr,corr2,corr3),by="test"),.pcv=FALSE)
```

```{r}
corr3 %>% dplyr::transmute(
  Test = test,
  Positivity = sprintf("%d/%d (%1.2f%%)", pos_obs, n_obs, pos_obs/n_obs*100),
  `Estimated prevalence` = prevalence.label,
  `Sensitivity` = sens.label,
  `Specificity` = spec.label,
  `Method` = prevalence.method
) %>% default_table() %>% huxtable::set_top_border(row = huxtable::final(1), col=huxtable::everywhere, value = 1)
```

```{r}
# testerror:::demo_qq_plot(tmp$summary %>% dplyr::inner_join(bind_rows(corr,corr2, corr3)))
```
