Skip to contents

PTSDdiag 0.5.0

This release adds two things. The first is the redundancy analysis the preprint’s headline claim rests on: the best symptom combination is rarely alone, and reporting only the winner overstates how much the data single it out. The second makes the package self-sufficient for multi-site validation — a site that cannot share its data can now receive a set of symptom combinations derived elsewhere, evaluate them locally, and return a releasable summary, without any analysis code living outside the package and without reaching into unexported internals.

New features

Redundancy analysis

  • compute_plateau() returns every combination whose balanced accuracy falls within delta percentage points of the best in the same search, as a count and as a proportion of that rule form’s own candidate space. The denominators matter: a six-item hierarchical search evaluates 13,685 combinations against the non-hierarchical search’s 38,760, so the same plateau count means different things. delta defaults to 1 percentage point and accepts a vector, so a primary and secondary plateau come from one call.
  • bootstrap_stability() re-runs the entire exhaustive search on bootstrap resamples of the respondents and reports, for every combination, how often it stayed within delta of that replicate’s best (pi) and how often it was the outright winner (p_argmax, with ties shared equally). Taking the reference point within each replicate rather than from the original sample keeps every comparison on the scale of its own resample. Note that pi for the combination that won in the original sample is optimistic, since replicates share most of their observations with the sample that selected it; the documentation says so. Per-replicate plateau sizes are returned as well, for a percentile interval across replicates.
  • symptom_selection() reports how often each item appears across the plateau against the chance baseline for that rule form, computed by enumeration rather than assumed. Raw selection counts are zero-sum and cannot be read alone. The baseline is flat at n_symptoms / 20 without a cluster constraint, but under the hierarchical rule it ranges from 25.2% for each cognition-and-mood item to 55.8% for each avoidance item, so a flat comparison would make two-item clusters look preferred when they are merely unavoidable. A stability weighted variant is available when a bootstrap_stability() result is supplied.
  • plot_symptom_selection() draws those frequencies with the chance baseline marked item by item, or as a ratio to chance.
  • compare_rule_forms() runs the above across a named list of searches and stacks the results into long data frames with a rule_form column, ready for a manuscript table.

Multi-site transport and release

  • evaluate_sets() scores a supplied list of symptom combinations against the full DSM-5-TR PCL-5 diagnosis. Where score_all_combinations() enumerates and scores a whole candidate space, this scores exactly the combinations it is given, which is what a validation site needs when the search happened elsewhere. It uses the same collapsed-pattern matrix path as bootstrap_stability(), so cost scales with the number of distinct response patterns rather than with sample size, and it reproduces score_all_combinations() exactly.
  • transport_plateau() turns those per-combination results into something a site may release: a five-number summary of balanced accuracy, sensitivity and specificity across each transported plateau, plus two-by-two counts for a short, pre-specified list of named combinations. The combination-to-value linkage is discarded deliberately — per-combination counts across a whole candidate space are enough constraints to recover the response-pattern distribution, and in a small sample a rare pattern is effectively an individual record. The function has no argument that emits per-combination detail for a plateau, so it cannot be released by accident.
  • write_transport() and read_transport() serialise a return bundle. Every filename is prefixed with the sample identifier and every table carries sample_id and payload_id columns, so bundles from different sites stay distinguishable as loose files in one directory. dir is required and must already exist, matching write_combinations(): the package never picks a location for you and never creates directories, so a mistyped path fails instead of scattering files. session_info is off by default.
  • diagnostic_metrics() computes every metric a two-by-two table supports, with intervals, recomputed from the counts. The interval functions behind it are exported too: wilson_ci(), exact_ci() (Clopper-Pearson, for small denominators), ba_ci(), lr_ci() (Simel) and kappa_ci(). ba_ci() combines the component intervals by Newcombe square-and-add (see Bug fixes), and every interval here is documented as marginal: none is a valid basis for comparing two combinations, which are paired on the same individuals.
  • five_number() reduces a vector to the six numbers a site may release.
  • icd11_items() exposes the six PCL-5 items create_icd11_diagnosis() scores, so a table describing the operationalisation is generated from the same definition that is applied and cannot drift from it.
  • chance_baseline() and n_candidates() expose the size of a candidate space and the proportion of it containing each item. Under a cluster constraint the baseline is a step function of the cluster rather than a constant, because a two-item cluster contributes far more of its combinations than a seven-item one — which is what makes a raw selection frequency misleading on its own.
  • set_id_to_items(), format_set_items(), format_set_labels() and pcl5_item_labels() convert between combination ids and item numbers or labels. as_set_matrix() accepts a combination specification in any of the three shapes the package produces.

