--- title: "Analysis Results Data" output: rmarkdown::html_vignette: toc: true vignette: > %\VignetteIndexEntry{Analysis Results Data} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(rmarkdown.html_vignette.check_title = FALSE) library(tplyr2) library(knitr) ``` ## Introduction Clinical summary tables are typically built for human consumption: aligned columns, formatted numbers, parenthesized percentages. But increasingly, regulatory and industry workflows require the underlying results in a machine-readable, standards-compliant format. The Analysis Results Data (ARD) model -- part of the broader CDISC ecosystem -- addresses this by defining a long-format structure where each row represents a single statistic for a single group combination. tplyr2 supports this through two functions: - `tplyr_to_ard()` converts a built result into ARD long format. - `tplyr_from_ard()` reconstructs a formatted table from ARD data and a spec. Together, these functions let you separate the _computed values_ from their _presentation_, enabling workflows where results are archived, exchanged, or validated independently of formatting. ## Converting to ARD Any result from `tplyr_build()` can be converted to ARD format. The raw numeric data is already attached to every build result as an attribute; `tplyr_to_ard()` melts that data into one-row-per-statistic long format. Here is a complete example using a demographics table with both count and descriptive statistics layers: ```{r} spec <- tplyr_spec( cols = "TRT01P", layers = tplyr_layers( group_count("SEX"), group_desc( "AGE", settings = layer_settings( format_strings = list( "n" = f_str("xxx", "n"), "Mean (SD)" = f_str("xx.x (xx.xx)", "mean", "sd"), "Median" = f_str("xx.x", "median"), "Min, Max" = f_str("xx, xx", "min", "max") ) ) ) ) ) result <- tplyr_build(spec, tplyr_adsl) ``` The formatted output looks like a typical clinical table: ```{r} kable(result[, !grepl("^ord", names(result))]) ``` Converting to ARD is a single function call: ```{r} ard <- tplyr_to_ard(result) kable(head(ard, 15)) ``` ## ARD Structure The ARD output is a data frame in long format. Each row represents one statistic for one group combination. The columns are: - **analysis_id**: An integer identifying which layer produced the row. Layer 1 is the first layer in the spec, layer 2 is the second, and so on. - **Grouping columns**: The original data variables that define the groups. For a count layer with `cols = "TRT01P"` and `target_var = "SEX"`, you will see `TRT01P` and `SEX` columns. - **stat_name**: The name of the statistic -- for example, `"n"`, `"pct"`, `"total"`, `"mean"`, `"sd"`, `"median"`, `"min"`, `"max"`. - **stat_value**: The numeric value of the statistic. Let us examine the count layer (analysis_id 1) and descriptive statistics layer (analysis_id 2) separately. ### Count Layer ARD The count layer produces statistics like `n`, `pct`, and `total` for each combination of treatment arm and target variable level: ```{r} count_ard <- ard[ard$analysis_id == 1, ] kable(count_ard) ``` Each sex-by-treatment combination has three rows: the raw count (`n`), the percentage (`pct`), and the denominator (`total`). ### Descriptive Statistics Layer ARD The descriptive layer produces a richer set of statistics. Even if the format strings only reference `n`, `mean`, `sd`, `median`, `min`, and `max`, the numeric data snapshot captures all computed statistics: ```{r} desc_ard <- ard[ard$analysis_id == 2, ] kable(head(desc_ard, 20)) ``` This is one of the key advantages of the ARD format: it preserves every computed value, not just those that appear in the formatted output. ## Reconstructing from ARD Given an ARD data frame and the original spec, `tplyr_from_ard()` applies the spec's formatting rules to rebuild the display table. This completes the round-trip: ```{r} rebuilt <- tplyr_from_ard(ard, spec) kable(rebuilt[, !grepl("^ord", names(rebuilt))]) ``` The reconstructed table applies the same format strings, row labels, and column structure defined in the spec. This means the spec acts as a reusable formatting template: the same ARD data can be reformatted with different specs if needed, or the same spec can be applied to ARD data from different studies. ### Verifying the Round-Trip We can confirm that the formatted values match between the original build and the ARD reconstruction: ```{r} original_sorted <- result[order(result$rowlabel1), ] rebuilt_sorted <- rebuilt[order(rebuilt$rowlabel1), ] all(trimws(original_sorted$res1) == trimws(rebuilt_sorted$res1)) ``` The formatted cell values are identical, confirming that no information is lost in the conversion. ## Use Cases ### Standards-Compliant Data Exchange When submitting results to a regulatory agency or sharing across organizations, the ARD format provides a self-describing, tool-agnostic representation of the computed values. The long format is straightforward to validate, compare across submissions, or load into any analysis environment. ### Separating Computation from Presentation In a production workflow, you might compute results once, archive the ARD, and then apply formatting later -- or apply different formatting for different audiences. The spec defines the presentation; the ARD holds the numbers. ```{r} # Same ARD, different formatting compact_spec <- tplyr_spec( cols = "TRT01P", layers = tplyr_layers( group_count( "SEX", settings = layer_settings( format_strings = list(n_counts = f_str("xx", "n")) ) ), group_desc( "AGE", settings = layer_settings( format_strings = list( "n" = f_str("xx", "n"), "Mean" = f_str("xx.x", "mean") ) ) ) ) ) compact_result <- tplyr_from_ard(ard, compact_spec) kable(compact_result[, !grepl("^ord", names(compact_result))]) ``` The same underlying data now appears in a more compact layout, with counts displayed without percentages and only two descriptive statistics rows. ### Layer Coverage The examples above use count and descriptive layers, but the ARD round-trip covers all four layer types -- **count** (including nested), **desc** (including multi-target), **shift**, and **analyze**. `tplyr_from_ard()` reconstructs the appropriate layer from the `analysis_id` and the stored statistics. One detail worth knowing: for `stat_columns` layers, the pre-formatted sub-columns are not carried into the ARD (they are display artifacts), but the underlying numeric statistics they were built from are retained, so the values still round-trip. ### Archiving and Reproducibility Because the ARD captures every computed statistic as a plain numeric value, it serves as a durable archive of the analysis results. Paired with a saved spec (see `vignette("serialization")`), the full table can be reproduced at any time without re-running the analysis against the source data. ## Summary The ARD workflow in tplyr2 is straightforward: 1. Build your table with `tplyr_build()` as usual. 2. Convert to long format with `tplyr_to_ard()` to get one row per statistic. 3. Reconstruct a formatted table with `tplyr_from_ard()` using any compatible spec. This separation of computed results from formatted output supports standards-compliant data exchange, flexible re-formatting, and reproducible archiving of analysis results.