First development version.
sdt_transformer() and its fit and predict plumbing) is now documented,
and sdt_read_gold_standard(), sdt_apply_gold_standard(),
sdt_triage_accuracy(), sdt_fit_triage_error(), sdt_simulate_triage()
and sdt_error_fidelity() are exported, as their documentation already
declared.format() and print() methods for sdt_simulated_triage and
sdt_triage_error_model objects, and the tibble::tbl_sum() methods for
sdt_error_fidelity, sdt_gold_standard and sdt_triage_accuracy
tables, are now registered, so these objects print with their own
headers everywhere, not only from inside the package.sdt_export_simulation() returns the archive path as it was passed, with
only ~ expanded. It returned a normalised spelling of the same file,
which differs from the caller's on macOS (/private/var) and Windows.sdt_workflowset() skips the transformer, and names it in skipped,
when keras3 is installed but has no working backend; the warning says
how to install one. Its fit used to abort and take every other engine's
row down with it. The transformer is now fitted after the other engines,
so Python is not loaded before their parallel workers start. The
workbench's skip notice gives each skipped engine its own reason.The reference distributions were re-read against the open-access paper and extended from four pinned quantities to the full published set. Several package defaults that contradicted the patient card reproduced in the source's Fig. 1 were corrected. These are breaking changes; the package is pre-1.0 and the previous values were never published ones.
sdt_therapy_levels() gains "none", the card's "no operation" option. A
casualty carrying it is never routed to theatre.sdt_diagnostics() vocabulary: "efast", "xray", "ct". X-ray was
absent, though the source reports its share.sdt_board_areas() gains "green_area", the board's green treatment area.
Green casualties are cared for there rather than being sent to a ward bed.sdt_reference() gains version, serving both evaluated versions of the
exercise, and a design part carrying the exercise as specified: 90 minutes,
20 patient cards, four card rounds, the team roles, and the nine-area board
with its layout.summary.game_run() and sdt_plot_end_state() gain version, and now fold
the board onto the eight locations the source reports rather than
comparing three and lumping the rest. The two foldings that are modelling
choices rather than published facts are recorded in
sdt_reference("defaults")$end_state_map.data-raw/calibrate.R, a re-runnable parallel search. The previous
calibration matched three locations while holding roughly twice the
published share of casualties in the emergency department; the error was
inside the cell that absorbed it.summary.game_run() dropped casualties that generated no events, shrinking
the denominator and making the source's "not yet seen" category unreachable.
Every casualty is now accounted for in every replication.sdt_decision_paths() dropped the same casualties. They now carry a
single-step "not_seen" path.sdt_simulate(horizon = Inf) produced NaN arrival times.sdt_plot_board() draws the nine board areas laid out as in the
source's Fig. 1, with every casualty as a point in the area it had reached
at a given moment. Pass interactive = TRUE for \pkg{ggiraph} tooltips
carrying the patient card.sdt_patient_card(), sdt_cohort() and sdt_scenario() form the data
model: a casualty, an ordered set of them, and a board to run them across.sdt_cohort() returns a validated tibble subclass. Validation is vectorised
over columns, so a bad import reports every offending row and column at once
instead of aborting on the first.format() methods for patient_card, scenario, game_run and
sdt_model, with print() delegating to them so the two cannot drift.as_tibble() and autoplot() methods for a scenario;
sdt_as_patient_cards() for the reverse direction.sdt_reference() returns the marginals the package calibrates against,
loaded lazily from inst/extdata and cached.published (attributed to Achatz et al.
2023) from defaults (modelling choices made where the source pins no
value). A calibration test that mixed the two would be checking the model
against its own assumptions.sdt_reference_scenario() and sdt_reference_horizon() build the board the
engine was calibrated on, so the calibration claim can be re-run.sdt_synthetic_cohort() gains a simstudy backend drawing correlated
age, injury severity and life points, with the sighting category derived
from acuity by rule. The base backend is the always-available reference
implementation and is pinned by a golden test at seed 1.sdt_synthesise() produces a releasable replica of a real seed cohort with
synthpop's sequential CART models. Identifiers are regenerated rather
than synthesised, since they carry row identity rather than structure.sdt_synthesis_utility() scores a replica against its seed per variable, so
it can be judged before release.sdt_add_decisions() simulates the category a participant assigns, using
the published per-category hit rates, with miscalls weighted toward
neighbouring categories.sdt_simulate() routes casualties by sighting category and therapy concept
rather than sending everyone through theatre, with diagnostics, theatre and
critical-care probabilities from the reference defaults.events carries both duration and service_time. They differ whenever a
casualty is finished but waiting for a bed downstream, and conflating them
made theatre times look like whatever the downstream bottleneck was.summary() on a game_run reports the end-of-game location distribution,
optionally beside the published reference.parallel = FALSE.sdt_decision_paths() returns the full ordered pathway per casualty per
replication, in long or state-sequence-wide form.sdt_decision_consistency() is rebuilt on distance rather than modal
agreement. The previous measure compared each casualty's single recorded
area with itself and was therefore always exactly 1. It now offers mean
pairwise edit distance across replications, or normalised longitudinal
entropy. A single path scores NA, not a perfect 1: there is nothing to
compare it with.sdt_seqdef() bridges to TraMineR over a fixed board-area alphabet, so
runs stay comparable; sdt_seq_cluster() groups pathways by
optimal-matching distance and partitioning around medoids.sdt_plot_sequences() draws index and state-distribution views in
ggplot2 rather than base graphics, so they theme like every other plot.sdt_fit() is a full tidymodels path: preprocessing recipe, stratified
resamples, tuning grid, and a final fit on a held-out split. The split and
resamples are kept, so reported numbers can be traced to what produced them.sdt_workflowset() screens every installed engine onto one leaderboard and
names what it skipped.sdt_engines(), sdt_metric_set(), sdt_metrics(),
sdt_predictions(), sdt_roc() and sdt_consistency_score().sdt_importance(), sdt_profile() and sdt_shap() wrap DALEX, all
returning ggplot2 objects and all degrading with an actionable message
when DALEX is absent.sdt_run_app() launches a six-panel workbench: scenario, simulate,
annotate, model lab, explain and sequences, plus a one-click self-contained
HTML report of the session.R/ rather than scripts
under inst/. Code under inst/ is invisible to R CMD check, lintr and
covr, so an app kept there is the one part of a package nothing verifies.
Every module is now driven headlessly by shiny::testServer().sdt_simulate() passed at = to simmer::add_generator() as a named
argument rather than as at(), so no simulation ran at all.cli format strings would have aborted at format time: one pluralising
with no quantity, one interpolating a dot-prefixed expression that cli reads
as a style name. Both sat on error paths, where they would not have been
noticed. Static tests now guard both patterns, along with line length,
library() calls and bare T/F.ventilated is logical, and recipes counts logicals as neither nominal nor
numeric, so it slipped past both selectors and reached glmnet and xgboost as
a column they could not put in a numeric matrix.tune() markers must appear literally in a model specification: passed
through a helper they evaluate to the same object, but parsnip inspects the
expression, so the parameter set came back empty and the placeholder reached
the engine.DALEX::loss_cross_entropy() looks the observed class up by name in the
probability matrix, and tidymodels names those columns .pred_red; every
lookup was out of bounds until the prefix was stripped.