Bug fixes

  • ba_ci() no longer returns a zero-width interval when sensitivity or specificity is at a boundary. The analytic variance Var(BA) = 0.25 (Var(sens) + Var(spec)) is degenerate there, because p(1-p)/n is zero, so a table with six positives all classified correctly reported 1.00 (1.00, 1.00) — no uncertainty at all from 183 observations, while its own component intervals were correctly wide. The default is now Newcombe square-and-add of the component intervals, which is not degenerate at a boundary: the same table gives 1.00 (0.80, 1.00) under the default method = "wilson", and 1.00 (0.77, 1.00) under method = "exact". The analytic form stays available as method = "wald" and warns when a component sits at 0 or 1.
  • diagnostic_metrics(ci = ) now reaches balanced accuracy. It previously applied the requested method to the proportions but always used the analytic form for their mean, so ci = "exact" returned Clopper-Pearson bounds on sensitivity and specificity next to a Wald interval on balanced accuracy. ba_method overrides.
  • The hierarchical rule could accept a set spanning one cluster too few. When clusters did not cover all 20 items, .diagnose_combination() counted items belonging to no cluster as though they formed a cluster of their own: the lookup table was sized to the highest clustered item, so an endorsed item above that index read as NA and one inside the range but outside every cluster read as 0, and unique() then counted either as a distinct cluster. With clusters = list(B = 1:5, C = 6:7, D = 8:14), a respondent endorsing items 1, 6 and 15 was diagnosed under a 3-of-3 hierarchical rule despite spanning only two of the three clusters. Items outside every cluster now contribute nothing towards spanning. Results are unchanged for the standard DSM-5-TR PCL-5 clusters, which cover all 20 items, so no analysis using the defaults is affected. Affects optimize_combinations_clusters(), score_all_combinations() and apply_symptom_combinations() when custom, non-exhaustive clusters were supplied.

Improvements

  • symptom_frequency() gains Baseline and Enrichment columns and a clusters argument, for the same reason: selection frequencies over a fixed number of combinations are only interpretable against chance. Existing columns are unchanged.
  • plot_symptom_frequency() gains type = "enrichment", filling each tile with the ratio of observed to chance selection on a scale diverging around 1. The default remains type = "relative".

Notes

  • The bootstrap collapses respondents to unique binarized response patterns and expresses each replicate as two matrix products, which is exact rather than an approximation because the reference diagnosis and every decision rule depend on the data only through the binarized responses. A full 2,000-replicate run over all 38,760 six-item combinations takes seconds.
  • Under the hierarchical rule, items belonging to no cluster count towards the endorsement threshold but not towards spanning the clusters. This follows from the bug fix above and holds identically in the per-row rule and the matrix path, which are checked against each other on every run.

PTSDdiag 0.4.1

CRAN release: 2026-07-13

CRAN check-time reduction; no functional changes. The 0.4.0 submission was auto-rejected by CRAN’s incoming pretests solely for exceeding the 10-minute check-time limit on r-devel-windows.

  • Search-heavy tests (exhaustive 6-symptom optimizations, holdout- and cross-validation runs) are now skipped on CRAN via skip_on_cran(); the complete suite continues to run on GitHub Actions for every push. CRAN still exercises the optimizer, the summary tables, the plot, and the whole 0.4.0 evaluation API on compact search spaces and hand-built definitions.
  • Vignettes compute on 120-row subsets (previously 250), the cross-validation demo uses 2 folds, and the external-validation example reuses the holdout-derived combinations instead of running a third full search. All narratives, code paths, and interpretive statements are unchanged and were re-verified against the re-rendered output.

PTSDdiag 0.4.0

This release focuses on the multi-site validation workflow: everything a collaborator site previously had to hand-code around the package is now built in. It also corrects the ICD-11 operationalization to the field-standard six-item mapping (see Corrections — ICD-11 results change). Apart from that correction, no existing call changes behavior.

