Skip to contents

Draws teams that were never in the data and, for each simulated team and each card, a called sighting category given the card's true one. It is the generative counterpart of sdt_fit_triage_error(): where that fits the rows of the confusion matrix, this replays them.

Usage

sdt_simulate_triage(
  model,
  n_groups = 20L,
  cards = NULL,
  seed = NULL,
  new_teams = TRUE,
  nsim = 1L,
  team_effects = TRUE
)

Arguments

model

An sdt_triage_error_model from sdt_fit_triage_error().

n_groups

Number of simulated teams per replicate.

cards

Cards to deal to each team. NULL uses every card in the fitted frame; anything passed must be a subset of them.

seed

Optional integer seed, applied with withr::local_seed().

new_teams

If TRUE (default), redraw the team and session effects, so every simulated team is new. If FALSE, reuse the fitted conditional modes of the first n_groups fitted teams, which is a posterior-predictive check rather than a generator.

nsim

Number of replicates. Replicates are stacked and carry a .replicate column; sim_group is unique across the whole result.

team_effects

If FALSE, simulate from the null fits – the same cells with (1 | group) removed. This is the counterfactual that shows what the team term is doing, and it is a different model rather than the same one with the draw switched off.

Value

An S3 object of class sdt_simulated_triage, a tibble with .replicate, sim_group, card, triage, called_triage, and the hit, over and under logicals. Carries the model, the call, a draws attribute holding the random effects used, and a gaps attribute counting the cells left NA.

Details

Each simulated team gets its own random effect per cell, drawn from N(0, sd_group), plus a session effect from N(0, sd_block). For a card of true category k the call is then a two-step draw: correct with probability plogis(card effect + team effect + session effect) from the hit cell, and otherwise one of the two wrong categories from the split cell. That is the confusion-matrix row, redrawn for a new team.

What comes back as NA

A cell that was never fitted is never guessed. If a category's hit cell is missing, every card of that category returns NA; if only its split cell is missing, the correct calls stand and the errors return NA. A card with no coefficient in a cell – a card the split part never saw, because no team miscalled it – returns NA for the same reason. Every one of these is counted in the result's gaps attribute and warned about once.

What is not reproduced

The two parts are drawn independently, so a simulated team's accuracy and its error direction are uncorrelated. That is the one parameter the decomposition gives up, and joint = TRUE in sdt_fit_triage_error() measures it. Card-to-card dependence within a team – a team that has just over-triaged three greens in a row – is absent as well: cards are drawn independently given the team.

Examples

set.seed(1)
cards <- sprintf("c%02d", seq_len(12))
truth <- rep(c("red", "yellow", "green"), each = 4)
d <- expand.grid(card = cards, group_id = sprintf("g%02d", seq_len(20)),
                 stringsAsFactors = FALSE)
d$session_id <- ifelse(d$group_id < "g11", "s1", "s2")
d$triage <- truth[match(d$card, cards)]
b <- stats::rnorm(20, sd = 1)
p <- stats::plogis(1 + b[match(d$group_id, unique(d$group_id))])
hit <- stats::runif(nrow(d)) < p
wrong <- vapply(seq_len(nrow(d)), function(i) {
  sample(setdiff(sdt_triage_levels(), d$triage[[i]]), 1)
}, character(1))
d$called_triage <- ifelse(hit, d$triage, wrong)
if (requireNamespace("lme4", quietly = TRUE)) {
  fit <- sdt_fit_triage_error(d, verbose = FALSE)
  sdt_simulate_triage(fit, n_groups = 5, seed = 1)
}
#> Warning: ! 1 cell fitted a zero team variance: "green.split".
#> ✖ Simulated teams will be identical there.
#> ℹ That is a finding about the cell, not a failure of the fit.
#> <sdt_simulated_triage> 5 new teams x 12 cards in 1 replicate
#> hit 0.717  over 0.083  under 0.200
#> # A tibble: 60 × 8
#>    .replicate sim_group card  triage called_triage hit   over  under
#>  *      <int> <chr>     <chr> <chr>  <chr>         <lgl> <lgl> <lgl>
#>  1          1 g0001     c01   red    red           TRUE  FALSE FALSE
#>  2          1 g0001     c02   red    red           TRUE  FALSE FALSE
#>  3          1 g0001     c03   red    yellow        FALSE FALSE TRUE 
#>  4          1 g0001     c04   red    red           TRUE  FALSE FALSE
#>  5          1 g0001     c05   yellow red           FALSE TRUE  FALSE
#>  6          1 g0001     c06   yellow green         FALSE FALSE TRUE 
#>  7          1 g0001     c07   yellow green         FALSE FALSE TRUE 
#>  8          1 g0001     c08   yellow green         FALSE FALSE TRUE 
#>  9          1 g0001     c09   green  green         TRUE  FALSE FALSE
#> 10          1 g0001     c10   green  green         TRUE  FALSE FALSE
#> # ℹ 50 more rows