suggest_fix.RdFor the finding classes with a deterministic remedy this recomputes the problem from the data and emits a specific suggestion, plus the R expression that fixes it whenever that expression is safely derivable. The classes are:
suggest_fix(traced)A p21_traced object from trace_findings().
A tibble of class p21_fixes, one row per finding, with the finding
identity (rule_id, severity, domain, variable, rule_class,
message), a fix_class, the class_source that produced it
("catalogue" or "message_regex"), a logical deterministic, a
human-readable suggestion, the r_expression that would fix it (NA
when none is safely derivable) and the evidence recomputed from the data.
missing_variable - a required or expected variable is absent.
required_value_null - a required variable is null on some records.
variable_length - the declared length is shorter than the longest value.
split_length - split datasets declare different lengths for one variable.
ct_codelist - a value is outside the codelist.
iso8601_date - a date is not ISO 8601.
key_not_unique - a key, or a --SEQ variable, is not unique.
referential_integrity - records are orphaned from their parent domain.
drop_variable - a variable the standard says should not be there.
missing_ts_param - a required Trial Summary parameter has no record.
domain_value - DOMAIN disagrees with the dataset name.
studyid_mismatch - STUDYID disagrees with DM.
variable_order - columns are not in the specification's order.
label_mismatch - a variable label disagrees with the specification.
variable_name_format - an ADaM variable name breaks the rule's own name
template, e.g. TRT1P where TRTxxP wants TRT01P.
ts_val_format - a TSVAL is written in the wrong format for its
parameter (a non-ISO date, a padded integer, a duration in words).
duplicate_ts_record - a Trial Summary parameter has more than one record.
define_missing_entry - a variable or dataset is in the data but not in
define.xml.
type_mismatch - define.xml declares one type and the column holds another.
datetime_component - a --DT or --TM disagrees with its --DTM.
non_ascii - a value holds non-ASCII or non-printable characters.
code_decode_pair - a --TEST is not the CDISC term that shares its
--TESTCD's NCI concept code.
Everything else is labelled "human_judgement" with deterministic = FALSE.
No fix is invented for a finding whose remedy depends on study knowledge, and
a class above still returns deterministic = FALSE whenever the recomputation
shows the remedy is not decided by the data - a prohibited variable that holds
real values, a STUDYID that DM does not settle, a column order the
specification does not cover. That restraint is the point of the package: it
is the reason a deterministic = TRUE row can be trusted.
406 of the 537 catalogued rules stay "human_judgement". 387 are
undecidable in principle - which of two disagreeing values is right, what a
missing unit or a missing result should be, whether a subject's actual arm
differs from the planned one for a good reason. The remaining 19 need data
p21bridge does not ship: MedDRA and WHODrug lookups (14), the five TSVAL
parameters whose value is an external dictionary term, and the SDTM
implementation guide's own variable type list. The split is counted by
data-raw/make-rule-catalogue.R on every rebuild. See the "Scope and limits"
section of the README.
The rule id is looked up in the shipped rule catalogue p21_rules first
(class_source = "catalogue"). Only if the rule is absent from the
catalogue, or the catalogue maps it to "human_judgement", does the finding
fall through to matching the report's message text
(class_source = "message_regex") - which is all v0.0.0.9000 did, and which
still covers rules newer than the catalogue. A finding that neither path
recognises degrades to "human_judgement" with deterministic = FALSE.
ex <- readRDS(system.file("extdata", "example_study.rds", package = "p21bridge"))
f <- read_findings(system.file("extdata", "findings.csv", package = "p21bridge"))
fx <- suggest_fix(trace_findings(f, ex$datasets, spec = ex$spec))
fx
#>
#> ── p21bridge fix suggestions ───────────────────────────────────────────────────
#> ✔ 6 of 12 findings have a deterministic remedy (6 with an R expression)
#> ! 3 findings need human judgement
#> ℹ Fix class: human_judgement 3 | ct_codelist 2 | variable_length 2 | iso8601_date 1 | key_not_unique 1 | missing_variable 1 | referential_integrity 1 | required_value_null 1
#> ℹ Classified by: catalogue 7 | message_regex 5
#> # A tibble: 12 × 12
#> rule_id severity domain variable rule_class message fix_class class_source
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 SD0056 Error DM RFENDTC SDTM data SDTM R… missing_… catalogue
#> 2 SD1310 Warning DM USUBJID SDTM data Variab… variable… message_reg…
#> 3 CT2001 Error DM SEX Controlled t… SEX va… ct_codel… catalogue
#> 4 CT2001 Error AE AESEV Controlled t… AESEV … ct_codel… catalogue
#> 5 SD1011 Error DM RFSTDTC SDTM data Invali… iso8601_… catalogue
#> 6 SD1117 Warning AE USUBJID SDTM data Duplic… key_not_… catalogue
#> 7 SD0064 Error AE USUBJID SDTM data Subjec… referent… catalogue
#> 8 SD0002 Error AE AESTDTC SDTM data NULL v… required… catalogue
#> 9 SD1234 Notice VS VSORRES SDTM data Result… human_ju… message_reg…
#> 10 DD0011 Warning DM AGE Define-XML Variab… human_ju… message_reg…
#> 11 SD1310 Error ADSL TRT01P SDTM data Variab… variable… message_reg…
#> 12 OD0018 Notice NA NA ODM / define… Define… human_ju… message_reg…
#> # ℹ 4 more variables: deterministic <lgl>, suggestion <chr>,
#> # r_expression <chr>, evidence <chr>
fx[fx$deterministic, c("rule_id", "fix_class", "r_expression")]
#> # A tibble: 6 × 3
#> rule_id fix_class r_expression
#> <chr> <chr> <chr>
#> 1 SD0056 missing_variable "DM <- dplyr::mutate(DM, RFENDTC = NA_character…
#> 2 SD1310 variable_length "spec$length[spec$dataset == \"DM\" & spec$vari…
#> 3 CT2001 ct_codelist "AE <- dplyr::mutate(AE, AESEV = toupper(trimws…
#> 4 SD1011 iso8601_date "DM <- dplyr::mutate(DM, RFSTDTC = ifelse(grepl…
#> 5 SD1117 key_not_unique "AE <- dplyr::ungroup(dplyr::mutate(dplyr::grou…
#> 6 SD0064 referential_integrity "AE <- dplyr::semi_join(AE, DM, by = \"USUBJID\…
# where each classification came from
table(fx$class_source)
#>
#> catalogue message_regex
#> 7 5