Corrections

  • ICD-11 re-experiencing now uses PCL-5 items 2-3 (was 1-3). create_icd11_diagnosis() — and everything built on it: the "icd11" fixed criterion in compare_optimizations(), include_icd11 = TRUE in evaluate_definitions(), and the ICD-11 row of the symptom-frequency heatmap — now operationalizes ICD-11 PTSD with the narrow six-item mapping (items 2, 3, 6, 7, 17, 18; at least one symptom per cluster). ICD-11 requires re-experiencing with a here-and-now quality, which nightmares (item 2) and flashbacks (item 3) capture but intrusive memories (item 1) as worded in the PCL-5 do not; this is the mapping used across the published PCL-5-to-ICD-11 literature (Kuester et al. 2017; Schellong et al. 2019; Heeke et al. 2020; Pettrich et al. 2025). All ICD-11 benchmark results change relative to earlier versions; because the six-item rule is strictly more conservative, ICD-11 diagnoses can only become less frequent. The broad seven-item variant remains available as a custom fixed criterion — see the recipe in ?create_icd11_diagnosis.

New features

  • as_definitions() converts combinations imported from JSON into the definitions list evaluate_definitions() expects, with optional top-n truncation and automatic rule labels such as "4/6 Hierarchical". read_combinations() output is now classed ptsdiag_spec and can be passed to evaluate_definitions() directly (single spec or a list of specs — the conversion happens automatically). write_combinations() gains an optional label argument stored in the file, so the derivation site controls how each rule is labelled downstream.
  • evaluate_definitions() gains reference: validate definitions against an external reference standard (e.g. a clinician CAPS diagnosis) supplied as a logical vector, a 0/1 column, or a column name. Rows with a missing reference are excluded with a message, and a "Full 20-item PCL-5" ceiling row is added by default (include_full_pcl5) so the cost of the reduced symptom set can be separated from the intrinsic PCL-5-vs-reference disagreement.
  • evaluate_definitions() gains tidy (and as_percent): return a plain analysis table with Approach / Rank / Combination, the 2x2 counts, and numeric metrics — the same layout as summarize_top_combinations(), so derivation and validation results can be combined with rbind(). No more parsing rule labels out of the formatted display table.
  • score_all_combinations() scores every candidate combination (optionally cluster-constrained) against the DSM-5-TR diagnosis and returns the complete ranked table — the exhaustive companion to optimize_combinations(), for interchangeability (“plateau”) analyses. Chunked, with optional parallel scoring via future.apply like cross_validation().
  • check_pcl5_data(): exported pre-flight check that reports every data problem in one pass (column count, numeric type, integer 0-4 range, missing values) instead of one error at a time, plus an informational note on all-zero rows. Aimed at collaborator sites preparing data for rename_ptsd_columns().
  • New inst/CITATION: citation("PTSDdiag") now points to the paper and the package.

Bug fixes

  • Hierarchical definitions that carry a custom cluster structure (stored in a combinations JSON file) are now evaluated with exactly those clusters; previously evaluate_definitions() silently substituted the default PCL-5 B/C/D/E structure.

PTSDdiag 0.3.5

New features

Behavior changes

  • All optimization and validation functions now default to score_by = "balanced_accuracy" (was "accuracy"). In imbalanced samples (the bundled clinical data is about 94% PTSD-positive) plain accuracy is dominated by the majority class; balanced accuracy weighs performance in the diagnosed and non-diagnosed groups equally. Pass score_by = "accuracy" to reproduce results from earlier versions.
  • score_by = "balanced_accuracy" requires both diagnosed and non-diagnosed cases under the reference criterion; data where every case falls in one class now stops with an informative error suggesting "accuracy" or "sensitivity".
  • create_readable_summary() now requires a balanced_accuracy column in its input. Data frames produced by summarize_ptsd_changes() carry it automatically; hand-built inputs need the additional column.

Documentation

  • All vignettes demonstrate score_by = "balanced_accuracy" as the standard outcome, and the Getting started vignette explains the choice among the three criteria.
  • Vignettes now run on a uniform 250-row subset of the bundled data, so building the package (and the CI checks) is several times faster. The Getting started vignette previously optimized on all 5,000 rows.
  • Removed leftover editorial text from the Getting started vignette, and the PCL-5 total score is now computed on a descriptive copy so the optimizer’s “total column detected” warning no longer appears in the rendered vignette. Fixed typos across the comparison, validation, and multi-site vignettes.
  • README article links point to the current vignettes (the previous links referenced articles removed in 0.3.1).
  • Examples use the same uniform 250-row subset with compact search spaces so that CRAN’s --run-donttest checks stay fast.

