Compares the metadata with the datasets it claims to describe and returns every disagreement as one row. This is the check that catches the drift a spreadsheet-driven define always accumulates: a Length that no longer covers the longest value actually present, a codelist term used in the data but never declared, a variable dropped from the data but left in the metadata.

check_define(spec, data)

Arguments

spec

A define21_spec object.

data

A named list of data frames, names matching spec$datasets$dataset (matched case-insensitively, so list(dm = dm) finds dataset DM).

Value

A tibble of class define21_check with columns dataset, variable, value (the value level key, NA for a variable-level finding), check, severity ("error" or "warning") and message. Zero rows means the metadata and the data agree.

Details

Findings reported:

  • dataset_missing - described in the metadata, not present in data.

  • dataset_undocumented - present in data, absent from the metadata.

  • variable_missing - declared for a dataset, not a column of it.

  • variable_undocumented - a column of the dataset, not declared.

  • type_mismatch - declared DataType disagrees with the observed R type. A character column behind a date/time declaration is accepted, because that is how SDTM ships ISO 8601 values; float covering integer data is accepted; integer covering non-whole numbers is not.

  • length_too_short - the longest observed value is wider than Length.

  • codelist_value_missing - a value in the data is not a term of the declared codelist.

  • codelist_external - warning: the codelist is an ExternalCodeList, so its terms are not in the document and the data could not be compared with them. The dictionary is not shipped with this package.

  • where_clause_not_evaluable - warning: a value level condition tests a variable outside the dataset being checked (a cross-domain where clause), so that value could not be checked.

  • value_absent - warning: value level metadata describes a value whose where clause selects no record in the data.

  • empty_dataset / all_missing - warnings noting checks that could not run because there was nothing to look at.

Every value in spec$value_levels is checked the same way as a variable, on the subset of rows its def:WhereClauseDef selects. That is the point of value level metadata: a Length declared for VSSTRESN where VSTESTCD = "SYSBP" is checked against the systolic rows only.

Examples

ex <- define21_example()
check_define(ex$spec, ex$data)
#> 
#> ── Define-XML consistency check ────────────────────────────────────────────────
#>  0 errors
#> ! 2 warning(s)
#>  codelist_external: 2
#> # A tibble: 2 × 6
#>   dataset variable value check             severity message                     
#>   <chr>   <chr>    <chr> <chr>             <chr>    <chr>                       
#> 1 AE      AEDECOD  NA    codelist_external warning  Variable 'AE.AEDECOD' uses …
#> 2 AE      AEBODSYS NA    codelist_external warning  Variable 'AE.AEBODSYS' uses…

# Introduce real drift and watch it surface.
bad <- ex$data
bad$DM$USUBJID[1] <- "STUDY01-A-VERY-LONG-SUBJECT-IDENTIFIER"
bad$DM$SEX[2] <- "U"
check_define(ex$spec, bad)[, c("variable", "check", "severity")]
#> 
#> ── Define-XML consistency check ────────────────────────────────────────────────
#>  2 error(s)
#> ! 2 warning(s)
#>  length_too_short: 1
#>  codelist_value_missing: 1
#>  codelist_external: 2
#> # A tibble: 4 × 3
#>   variable check                  severity
#>   <chr>    <chr>                  <chr>   
#> 1 USUBJID  length_too_short       error   
#> 2 SEX      codelist_value_missing error   
#> 3 AEDECOD  codelist_external      warning 
#> 4 AEBODSYS codelist_external      warning 

# Drift inside one value of a value list, invisible at variable level.
vsbad <- ex$data
vsbad$VS$VSSTRESU[vsbad$VS$VSTESTCD == "PULSE"] <- "mmHg"
subset(check_define(ex$spec, vsbad), !is.na(value))[, c("variable", "value", "check")]
#> # A tibble: 1 × 3
#>   variable value check                 
#>   <chr>    <chr> <chr>                 
#> 1 VSSTRESU PULSE codelist_value_missing