For 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)

Arguments

traced

A p21_traced object from trace_findings().

Value

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.

Details

  • 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.

What is deliberately not classified

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.

How a finding is classified

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.

Examples

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