Identification-through-group-testing"

knitr::opts_chunk$set(
  prompt = TRUE, 
  comment = NA, 
  background = "white", 
  tidy = TRUE,
  tidy.opts = list(width.cutoff = 65, blank = TRUE),
  collapse = TRUE,
  comment = "#>"
)

options(continue = " ") # Removes a "+" from a line of code that continues on to the next line
library(binGroup2)

We provide a few examples here showing how to use binGroup2 for the identification aspect of group testing. More in-depth examples are available in Bilder et al. (2023), which has been tentatively accepted by the R Journal at the time of writing this vignette. For further information on group testing, please see www.chrisbilder.com/grouptesting. This website also includes the most recent version of Bilder et al. (2023).

Operating characteristics

GroupMembershipMatrix <- function(stage1, stage2 = NULL,
                                  stage3 = NULL, stage4 = NULL) {

  # Check group sizes
  if (!is.null(stage2)) {
    if (sum(stage2) != stage1) {
      stop("The number of individuals in stage 2 is not the same as in stage 1.\n")
    }

    if (is.null(stage1)) {
      stop("Please provide an initial group size for stage 1 of testing.\n")
    }
  }

  if (!is.null(stage3)) {
    if (sum(stage3) != stage1) {
      stop("The number of individuals in stage 3 is not the same as in stage 1.\n")
    }

    if (is.null(stage1)) {
      stop("Please provide an initial group size for stage 1 of testing.\n")
    }

    if (is.null(stage2)) {
      stop("No group sizes were provided for stage 2 of testing. A group membership matrix will be constructed for a two-stage hierarchical algorithm.\n")
    }
  }

  if (!is.null(stage4)) {
    if (sum(stage4) != stage1) {
      stop("The number of individuals in stage 3 is not the same as in stage 1.\n")
    }

    if (is.null(stage1)) {
      stop("Please provide an initial group size for stage 1 of testing.\n")
    }

    if (is.null(stage2)) {
      stop("No group sizes were provided for stage 2 of testing. A group membership matrix will be constructed for a two-stage hierarchical algorithm.\n")
    }

    if (is.null(stage3)) {
      stop("No group sizes were provided for stage 3 of testing. A group membership matrix will be constructed for a three-stage hierarchical algorithm.\n")
    }

  }

  # Create group membership matrix for two-stage hierarchical testing
  if (is.null(stage2)) {
    # Set row1
    row1 <- rep(x = 1, time = stage1)

    # Set row2
    row2 <- 1:stage1

    # Set hier.config
    hier.config <- matrix(data = c(row1, row2), nrow = 2, byrow = TRUE)
    }

  # Create group membership matrix for three-stage hierarchical testing
  else if (is.null(stage3)) {
    # Set row1
    row1 <- rep(x = 1, time = stage1)

    # Set row2
    row2 <- NULL # Initiate row2
    for (i in 1:length(stage2)) {
      row2 <- c(row2, rep(x = i, time = stage2[i]))
      }

    # Set row3
    row3 <- 1:stage1

    # Set hier.config
    hier.config <- matrix(data = c(row1, row2, row3), nrow = 3, byrow = TRUE)
    }

  # Create group membership matrix for four-stage hierarchical testing
  else if (is.null(stage4)) {
    # Set row1
    row1 <- rep(x = 1, time = stage1)

    # Set row2
    row2 <- NULL # Initiate row2
    for (i in 1:length(stage2)) {
      row2 <- c(row2, rep(x = i, time = stage2[i]))
      }

    # Set row3
    row3 <- NULL # Initiate row3
    for (i in 1:length(stage3)) {
      row3 <- c(row3, rep(x = i, time = stage3[i]))
      }

    # Set row4
    row4 <- 1:stage1

    # Set hier.config
    hier.config <- matrix(data = c(row1, row2, row3, row4),
                          nrow = 4, byrow = TRUE)
    }

  else {
    # Set row1
    row1 <- rep(x = 1, time = stage1)

    # Set row2
    row2 <- NULL # Initiate row2
    for (i in 1:length(stage2)) {
      row2 <- c(row2, rep(x = i, time = stage2[i]))
      }

    # Set row3
    row3 <- NULL # Initiate row3
    for (i in 1:length(stage3)) {
      row3 <- c(row3, rep(x = i, time = stage3[i]))
      }

    # Set row4
    row4 <- NULL # Initiate row3
    for (i in 1:length(stage4)) {
      row4 <- c(row4, rep(x = i, time = stage4[i]))
      }

    # Set row5
    row5 <- 1:stage1

    # Set hier.config
    hier.config <- matrix(data = c(row1, row2, row3, row4, row5),
                          nrow = 5, byrow = TRUE)
    }

  hier.config
}

