Repository: UCD-SERG/serodynamics
Pull request #66: Effective sample size diagnostics
Author: login: sschildhauer
Created: 2025-03-06T06:34:36Z | Merged: 2025-03-12T18:13:40Z

This function creates a plot that shows the proportion of effective samples drawn from the total samples drawn for each estimated parameter. This function uses ggs_effective to create the plot. 


Diff:
diff --git a/DESCRIPTION b/DESCRIPTION
index 0386529f..ab662879 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
 Package: serodynamics
 Title: What the Package Does (One Line, Title Case)
-Version: 0.0.0.9020
+Version: 0.0.0.9022
 Authors@R: c(
     person("Peter", "Teunis", , "p.teunis@emory.edu", role = c("aut", "cph"),
            comment = "Author of the method and original code."),
@@ -20,7 +20,7 @@ Imports:
     cli,
     coda,
     dplyr,
-    ggmcmc,
+    ggmcmc (>= 1.5.1.1),
     ggplot2,
     purrr,
     rlang,
@@ -60,8 +60,9 @@ Config/needs/test:
     readr,
     rlist
 Config/needs/check: commonmark, xml2
-Remotes: 
-    ucd-serg/serocalculator
+Remotes:  
+    ucd-serg/serocalculator,
+    xfim/ggmcmc
 Depends: 
     R (>= 4.1.0)
 LazyData: true
diff --git a/NAMESPACE b/NAMESPACE
index ff3ff0fc..c8567aa5 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -8,6 +8,7 @@ export(get_biomarker_names_var)
 export(initsfunction)
 export(load_data)
 export(plot_jags_dens)
+export(plot_jags_effect)
 export(postprocess_jags_output)
 export(prep_data)
 export(prep_priors)
@@ -19,6 +20,7 @@ importFrom(dplyr,.data)
 importFrom(dplyr,all_of)
 importFrom(dplyr,filter)
 importFrom(dplyr,mutate)
+importFrom(dplyr,n)
 importFrom(dplyr,reframe)
 importFrom(dplyr,rename)
 importFrom(dplyr,rowwise)
diff --git a/NEWS.md b/NEWS.md
index ce4fc24a..190b186a 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -4,6 +4,8 @@
 
 ## New features
 
+* Diagnostic function to produce effective sample size plots with
+stratification (#66)
 * Diagnostic function to produce density plots with stratification (#27)
 * Added SEES data set data folder and documentation (#41)
 * Fixing SEES data and added jags_post for SEES (#63)
diff --git a/R/plot_jags_effectivedx.R b/R/plot_jags_effectivedx.R
new file mode 100644
index 00000000..40dfe446
--- /dev/null
+++ b/R/plot_jags_effectivedx.R
@@ -0,0 +1,83 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [list] of [ggplot2::ggplot] objects showing the 
+#' proportion of effective samples taken/total samples taken for all parameter
+#' iso combinations. The estimate with the highest proportion of effective
+#' samples taken will be listed first.
+#' @export
+#' @example inst/examples/examples-plot_jags_effectivedx.R
+
+
+plot_jags_effect <- function(data,
+                             iso = unique(data$curve_params$Iso_type),
+                             param = unique(data$curve_params$Parameter_sub),
+                             strat = unique(data$curve_params$Stratification)) {
+  visualize_jags <- data[["curve_params"]]
+  attributes_jags <- data[["attributes"]]
+
+  eff_strat_list <- list()
+  for (i in strat) {
+
+    visualize_jags_sub <- visualize_jags |>
+      dplyr::filter(.data$Stratification == i)
+
+    # Creating open list to store ggplots
+    eff_out <- list()
+    # Looping through the isos
+    for (j in iso) {
+      visualize_jags_plot <- visualize_jags_sub |>
+        dplyr::filter(.data$Iso_type == j)
+
+      # Will not loop through parameters, as we may want each to show on the
+      # same plot by default.
+      visualize_jags_plot <- visualize_jags_plot |>
+        dplyr::filter(.data$Parameter_sub %in% param)
+
+      visualize_jags_plot <- visualize_jags_plot |>
+        # Changing parameter name to reflect the input
+        dplyr::mutate(Parameter = paste0("iso = ", j, ", parameter = ",
+                                         .data$Parameter_sub, ", strat = ",
+                                         i))
+      # Assigning attributes, which are needed to run ggs_density
+      attributes(visualize_jags_plot) <- c(attributes(visualize_jags_plot),
+                                           attributes_jags)
+      # Creating density plot
+      eff <- ggmcmc::ggs_effective(visualize_jags_plot) +
+        theme_bw()
+      eff_out[[j]] <- eff
+    }
+    eff_strat_list[[i]] <- eff_out
+  }
+  #Printing only one plot if only one exists.
+  if (sum(lengths(eff_strat_list)) == 1) {
+    eff_strat_list <- eff_strat_list[[1]][[iso]]
+  } 
+  eff_strat_list
+}
diff --git a/R/serodynamics-package.R b/R/serodynamics-package.R
index e93a00f1..71c99410 100644
--- a/R/serodynamics-package.R
+++ b/R/serodynamics-package.R
@@ -6,6 +6,7 @@
 #' @importFrom dplyr all_of
 #' @importFrom dplyr filter
 #' @importFrom dplyr mutate
+#' @importFrom dplyr n
 #' @importFrom dplyr reframe
 #' @importFrom dplyr rename
 #' @importFrom dplyr rowwise
diff --git a/inst/WORDLIST b/inst/WORDLIST
index a7512793..7d41a361 100644
--- a/inst/WORDLIST
+++ b/inst/WORDLIST
@@ -9,7 +9,6 @@ bayesian
 behaviour
 biomarker
 biomarkers
-bldculres
 dobson
 geoms
 ggproto
@@ -18,7 +17,6 @@ iso
 isotype
 isotypes
 mcmc
-nepal
 newperson
 nmc
 params
diff --git a/inst/examples/examples-plot_jags_effectivedx.R b/inst/examples/examples-plot_jags_effectivedx.R
new file mode 100644
index 00000000..ba11b952
--- /dev/null
+++ b/inst/examples/examples-plot_jags_effectivedx.R
@@ -0,0 +1,6 @@
+
+data <- serodynamics::nepal_sees_jags_post
+
+plot_jags_effect(data = data,
+                 iso = "HlyE_IgA",
+                 strat = "typhi")
diff --git a/man/plot_jags_effect.Rd b/man/plot_jags_effect.Rd
new file mode 100644
index 00000000..1838288f
--- /dev/null
+++ b/man/plot_jags_effect.Rd
@@ -0,0 +1,66 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plot_jags_effectivedx.R
+\name{plot_jags_effect}
+\alias{plot_jags_effect}
+\title{Plot Effective Sample Size Diagnostics}
+\usage{
+plot_jags_effect(
+  data,
+  iso = unique(data$curve_params$Iso_type),
+  param = unique(data$curve_params$Parameter_sub),
+  strat = unique(data$curve_params$Stratification)
+)
+}
+\arguments{
+\item{data}{A \link{list} outputted from run_mod().}
+
+\item{iso}{Specify \link{character} string to produce plots of only a
+specific antigen/antibody combination, entered with quotes. Default outputs
+all antigen/antibody combinations.}
+
+\item{param}{Specify \link{character} string to produce plots of only a
+specific parameter, entered with quotes. Options include:
+\itemize{
+\item \code{alpha} = posterior estimate of decay rate
+\item \code{r} = posterior estimate of shape parameter
+\item \code{t1} = posterior estimate of time to peak
+\item \code{y0} = posterior estimate of baseline antibody concentration
+\item \code{y1} = posterior estimate of peak antibody concentration
+}}
+
+\item{strat}{Specify \link{character} string to produce plots of specific
+stratification entered in quotes.}
+}
+\value{
+A \link{list} of \link[ggplot2:ggplot]{ggplot2::ggplot} objects showing the
+proportion of effective samples taken/total samples taken for all parameter
+iso combinations. The estimate with the highest proportion of effective
+samples taken will be listed first.
+}
+\description{
+plot_jags_effect() takes a \link{list} output from \code{\link[=run_mod]{run_mod()}}
+to create summary diagnostics for each chain run in the mcmc estimation.
+Defaults will produce every combination of antigen/antibody, parameters,
+and stratifications, unless otherwise specified. At least 2 chains are
+required to run function.
+Antigen/antibody combinations and stratifications will vary by analysis.
+The antibody dynamic curve includes the following parameters:
+\itemize{
+\item y0 = baseline antibody concentration
+\item y1 = peak antibody concentration
+\item t1 = time to peak
+\item r = shape parameter
+\item alpha = decay rate
+}
+}
+\examples{
+
+data <- serodynamics::nepal_sees_jags_post
+
+plot_jags_effect(data = data,
+                 iso = "HlyE_IgA",
+                 strat = "typhi")
+}
+\author{
+Sam Schildhauer
+}
diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml
index 008f7fc6..29c7628a 100644
--- a/pkgdown/_pkgdown.yml
+++ b/pkgdown/_pkgdown.yml
@@ -32,6 +32,7 @@ reference:
 - title: Model diagnostics
   contents:
   - plot_jags_dens
+  - plot_jags_effect
 - title: Postprocess JAGS output
   contents:
   - postprocess_jags_output
diff --git a/tests/testthat/_snaps/plot_jags_effectivedx/typhoid-plot-ess.svg b/tests/testthat/_snaps/plot_jags_effectivedx/typhoid-plot-ess.svg
new file mode 100644
index 00000000..05fa3012
--- /dev/null
+++ b/tests/testthat/_snaps/plot_jags_effectivedx/typhoid-plot-ess.svg
@@ -0,0 +1,76 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' class='svglite' data-engine-version='2.0' width='720.00pt' height='576.00pt' viewBox='0 0 720.00 576.00'>
+<defs>
+  <style type='text/css'><![CDATA[
+    .svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
+      fill: none;
+      stroke: #000000;
+      stroke-linecap: round;
+      stroke-linejoin: round;
+      stroke-miterlimit: 10.00;
+    }
+  ]]></style>
+</defs>
+<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
+<defs>
+  <clipPath id='cpMC4wMHw3MjAuMDB8MC4wMHw1NzYuMDA='>
+    <rect x='0.00' y='0.00' width='720.00' height='576.00' />
+  </clipPath>
+</defs>
+<g clip-path='url(#cpMC4wMHw3MjAuMDB8MC4wMHw1NzYuMDA=)'>
+<rect x='0.00' y='0.00' width='720.00' height='576.00' style='stroke-width: 1.07; stroke: #FFFFFF; fill: #FFFFFF;' />
+</g>
+<defs>
+  <clipPath id='cpMjEwLjYyfDcxNC41MnwyMi43OHw1NDUuMTE='>
+    <rect x='210.62' y='22.78' width='503.90' height='522.33' />
+  </clipPath>
+</defs>
+<g clip-path='url(#cpMjEwLjYyfDcxNC41MnwyMi43OHw1NDUuMTE=)'>
+<rect x='210.62' y='22.78' width='503.90' height='522.33' style='stroke-width: 1.07; stroke: none; fill: #FFFFFF;' />
+<polyline points='290.79,545.11 290.79,22.78 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='405.31,545.11 405.31,22.78 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='519.83,545.11 519.83,22.78 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='634.36,545.11 634.36,22.78 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='210.62,484.84 714.52,484.84 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='210.62,384.40 714.52,384.40 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='210.62,283.95 714.52,283.95 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='210.62,183.50 714.52,183.50 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='210.62,83.05 714.52,83.05 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='233.53,545.11 233.53,22.78 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='348.05,545.11 348.05,22.78 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='462.57,545.11 462.57,22.78 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='577.09,545.11 577.09,22.78 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<polyline points='691.62,545.11 691.62,22.78 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
+<circle cx='240.49' cy='484.84' r='1.95' style='stroke-width: 0.71; fill: #000000;' />
+<circle cx='251.50' cy='384.40' r='1.95' style='stroke-width: 0.71; fill: #000000;' />
+<circle cx='269.04' cy='283.95' r='1.95' style='stroke-width: 0.71; fill: #000000;' />
+<circle cx='294.33' cy='183.50' r='1.95' style='stroke-width: 0.71; fill: #000000;' />
+<circle cx='638.05' cy='83.05' r='1.95' style='stroke-width: 0.71; fill: #000000;' />
+<rect x='210.62' y='22.78' width='503.90' height='522.33' style='stroke-width: 1.07; stroke: #333333;' />
+</g>
+<g clip-path='url(#cpMC4wMHw3MjAuMDB8MC4wMHw1NzYuMDA=)'>
+<text x='205.69' y='487.87' text-anchor='end' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='185.17px' lengthAdjust='spacingAndGlyphs'>iso = HlyE_IgA, parameter = alpha, strat = typhi</text>
+<text x='205.69' y='387.42' text-anchor='end' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='187.62px' lengthAdjust='spacingAndGlyphs'>iso = HlyE_IgA, parameter = shape, strat = typhi</text>
+<text x='205.69' y='286.98' text-anchor='end' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='170.98px' lengthAdjust='spacingAndGlyphs'>iso = HlyE_IgA, parameter = t1, strat = typhi</text>
+<text x='205.69' y='186.53' text-anchor='end' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='172.94px' lengthAdjust='spacingAndGlyphs'>iso = HlyE_IgA, parameter = y0, strat = typhi</text>
+<text x='205.69' y='86.08' text-anchor='end' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='172.94px' lengthAdjust='spacingAndGlyphs'>iso = HlyE_IgA, parameter = y1, strat = typhi</text>
+<polyline points='207.88,484.84 210.62,484.84 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='207.88,384.40 210.62,384.40 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='207.88,283.95 210.62,283.95 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='207.88,183.50 210.62,183.50 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='207.88,83.05 210.62,83.05 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='233.53,547.85 233.53,545.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='348.05,547.85 348.05,545.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='462.57,547.85 462.57,545.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='577.09,547.85 577.09,545.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<polyline points='691.62,547.85 691.62,545.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
+<text x='233.53' y='556.10' text-anchor='middle' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='17.13px' lengthAdjust='spacingAndGlyphs'>0.00</text>
+<text x='348.05' y='556.10' text-anchor='middle' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='17.13px' lengthAdjust='spacingAndGlyphs'>0.25</text>
+<text x='462.57' y='556.10' text-anchor='middle' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='17.13px' lengthAdjust='spacingAndGlyphs'>0.50</text>
+<text x='577.09' y='556.10' text-anchor='middle' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='17.13px' lengthAdjust='spacingAndGlyphs'>0.75</text>
+<text x='691.62' y='556.10' text-anchor='middle' style='font-size: 8.80px; fill: #4D4D4D; font-family: sans;' textLength='17.13px' lengthAdjust='spacingAndGlyphs'>1.00</text>
+<text x='462.57' y='568.24' text-anchor='middle' style='font-size: 11.00px; font-family: sans;' textLength='42.19px' lengthAdjust='spacingAndGlyphs'>Effective</text>
+<text transform='translate(13.05,283.95) rotate(-90)' text-anchor='middle' style='font-size: 11.00px; font-family: sans;' textLength='51.36px' lengthAdjust='spacingAndGlyphs'>Parameter</text>
+<text x='210.62' y='14.56' style='font-size: 13.20px; font-family: sans;' textLength='243.64px' lengthAdjust='spacingAndGlyphs'>Proportion of effective independent draws</text>
+</g>
+</svg>
diff --git a/tests/testthat/test-plot_jags_effectivedx.R b/tests/testthat/test-plot_jags_effectivedx.R
new file mode 100644
index 00000000..4d79aeaf
--- /dev/null
+++ b/tests/testthat/test-plot_jags_effectivedx.R
@@ -0,0 +1,20 @@
+
+test_that(
+  desc = "results are consistent with ggplot output showing ESS",
+  code = {
+    skip_if(getRversion() < "4.4.1") # 4.3.3 had issues
+
+    data <- serodynamics::nepal_sees_jags_post |>
+      suppressWarnings()
+
+    # Testing for any errors:
+    results <- plot_jags_effect(data) |> expect_no_error()
+      
+    # Test to ensure output is a list object:
+    expect_true(is.list(results))
+    
+    # Test to ensure that a piece of the list is a ggplot object:
+    results$typhi$HlyE_IgA |> 
+      vdiffr::expect_doppelganger(title = "typhoid_plot_ESS")
+  }
+)


Reviews:
user: login: d-morrison
body: Looks good! I don't have time for a complete line-by-line review right now, but I caught a few minor formatting issues. Please address those, and then request a review from @Kwan-Jenny; I can do another pass after Kwan approves.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T07:50:44Z
user: login: d-morrison
body: (added one more note)
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T07:55:39Z
user: login: sschildhauer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-07T06:16:42Z
user: login: sschildhauer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-07T06:17:00Z
user: login: Kwan-Jenny
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-10T22:47:32Z
user: login: d-morrison
body: coming along - see comments.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T04:24:28Z
user: login: d-morrison
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T04:25:07Z
user: login: d-morrison
body: Looks good - thanks!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-12T18:13:22Z

Review comments:
diff_hunk: @@ -0,0 +1,95 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [base::list()] of [ggplot2::ggplot()] objects showing the 
+#' proportion of effective samples taken/total samples taken for all parameter
+#' iso combinations. The estimate with the highest proportion of effective
+#' samples taken will be listed first.
+#' @export
+#' @examples
+#' if (!is.element(runjags::findjags(), c("", NULL))) {
+#'   library(runjags)
+#'   library(ggmcmc)
+#'   library(dplyr)
+#'
+#'   data <- serodynamics::nepal_sees_jags_post
+#'
+#' plot_jags_effect(
+#'     data = data, #A [serodynamics::run_mod()] [list] output.
+#'     iso = "HlyE_IgA", #A [character] string specifying
+#'     #nantigen/antibody of interest.
+#'     param = "alpha",  #A [character] string specifying parameter of
+#'     # interest.
+#'     strat = "typhi")  #A [character] string specifying
+#'     # stratification of interest.
+#'     }
path: R/plot_jags_effectivedx.R
user: login: d-morrison
body: The indentation of the closing `}` looks off. 
Let's switch to using separate example files for any examples that are more than ~5 lines: https://roxygen2.r-lib.org/articles/rd.html?q=examples#examples:~:text=put%20them%20in-,separate%20files,-and%20use%20%40example

Then the linter should catch these kinds of indentation issues automatically.
created_at: 2025-03-06T07:46:45Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,95 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [base::list()] of [ggplot2::ggplot()] objects showing the 
path: R/plot_jags_effectivedx.R
user: login: d-morrison
body: see comments on previous PRs; let's not use parentheses when cross-linking object class names.
created_at: 2025-03-06T07:47:39Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,95 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [base::list()] of [ggplot2::ggplot()] objects showing the 
+#' proportion of effective samples taken/total samples taken for all parameter
+#' iso combinations. The estimate with the highest proportion of effective
+#' samples taken will be listed first.
+#' @export
+#' @examples
+#' if (!is.element(runjags::findjags(), c("", NULL))) {
+#'   library(runjags)
+#'   library(ggmcmc)
+#'   library(dplyr)
+#'
+#'   data <- serodynamics::nepal_sees_jags_post
+#'
+#' plot_jags_effect(
+#'     data = data, #A [serodynamics::run_mod()] [list] output.
path: R/plot_jags_effectivedx.R
user: login: d-morrison
body: @sschildhauer one more note; in the examples sections, the `[]` syntax doesn't do anything, so let's not use it; take a look at https://ucd-serg.github.io/serodynamics/preview/pr66/reference/plot_jags_effect.html to see how the example gets rendered.
Instead, you can either just use backticks (for example \`list\`), quotes, or just plain text. 
In fact, this amount of commenting is pretty unusual for examples; take a look at the tidyverse packages to see how examples are usually formatted
created_at: 2025-03-06T07:54:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,95 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [base::list()] of [ggplot2::ggplot()] objects showing the 
+#' proportion of effective samples taken/total samples taken for all parameter
+#' iso combinations. The estimate with the highest proportion of effective
+#' samples taken will be listed first.
+#' @export
+#' @examples
+#' if (!is.element(runjags::findjags(), c("", NULL))) {
+#'   library(runjags)
+#'   library(ggmcmc)
+#'   library(dplyr)
+#'
+#'   data <- serodynamics::nepal_sees_jags_post
+#'
+#' plot_jags_effect(
+#'     data = data, #A [serodynamics::run_mod()] [list] output.
+#'     iso = "HlyE_IgA", #A [character] string specifying
+#'     #nantigen/antibody of interest.
+#'     param = "alpha",  #A [character] string specifying parameter of
+#'     # interest.
+#'     strat = "typhi")  #A [character] string specifying
+#'     # stratification of interest.
+#'     }
path: R/plot_jags_effectivedx.R
user: login: sschildhauer
body: Moved example to separate folder. 
created_at: 2025-03-07T06:16:42Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,95 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [base::list()] of [ggplot2::ggplot()] objects showing the 
path: R/plot_jags_effectivedx.R
user: login: sschildhauer
body: Took parentheses out. 
created_at: 2025-03-07T06:17:00Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,83 @@
+
+#' @title Plot Effective Sample Size Diagnostics
+#' @author Sam Schildhauer
+#' @description
+#'  plot_jags_effect() takes a [list] output from [serodynamics::run_mod()]
+#'  to create summary diagnostics for each chain run in the mcmc estimation.
+#'  Defaults will produce every combination of antigen/antibody, parameters,
+#'  and stratifications, unless otherwise specified. At least 2 chains are 
+#'  required to run function. 
+#'  Antigen/antibody combinations and stratifications will vary by analysis.
+#'  The antibody dynamic curve includes the following parameters:
+#'  - y0 = baseline antibody concentration
+#'  - y1 = peak antibody concentration
+#'  - t1 = time to peak
+#'  - r = shape parameter
+#'  - alpha = decay rate
+#' @param data A [list] outputted from run_mod().
+#' @param iso Specify [character] string to produce plots of only a
+#' specific antigen/antibody combination, entered with quotes. Default outputs
+#' all antigen/antibody combinations.
+#' @param param Specify [character] string to produce plots of only a
+#' specific parameter, entered with quotes. Options include:
+#' - `alpha` = posterior estimate of decay rate
+#' - `r` = posterior estimate of shape parameter
+#' - `t1` = posterior estimate of time to peak
+#' - `y0` = posterior estimate of baseline antibody concentration
+#' - `y1` = posterior estimate of peak antibody concentration
+#' @param strat Specify [character] string to produce plots of specific
+#' stratification entered in quotes.
+#' @return A [list] of [ggplot2::ggplot] objects showing the 
+#' proportion of effective samples taken/total samples taken for all parameter
+#' iso combinations. The estimate with the highest proportion of effective
+#' samples taken will be listed first.
+#' @export
+#' @example inst/examples/examples-plot_jags_effectivedx.R
+
+
+plot_jags_effect <- function(data,
+                             iso = unique(data$curve_params$Iso_type),
+                             param = unique(data$curve_params$Parameter_sub),
+                             strat = unique(data$curve_params$Stratification)) {
+  visualize_jags <- data[["curve_params"]]
+  attributes_jags <- data[["attributes"]]
+
+  eff_strat_list <- list()
+  for (i in strat) {
+
+    visualize_jags_sub <- visualize_jags |>
+      dplyr::filter(.data$Stratification == i)
+
+    # Creating open list to store ggplots
+    eff_out <- list()
+    # Looping through the isos
+    for (j in iso) {
+      visualize_jags_plot <- visualize_jags_sub |>
+        dplyr::filter(.data$Iso_type == j)
+
+      # Will not loop through parameters, as we may want each to show on the
+      # same plot by default.
+      visualize_jags_plot <- visualize_jags_plot |>
+        dplyr::filter(.data$Parameter_sub %in% param)
+
+      visualize_jags_plot <- visualize_jags_plot |>
+        # Changing parameter name to reflect the input
+        dplyr::mutate(Parameter = paste0("iso = ", j, ", parameter = ",
+                                         .data$Parameter_sub, ", strat = ",
+                                         i))
+      # Assigning attributes, which are needed to run ggs_density
+      attributes(visualize_jags_plot) <- c(attributes(visualize_jags_plot),
+                                           attributes_jags)
+      # Creating density plot
+      eff <- ggmcmc::ggs_effective(visualize_jags_plot) +
+        theme_bw()
+      eff_out[[j]] <- eff
+    }
+    eff_strat_list[[i]] <- eff_out
+  }
+  #Printing only one plot if only one exists.
+  if (length(eff_strat_list) == 1) {
+    eff_strat_list <- eff_strat_list[[1]][[iso]]
path: R/plot_jags_effectivedx.R
user: login: d-morrison
body: same concern as in the other PRs; please test what happens with one stratum, multiple isos
created_at: 2025-03-12T04:20:02Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,10 @@
+
+if (!is.element(runjags::findjags(), c("", NULL))) {
+
+  data <- serodynamics::nepal_sees_jags_post
+
+  plot_jags_effect(data = data,
+                   iso = "HlyE_IgA",
+                   param = "alpha",
path: inst/examples/examples-plot_jags_effectivedx.R
user: login: d-morrison
body: maybe plot all params like in the unit test; the example plot only has one plotted point right now, which doesn't really illustrate what this function is for:
https://ucd-serg.github.io/serodynamics/preview/pr66/reference/plot_jags_effect.html#ref-examples
created_at: 2025-03-12T04:24:09Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,10 @@
+
+if (!is.element(runjags::findjags(), c("", NULL))) {
path: inst/examples/examples-plot_jags_effectivedx.R
user: login: d-morrison
body: can we delete this if() condition?
created_at: 2025-03-12T04:25:07Z
author_association: COLLABORATOR
diff_hunk: @@ -76,7 +76,7 @@ plot_jags_effect <- function(data,
     eff_strat_list[[i]] <- eff_out
   }
   #Printing only one plot if only one exists.
-  if (length(eff_strat_list) == 1) {
+  if (sum(lengths(eff_strat_list)) == 1) {
path: R/plot_jags_effectivedx.R
user: login: d-morrison
body: I was unaware of the `lengths()` function; thanks!
created_at: 2025-03-12T17:58:53Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-06T06:37:44Z
body: ## [Codecov](https://app.codecov.io/gh/UCD-SERG/serodynamics/pull/66?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=UCD-SERG) Report
Attention: Patch coverage is `96.00000%` with `1 line` in your changes missing coverage. Please review.

:white_check_mark: All tests successful. No failed tests found.

| [Files with missing lines](https://app.codecov.io/gh/UCD-SERG/serodynamics/pull/66?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=UCD-SERG) | Patch % | Lines |
|---|---|---|
| [R/plot\_jags\_effectivedx.R](https://app.codecov.io/gh/UCD-SERG/serodynamics/pull/66?src=pr&el=tree&filepath=R%2Fplot_jags_effectivedx.R&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=UCD-SERG#diff-Ui9wbG90X2phZ3NfZWZmZWN0aXZlZHguUg==) | 96.00% | [1 Missing :warning: ](https://app.codecov.io/gh/UCD-SERG/serodynamics/pull/66?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=UCD-SERG) |

| [Files with missing lines](https://app.codecov.io/gh/UCD-SERG/serodynamics/pull/66?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=UCD-SERG) | Coverage Δ | |
|---|---|---|
| [R/plot\_jags\_effectivedx.R](https://app.codecov.io/gh/UCD-SERG/serodynamics/pull/66?src=pr&el=tree&filepath=R%2Fplot_jags_effectivedx.R&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=UCD-SERG#diff-Ui9wbG90X2phZ3NfZWZmZWN0aXZlZHguUg==) | `96.00% <96.00%> (ø)` | |

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-06T06:38:19Z
body: <!-- pkgdown-deploy: do not delete/edit this line -->
_:closed_book: Preview documentation for this PR has been cleaned up._
author_association: CONTRIBUTOR
user: login: sschildhauer
created_at: 2025-03-07T07:17:56Z
body: I fixed requested comments by Ezra  and moved example to inst/examples/. I am now getting an error that I don't quite understand. It looks like it is an error in n() from dplyr::summarize... but I am confused as to why an error would now be triggered from dplyr. I didn't really change much that would seem to set off a trigger and examples are running fine when I conduct them internally with addins. 
author_association: COLLABORATOR
user: login: d-morrison
created_at: 2025-03-10T23:47:27Z
body: > I fixed requested comments by Ezra and moved example to inst/examples/. I am now getting an error that I don't quite understand. It looks like it is an error in n() from dplyr::summarize... but I am confused as to why an error would now be triggered from dplyr. I didn't really change much that would seem to set off a trigger and examples are running fine when I conduct them internally with addins.

@sschildhauer @Kwan-Jenny Sorry to jump in unrequested; the error you're facing is a tricky one that I'm partly to blame for, so I want to help fix it. The error is actually coming from `ggmcmc::ggs_effective()`, which I can tell from the `Backtrace` part of the error in https://github.com/UCD-SERG/serodynamics/actions/runs/13715802955/job/38360355850?pr=66:

![image](https://github.com/user-attachments/assets/98c7430e-5b5e-4312-8cef-8134978f69b1)

The problem is that `ggmcmc` [doesn't import](https://github.com/xfim/ggmcmc/blob/352a0f8139a023cc30db9f18477419f8cf4f7623/R/help.R#L12) the function `n()` from `dplyr`, and attempts to use it [here](https://github.com/xfim/ggmcmc/blob/352a0f8139a023cc30db9f18477419f8cf4f7623/R/ggs_effective.R#L33)

That's normally not a problem for ggmcmc because it [`Depends`](https://r-pkgs.org/dependencies-in-practice.html#sec-dependencies-in-depends) on `dplyr` ([here](https://github.com/xfim/ggmcmc/blob/352a0f8139a023cc30db9f18477419f8cf4f7623/DESCRIPTION#L14)).
But if you never call `library(ggmcmc)`, then `dplyr` never gets loaded, so `n()` isn't defined for use in `ggmcmc::ggs_effective()`.
You're not encountering this problem on your local computers because I unwisely set up dplyr to get automatically loaded when R starts in an interactive session, using the [`.Rprofile` file](https://github.com/UCD-SERG/serodynamics/blob/250f1a612582447451a74da3f1d2baba3da04809/.Rprofile#L11) in the serodynamics package. But that .Rprofile code doesn't get run on Github, so `n()` never gets defined.

In the long-term, we should ask the ggmcmc authors to import `n()` from `dplyr` (by filing an [Issue](https://github.com/xfim/ggmcmc/blob/master/R/help.R#L12) with a [reprex](https://reprex.tidyverse.org/)). 

But in the short term, we can fix this problem for ourselves by importing `n()` into `serodynamics`, with `usethis::use_import_from("dplyr", "n")`. 

We should also edit `.Rprofile` to not auto-load `dplyr`; I'll do that now.
author_association: COLLABORATOR
user: login: sschildhauer
created_at: 2025-03-11T13:29:20Z
body: Thank you for reviewing Ezra, this was a tough one. I used use_import_from to add n().. unsure why it still is not passing. 
author_association: COLLABORATOR
user: login: d-morrison
created_at: 2025-03-12T04:17:58Z
body: > Thank you for reviewing Ezra, this was a tough one. I used use_import_from to add n().. unsure why it still is not passing.

@sschildhauer @Kwan-Jenny I'm not sure why either, but I noticed that ggmcmc actually fixed this issue on their end in their most recent commit (https://github.com/xfim/ggmcmc/commit/352a0f8139a023cc30db9f18477419f8cf4f7623, May 12, 2023). They haven't updated their [CRAN ](https://cran.r-project.org/web/packages/ggmcmc/index.html) version since 2021-02-10. I pinged them about that [here](https://github.com/xfim/ggmcmc/issues/82); hopefully they'll update the CRAN version soon, but in the meantime we can use the github version.

I added commit https://github.com/UCD-SERG/serodynamics/pull/66/commits/12e151f43589d843a1c96b452aedae40fc10be93 to this branch; it modifies `DESCRIPTION` accordingly; on your local computers you should run `devtools::install_github("xfim/ggmcmc")` to make sure you have the latest version of ggmcmc

author_association: COLLABORATOR