Internal

  • Structural tests now run on compact search spaces and reuse expensive comparison objects, cutting the test-suite runtime from about five minutes to under a minute without losing coverage; the canonical 6-of-4 searches are still exercised once per optimizer.

PTSDdiag 0.3.3

Bug fixes

  • create_icd11_diagnosis() now operationalises the ICD-11 “sense of current threat” cluster with PCL-5 items 17 (hypervigilance) and 18 (exaggerated startle), the symptoms the documentation always intended. Earlier versions used items 16 (risk-taking behaviour) and 17, so the ICD-11 benchmark diagnosis, the ICD-11 symptom set reported by compare_optimizations() / symptom_frequency(), and any comparison against ICD-11 are affected. Re-run analyses that benchmarked against ICD-11.

PTSDdiag 0.3.2

New features

Behavior changes

Data

  • simulated_ptsd_genpop now also ships paired clinician-administered CAPS-5 ratings (C1C20) for the same participants, simulated to correlate with the PCL-5 items at a total-score r of about 0.8. The dataset now has 43 columns (demographics + 20 PCL-5 + 20 CAPS-5); the PCL-5 items and demographics are unchanged.

Documentation

  • The CAPS-5 workflow vignette now uses the bundled paired data instead of random CAPS-5 ratings, so the instrument agreement it shows is realistic.

PTSDdiag 0.3.1

Breaking changes

Bundled data

  • simulated_ptsd and simulated_ptsd_genpop now ship with three demographic columns (patient_id, age, sex) so that the demographic carry-through workflow (id_col) can be demonstrated end-to-end on the bundled data. The numerical PCL-5 profile of both datasets is unchanged.

Documentation

  • The error message from rename_ptsd_columns() / rename_caps5_columns() when the number of non-ID columns is not exactly 20 now spells out the strict positional behaviour and points users at id_col for unrelated covariates.
  • Vignette restructure: the previous six vignettes have been consolidated into four with a shared template (clinical purpose + explicit input assumptions + epidemiological vocabulary + interpretation of every results table):
    • Getting started — single-cohort workflow + demographic join-back (replaces quickstart and folds in id_column_workflow).
    • Comparing diagnostic criteria — multi-scenario optimisation + ICD-11 + symptom-frequency heatmap (replaces internal_analysis and multi_scenario_analysis).
    • Validating abbreviated symptom definitions — internal and external validation (replaces validation sections of internal_analysis and external_validation_pcl5).
    • Validating a shared definition across sites — a multi-site workflow that derives a definition at one site and validates it at another by exchanging only a JSON symptom-index file, with no patient-level data shared.
    • CAPS-5 workflow — using CAPS-5 as the reference instrument (replaces external_validation_caps5).

PTSDdiag 0.3.0

New features

  • compare_optimizations() runs multiple symptom-optimization scenarios on the same dataset in a single call and returns a ptsdiag_comparison S3 object. Default scenarios reproduce the three approaches compared in the PTSDdiag preprint (4/6 hierarchical, 4/6 non-hierarchical, 3/6 non-hierarchical). Users can supply their own named list of scenarios with arbitrary n_symptoms / n_required / hierarchical combinations.
  • Fixed (non-optimised) criteria such as ICD-11 can be added to the same comparison either via the include_icd11 = TRUE convenience flag or via scenarios entries of type = "fixed", including user-supplied logical diagnosis vectors. They appear as additional rows in the comparison table and as rows of 0/1 cells in the heatmap.
  • summarize_top_combinations() produces a tidy manuscript-ready table (Approach / Rank / Combination / TP / FN / FP / TN / Sensitivity / Specificity / PPV / NPV) from a ptsdiag_comparison. Optional as_percent = TRUE returns percentages for direct manuscript use.
  • symptom_frequency() returns the long-format Symptom × Approach × Count / RelFreq dataframe (source of the preprint’s Supplementary Table S4), optionally appending an OVERALL pooled row.
  • plot_symptom_frequency() draws the symptom-selection heatmap (Figure 1 of the preprint) as a ggplot object, with the OVERALL row in its own facet for visual separation. ggplot2 is now in Suggests.
  • id_col carry-through (introduced in 0.2.7) propagates automatically through every scenario in compare_optimizations().