The opChar1() and opChar2() functions compute operating characteristics, such as the expected number of tests, for a group testing algorithm. Below is an example of how opChar1() can be used for two-stage hierarchical testing (also known as Dorfman testing) with a one-infection assay. In this example, we use an overall prevalence of 0.01, a sensitivity and specificity of 0.99 at each stage, and an initial group size of 10.

# Group membership matrix
group.member <- GroupMembershipMatrix(stage1 = 10)
group.member

# Compute operating characteristics for a one-infection assay
save1 <- opChar1(algorithm = "D2", p = 0.01, Se = 0.99, Sp = 0.99, 
                 hier.config = group.member, print.time = FALSE)
names(save1)
summary(save1)
ExpTests(save1)

The group membership matrix shows that each individual is tested in group 1 for the first stage. If this group tests positive, each individual is tested separately in the second stage. The opChar1() function returns its calculations in a list object that is then summarized by summary(). For example, the expected number of tests is 2.04 for this group. Additional functions, like ExpTests(), can access the information available in objects created by opChar1() as well.

The opChar2() function performs very similar calculations but for two-infection assays. Below is an example using two-stage hierarchical testing again. Because there are two infections, a vector of joint probabilities is specified in the form $(p_{--},p_{+-},p_{-+},p_{++})$, where $p_{ab}$ is the probability of being positive/negative $(+/-)$ for infections $a$ and $b$. We continue using a sensitivity and specificity of 0.99 at each stage and an initial group size of 10.

# Compute operating characteristics for a two-infection assay
save2 <- opChar2(algorithm = "D2", p.vec = c(0.95, 0.02, 0.02, 0.01), 
                 Se = c(0.99, 0.99), Sp = c(0.99, 0.99), 
                 hier.config = group.member, print.time = FALSE)
names(save2)
summary(save2)
ExpTests(save2)

The expected number of tests is 5.10 for this testing configuration.

Optimal testing configuration

The OTC1() and OTC2() functions find the optimal testing configuration for a group testing algorithm. Below is an example of how OTC1() can be used for two-stage hierarchical testing with a one-infection assay. In this example, we again use an overall prevalence of 0.01 and a sensitivity and specificity of 0.99 at each stage. The OTC is searched for over group sizes of 3 to 20.

# Find OTC for a one-infection assay
save3 <- OTC1(algorithm = "D2", p = 0.01, Se = 0.99, Sp = 0.99, 
              group.sz = 3:20, obj.fn = "ET", print.time = FALSE)
names(save3)
summary(save3)
Config(save3)

The OTC has a group size of 11 with an expected number of tests per individual of 0.2035. This testing configuration is slightly more efficient than using a group size of 10 as specified in our previous example. This can be seen by comparing the summary() outputs or from the Config() function results which accesses information stored in save3.

The OTC2() function works in much the same way as OTC1() with the main difference being the specification of joint probabilities rather a single-infection prevalence. In the example below, we search for the OTC over group sizes of 3 to 20 when using a two-stage hierarchical testing algorithm.

# Find OTC for a two-infection assay
save4 <- OTC2(algorithm = "D2", p.vec = c(0.95, 0.02, 0.02, 0.01), 
              Se = c(0.99, 0.99), Sp = c(0.99, 0.99), 
              group.sz = 3:20, obj.fn = "ET", print.time = FALSE)
names(save4)
summary(save4)
Config(save4)

The OTC has a group size of 5 with an expected number of tests per individual of 0.4399. To determine how much more efficient the OTC is in comparison to our previous two-infection example's use of a group size of 10, we use the CompareConfig() function.

# Compare testing configurations
group.member.OTC <- GroupMembershipMatrix(stage1 = 5)
save5 <- opChar2(algorithm = "D2", p.vec = c(0.95, 0.02, 0.02, 0.01), 
                 Se = c(0.99, 0.99), Sp = c(0.99, 0.99), 
                 hier.config = group.member.OTC, print.time = FALSE)
CompareConfig(save2, save5)

The OTC is 13.81% more efficient than using a group size of 10.

This vignette focuses on a two-stage hierarchical testing algorithm in a homogeneous population. Many other group testing algorithms are available! We provide a large number of examples within the help for the package to illustrate these other algorithms. Our Bilder et al. (2023) paper also provides many examples. In particular, the appendix of the paper provides more advanced uses of functions in the package.



Try the binGroup2 package in your browser

Any scripts or data that you put into this service are public.

binGroup2 documentation built on Nov. 14, 2023, 9:06 a.m.