Documentation

  • New vignette multi-scenario-analysis replicates the preprint’s end-to-end derivation workflow in roughly twenty lines of code.

PTSDdiag 0.2.7

New features

Behavior changes

  • binarize_data() now mutates only the symptom_1..symptom_20 columns rather than the whole dataframe. Any additional columns (e.g. an ID column) are preserved unchanged. This fixes a latent bug where a non-numeric carry-through column would have been coerced or errored.
  • create_ptsd_diagnosis_binarized() now operates on the symptom subset only, accepting input dataframes with extra columns.
  • summarize_ptsd_changes() silently drops non-logical columns before computing metrics (previously errored). This lets ID columns ride along in comparison dataframes without breaking downstream summarization.

Documentation

  • New vignette id-column-workflow demonstrating how to use id_col to merge per-row diagnoses back to a dataframe with demographics.

PTSDdiag 0.2.6

Improvements

  • All user-facing error messages now use the cli package for consistent, rich formatting: argument names are highlighted, actual values are shown, and hint bullets guide users toward fixes.
  • Centralized input validation via .validate_pcl5_data() now accepts strict_cols, warn_total, and instrument parameters, reducing code duplication across exported functions.
  • holdout_validation() and cross_validation() now correctly restore the global RNG state on exit (replaced buggy withr::local_seed() with manual on.exit save/restore).
  • Removed withr from Imports (no longer used).

Documentation

  • Added @note to simulated_ptsd and simulated_ptsd_genpop dataset documentation clarifying that symptoms were simulated independently (no within-cluster correlations).

Tests

  • New tests for RNG state restoration, edge cases (all-positive/all-negative baselines, binarize boundary values), non-integer k rejection, empty split guard, and n_tied output from optimization functions.

PTSDdiag 0.2.5.1

Bug fixes

  • Fixed stale README.md that still linked to the removed introduction.html vignette instead of the new internal_analysis.html.
  • Restored package website link in README.
  • Added pkgdown site URL to DESCRIPTION URL field.
  • Updated JamesIves/github-pages-deploy-action to v4.7.3 for Node.js 24 compatibility.

PTSDdiag 0.2.5

Documentation

  • Restructured vignettes into four focused articles: Quick Start, Full Internal Analysis, External Validation (PCL-5), and External Validation (CAPS-5).
  • Updated pkgdown website configuration: fixed duplicate logo, added call-to-action buttons on the home page, added OpenGraph metadata for social sharing.
  • Added changelog entries for versions 0.2.2–0.2.4.

PTSDdiag 0.2.4

New functions

  • create_caps5_diagnosis() applies the DSM-5-TR diagnostic algorithm to CAPS-5 (Clinician-Administered PTSD Scale for DSM-5) item scores and returns the clinician-rated diagnostic status.
  • rename_caps5_columns() standardises CAPS-5 column names to symptom_1 through symptom_20, enabling all downstream functions to work transparently on CAPS-5 data.

Improvements

  • compare_diagnostic_systems() gains a caps5_data argument for including CAPS-5 diagnoses in the unified comparison table, and a reference argument ("pcl5" or "caps5") for selecting which instrument defines diagnostic “truth”. Labels are disambiguated automatically when both instruments are present (e.g. "DSM-5-TR (PCL-5)", "DSM-5-TR (CAPS-5)").

Infrastructure

  • Updated GitHub Actions workflows to Node.js 24 compatible versions.

PTSDdiag 0.2.3

New functions

PTSDdiag 0.2.2

Improvements

PTSDdiag 0.2.1

Improvements

Documentation

PTSDdiag 0.2.0

PTSDdiag 0.1.0

CRAN release: 2026-02-13

  • Initial CRAN submission.
  • Core analysis functions for identifying optimal 6-symptom PTSD diagnostic combinations using PCL-5 data.
  • Hierarchical (cluster-based) and non-hierarchical analysis approaches.
  • DSM-5 diagnostic criteria implementation (binarized and non-binarized).
  • Diagnostic accuracy metrics: sensitivity, specificity, PPV, NPV.
  • Holdout and k-fold cross-validation methods for model evaluation.
  • Simulated PCL-5 dataset with 5,000 observations for demonstration.