Skip to contents

pivot_across() is tabular's input-side helper: it consumes a long Analysis Results Data (ARD) data frame (typically produced by cards::ard_stack() or cards::ard_stack_hierarchical()) and returns a wide display data.frame ready to pass to tabular().

Usage

pivot_across(
  data,
  statistic = list(continuous = "{mean} ({sd})", categorical = "{n} ({p}%)"),
  column = NULL,
  row_group = NULL,
  label = NULL,
  overall = "Total",
  decimals = NULL,
  fmt = NULL
)

Arguments

data

Long ARD input data. <data.frame>: required. At minimum needs stat_name and stat. Cards-style group columns (group1, group1_level, ...) and variable / variable_level are auto-detected. Tibbles / card objects / arrow tables are coerced via as.data.frame().

statistic

Format spec for cell composition. <character(1) | named list>: required. Combines one or more ARD stats into one display cell. Three accepted forms — each illustrated below. Inside a format string, {stat_name} substitutes that stat's value from the ARD (for example, "{n} ({p}%)" interpolates the n and p stats into a "53 (62%)" cell). The lookup order when a value is needed for a variable is: per-variable -> per-context -> default -> the literal "{n}".

Form 1: single string

One format string applied to every variable regardless of context. Use when your ARD is homogeneous (e.g. all categorical).

# Every variable rendered as "n (p%)" — categorical-only slice.
cat_only <- cdisc_saf_demo_ard[cdisc_saf_demo_ard$context == "categorical", ]
pivot_across(
  cat_only,
  statistic = "{n} ({p}%)"
)

Form 2: named list by context

Different formats per context. This is the typical clinical-table form because demographics mix continuous and categorical variables.

The list names must match the values in the ARD's context column verbatim. Which strings appear there depends on how the ARD was built:

So an ARD assembled with ard_stack(ard_summary(...), ard_tabulate(...)) is keyed summary / tabulate, not continuous / categorical. Inspect unique(ard$context) when unsure.

# AGE (continuous) -> "75.2 (8.59)"; SEX (categorical) -> "53 (62%)"
pivot_across(
  cdisc_saf_demo_ard,
  statistic = list(
    continuous  = "{mean} ({sd})",
    categorical = "{n} ({p}%)"
  )
)

Form 3: named list by variable

Override on a per-variable basis; fall back to default or context. Use when one variable needs a custom format.

# AGE shows just the mean; SEX / RACE keep the categorical default.
pivot_across(
  cdisc_saf_demo_ard,
  statistic = list(
    AGE         = "{mean}",
    categorical = "{n} ({p}%)",
    default     = "{mean} ({sd})"
  )
)

Multi-row continuous spec

Any single entry can itself be a named character vector — each element becomes one display row, with the name as the row label. Use for N / Mean (SD) / Median / Min, Max-style blocks.

pivot_across(
  cdisc_saf_demo_ard,
  statistic = list(
    continuous = c(
      N           = "{N}",
      "Mean (SD)" = "{mean} ({sd})",
      Median      = "{median}",
      "Min, Max"  = "{min}, {max}"
    ),
    categorical = "{n} ({p}%)"
  )
)

column

Grouping column whose unique values become arms. <character(1) | NULL>: default NULL. NULL auto-detects from the ARD's group1 value or — for renamed input — picks the single non-standard column. Pass a string when multiple group columns exist.

row_group

Second, non-column grouping dimension. <character(1) | NULL>: default NULL. Names the non-arm group variable of a two-variable .by (e.g. SEX in ard_stack(.by = c(ARM, SEX))). It widens into a leading row column (not a pivoted arm column), so the result composes with subgroup(by = ...) or col_spec(usage = "group") downstream.

Why it is required. cards encodes a crossing factor and a SOC/PT hierarchy identically (the second group variable appears in variable on its by-marginal rows), so the two cannot be told apart automatically. Naming row_group declares "this is a crossing factor": the by-marginal rows are dropped and the flat path is used. Leave it NULL for a genuine hierarchy.

Restriction: Must name a second grouping variable present in the ARD and must differ from column.

label

Variable-name to display-label map. <character> | NULL: default NULL. Named character vector mapping variable names to display labels (e.g. c(AGE = "Age (years)", SEX = "Sex")). Applies to variable, soc, and label columns of the output. NULL leaves the upstream variable names verbatim.

Renaming the hierarchical "overall" row. A cards::ard_stack_hierarchical(overall = TRUE) ARD carries an internal ..ard_hierarchical_overall.. sentinel for the grand-total ("any event") row. It is relabelled to "Overall" by default; map the sentinel key to override, e.g. label = c("..ard_hierarchical_overall.." = "TOTAL SUBJECTS WITH AN EVENT"). The raw sentinel never reaches the output at any hierarchy depth.

overall

Column name for NA-arm (overall / total) rows. <character(1) | NULL>: default "Total". Pass NULL to drop overall rows entirely (per-arm only output).

decimals

Per-stat decimal precision. <named integer | named list>: default c()“. Accepts two forms:

  • named integer vector — global per-stat overrides (c(mean = 1, sd = 2, p = 0)).

  • named list — per-variable plus .default (list(AGE = c(mean = 2), .default = c(p = 1))).

Built-in defaults apply when neither sets a stat.

fmt

Per-stat custom formatter functions. <named list of function>: default list()“. Each function takes a numeric value and returns a character string; overrides built-ins and decimals for that stat. Useful for p-value styling and other domain-specific formatting.

# p-value formatter: render below-threshold values as "<0.001".
fmt = list(
  p.value = function(x) {
    ifelse(x < 0.001, "<0.001", sprintf("%.3f", x))
  }
)

Value

A wide data.frame ready for tabular(). Schema:

  • variable — variable name (or label after label = ...).

  • stat_label — display-row label.

  • One column per arm level (named after the group1_level values or the renamed arm column).

  • Total (or whatever overall is set to) when applicable.

  • A leading column named after row_group when set (the second grouping dimension).

  • Hierarchical ARD adds soc, label, row_type instead of variable.

Pass the result straight into tabular() to start the render pipeline.

Details

tabular's package boundary is display-only: pre-summarised data in, rendered file out. pivot_across() is the canonical bridge between the cards aggregation backend and that boundary. It does not aggregate — it pivots arms to columns, interpolates per-cell display strings from the stat values, and applies decimal precision. Filtering, weighting, and aggregation happen upstream in cards or your own data-prep step.

Key statistic by the ARD context

statistic (and fmt) are matched against the ARD's context column verbatim, and that value differs per generating function. Keying by the wrong name silently drops the format. Inspect unique(ard$context) first and key to match (or pass a single format string / default = to cover everything). When an explicitly-supplied statistic matches no context at all, pivot_across() warns rather than silently emitting {n}.

Generating functioncontext to key on
cards::ard_summary()summary
cards::ard_tabulate()tabulate
cards::ard_continuous()continuous
cards::ard_categorical()categorical
cards::ard_stack_hierarchical()tabulate + hierarchical
cardx::ard_categorical_ci()proportion_ci
cardx::ard_continuous_ci()continuous_ci

Indentation of stat_label

Categorical levels and the multi-row continuous stat labels come back already indented with two leading spaces, ready to render as a plain display column. Do not also set col_spec(indent = ...) on stat_label — that stacks the engine indent on top of the string indent (a double indent). Use one or the other.

Zero-suppression (always-on default)

A row whose n value equals zero renders the whole cell as the bare n value instead of fully interpolating the format string. For a categorical level with n = 0, the cell shows "0", not "0 (0.0%)". This is clinical convention — empty cells should read as a single zero, not advertise a meaningless rate.

How the default fires (chain of events). During cell assembly, before format-string interpolation, the engine checks the row's n stat. If it is zero, the engine short-circuits and returns the formatted n value ("0") as the entire cell — {p} is never substituted, so the (0.0%) half of the format string is dropped.

How to opt out: supply a custom fmt$n. Setting any function under fmt$n is the engine's signal that the user owns the n rendering. The short-circuit is disabled for the whole table; for every row the full format string interpolates, so {n} becomes your formatter's output and {p} becomes the standard percentage. For n = 0, that's "0 (0.0%)".

# Force "0 (0.0%)" for n = 0 rows by attaching a custom n formatter.
# The body of fmt$n can be the default integer rendering — its
# presence alone is what disables the zero-suppression branch.
pivot_across(
  cdisc_saf_demo_ard,
  statistic = list(
    continuous  = "{mean} ({sd})",
    categorical = "{n} ({p}%)"
  ),
  fmt = list(n = function(x) sprintf("%d", as.integer(x)))
)

Pharma rounding (always-on default)

A percentage that would otherwise round to 0 (when the value is positive but smaller than the chosen precision) renders as <0.1; one that would round to 100 (positive but smaller than 100) renders as >99.9. The threshold is precision-aware: decimals = c(p = 2) produces <0.01 / >99.99. This matches the pharma convention of never claiming exactly 0% or 100% when at least one subject contributed.

Override per-stat via fmt:

# Show exact rounded percentages even at the extremes
pivot_across(
  data,
  statistic = "{n} ({p}%)",
  decimals  = c(p = 1),
  fmt = list(p = function(x) sprintf("%.1f", x * 100))
)

Your fmt$p receives the raw stat value (a proportion between 0 and 1) and returns the displayed string. The pharma-threshold branch only fires inside the built-in p formatter and the decimals-driven path, so any custom fmt$p bypasses it.

See also

Pipeline entry consumer: tabular() — wraps the wide data frame this helper returns.

Downstream spec-build verbs: cols() / col_spec(), headers(), sort_rows(), style(), paginate(), preset().

Terminal verbs: emit(), as_grid().

Examples

# ---- Example 1: Demographics — long ARD to rendered spec ----
#
# Full pipeline from a `cards::ard_stack()`-style long ARD to a
# sorted `tabular_spec`. The multi-row continuous block (N /
# Mean (SD) / Median / Min, Max) sits above each categorical
# block; decimals are set per-stat (mean 1, sd 2, p 1) to match
# the CDISC convention.
n <- stats::setNames(cdisc_saf_n$n, cdisc_saf_n$arm_short)

cdisc_saf_demo_ard |>
  pivot_across(
    statistic = list(
      continuous = c(
        N           = "{N}",
        "Mean (SD)" = "{mean} ({sd})",
        Median      = "{median}",
        "Min, Max"  = "{min}, {max}"
      ),
      categorical = "{n} ({p}%)"
    ),
    decimals = c(mean = 1, sd = 2, p = 1, median = 1, min = 0, max = 0),
    label    = c(AGE = "Age (years)", SEX = "Sex", RACE = "Race")
  ) |>
  tabular(
    titles = c(
      "Table 14.1.1",
      "Demographics and Baseline Characteristics",
      "Safety Population"
    ),
    footnotes = "Percentages based on N per treatment group."
  ) |>
  cols(
    variable   = col_spec(usage = "group", label = "Parameter"),
    stat_label = col_spec(label = "Statistic"),
    Placebo    = col_spec(
      label = "Placebo\nN={n['placebo']}",
      align = "decimal"
    ),
    `Xanomeline Low Dose` = col_spec(
      label = "Drug 50\nN={n['drug_50']}",
      align = "decimal"
    ),
    `Xanomeline High Dose` = col_spec(
      label = "Drug 100\nN={n['drug_100']}",
      align = "decimal"
    ),
    Total = col_spec(
      label = "Total\nN={n['Total']}",
      align = "decimal"
    )
  )

 

Table 14.1.1

Demographics and Baseline Characteristics

Safety Population

 

StatisticPlacebo
N=86
Drug 100
N=72
Drug 50
N=96
Total
N=254
Age (years)
  N 86           72           96          254          
  Mean (SD) 75.2 (8.59)  73.8 (7.94)  76.0 (8.11)  75.1 (8.25) 
  Median 76.0         75.5         78.0         77.0        
  Min, Max 52  , 89     56  , 88     51  , 88     51  , 89    
 
WEIGHT
  N 86           72           95          253          
  Mean (SD) 62.8 (12.77) 69.5 (14.35) 68.0 (14.50) 66.6 (14.13)
  Median 60.6         69.0         66.7         66.7        
  Min, Max 34  , 86     44  , 108    42  , 106    34  , 108   
 
HEIGHT
  N 86           72           96          254          
  Mean (SD)162.6 (11.52)165.9 (10.28)163.7 (10.30)163.9 (10.76)
  Median162.6        165.1        162.6        162.8        
  Min, Max137  , 185   146  , 190   136  , 196   136  , 196   
 
BMI
  N 86           72           95          253          
  Mean (SD) 23.6 (3.67)  25.2 (3.97)  25.2 (4.40)  24.7 (4.09) 
  Median 23.4         24.8         24.8         24.2        
  Min, Max 15  , 33     14  , 35     15  , 40     14  , 40    
 
AGEGR1
  18-64 14 (16.3%)   11 (15.3%)    8 ( 8.3%)   33 (13.0%)  
  >64 72 (83.7%)   61 (84.7%)   88 (91.7%)  221 (87.0%)  
 
Sex
  F 53 (61.6%)   35 (48.6%)   55 (57.3%)  143 (56.3%)  
  M 33 (38.4%)   37 (51.4%)   41 (42.7%)  111 (43.7%)  
 
Race
  WHITE 78 (90.7%)   62 (86.1%)   90 (93.8%)  230 (90.6%)  
  BLACK OR AFRICAN AMERICAN  8 ( 9.3%)    9 (12.5%)    6 ( 6.2%)   23 ( 9.1%)  
  ASIAN  0            0            0            0          
  AMERICAN INDIAN OR ALASKA NATIVE  0            1 ( 1.4%)    0            1 ( 0.4%)  
 
ETHNIC
  HISPANIC OR LATINO  3 ( 3.5%)    3 ( 4.2%)    6 ( 6.2%)   12 ( 4.7%)  
  NOT HISPANIC OR LATINO 83 (96.5%)   69 (95.8%)   90 (93.8%)  242 (95.3%)  
  NOT REPORTED  0            0            0            0          
 
BMI_CAT
  Underweight (<18.5)  3 ( 3.5%)    1 ( 1.4%)    4 ( 4.2%)    8 ( 3.2%)  
  Normal (18.5-24.9) 57 (66.3%)   39 (54.2%)   46 (48.4%)  142 (56.1%)  
  Overweight (25-29.9) 20 (23.3%)   23 (31.9%)   32 (33.7%)   75 (29.6%)  
  Obese (>=30)  6 ( 7.0%)    9 (12.5%)   13 (13.7%)   28 (11.1%)  

Percentages based on N per treatment group.

# ---- Example 2: Hierarchical SOC/PT AE table ---- # # Hierarchical `cards::ard_stack_hierarchical()` output threaded # through `pivot_across()`. The hierarchical ARD emits a # (soc, label, row_type) triple plus one stat row per (arm, SOC, PT); # `pivot_across()` folds the arm dimension to columns and preserves # the hierarchy markers. Derive `indent_level` from `row_type` so # `col_spec(indent = "indent_level")` drives the SOC -> PT # indent on the `label` column. wide <- cdisc_saf_aesocpt_ard |> pivot_across(statistic = "{n} ({p}%)") wide$indent_level <- as.integer(wide$row_type == "pt") tabular( wide, titles = c( "Table 14.3.1", "Adverse Events by System Organ Class and Preferred Term", "Safety Population" ), footnotes = c( "Subjects are counted once per SOC and once per PT.", "Percentages based on N per treatment group." ) ) |> cols( label = col_spec(label = "SOC / PT", indent = "indent_level"), soc = col_spec(visible = FALSE), row_type = col_spec(visible = FALSE), Placebo = col_spec( label = "Placebo\nN={n['placebo']}", align = "decimal" ), `Xanomeline Low Dose` = col_spec( label = "Drug 50\nN={n['drug_50']}", align = "decimal" ), `Xanomeline High Dose` = col_spec( label = "Drug 100\nN={n['drug_100']}", align = "decimal" ) )

 

Table 14.3.1

Adverse Events by System Organ Class and Preferred Term

Safety Population

 

SOC / PTPlacebo
N=86
Drug 100
N=72
Drug 50
N=96
Overall52 (60%)66 (92%)81 (84%)
SKIN AND SUBCUTANEOUS TISSUE DISORDERS19 (22%)35 (49%)36 (38%)
PRURITUS 8 ( 9%)25 (35%)21 (22%)
ERYTHEMA 8 ( 9%)14 (19%)14 (15%)
RASH 5 ( 6%) 8 (11%)13 (14%)
HYPERHIDROSIS 2 ( 2%) 8 (11%) 4 ( 4%)
SKIN IRRITATION 3 ( 3%) 5 ( 7%) 6 ( 6%)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS15 (17%)30 (42%)36 (38%)
APPLICATION SITE PRURITUS 6 ( 7%)21 (29%)23 (24%)
APPLICATION SITE ERYTHEMA 3 ( 3%)14 (19%)13 (14%)
APPLICATION SITE DERMATITIS 5 ( 6%) 7 (10%) 9 ( 9%)
APPLICATION SITE IRRITATION 3 ( 3%) 9 (12%) 9 ( 9%)
APPLICATION SITE VESICLES 1 ( 1%) 5 ( 7%) 5 ( 5%)
GASTROINTESTINAL DISORDERS13 (15%)17 (24%)12 (12%)
DIARRHOEA 9 (10%) 3 ( 4%) 5 ( 5%)
VOMITING 3 ( 3%) 6 ( 8%) 4 ( 4%)
NAUSEA 3 ( 3%) 6 ( 8%) 3 ( 3%)
ABDOMINAL PAIN 1 ( 1%) 1 ( 1%) 3 ( 3%)
SALIVARY HYPERSECRETION 0       4 ( 6%) 0      
NERVOUS SYSTEM DISORDERS 6 ( 7%)17 (24%)18 (19%)
DIZZINESS 2 ( 2%)10 (14%) 9 ( 9%)
HEADACHE 3 ( 3%) 5 ( 7%) 3 ( 3%)
SYNCOPE 0       2 ( 3%) 5 ( 5%)
SOMNOLENCE 2 ( 2%) 1 ( 1%) 3 ( 3%)
TRANSIENT ISCHAEMIC ATTACK 0       1 ( 1%) 2 ( 2%)
CARDIAC DISORDERS 7 ( 8%)14 (19%)12 (12%)
SINUS BRADYCARDIA 2 ( 2%) 8 (11%) 7 ( 7%)
MYOCARDIAL INFARCTION 4 ( 5%) 4 ( 6%) 2 ( 2%)
ATRIAL FIBRILLATION 1 ( 1%) 2 ( 3%) 2 ( 2%)
SUPRAVENTRICULAR EXTRASYSTOLES 1 ( 1%) 1 ( 1%) 1 ( 1%)
VENTRICULAR EXTRASYSTOLES 0       1 ( 1%) 2 ( 2%)
INFECTIONS AND INFESTATIONS12 (14%)11 (15%) 6 ( 6%)
NASOPHARYNGITIS 2 ( 2%) 6 ( 8%) 4 ( 4%)
UPPER RESPIRATORY TRACT INFECTION 6 ( 7%) 3 ( 4%) 1 ( 1%)
INFLUENZA 1 ( 1%) 1 ( 1%) 1 ( 1%)
URINARY TRACT INFECTION 2 ( 2%) 1 ( 1%) 0      
CYSTITIS 1 ( 1%) 1 ( 1%) 0      
RESPIRATORY, THORACIC AND MEDIASTINAL DISORDERS 5 ( 6%) 9 (12%) 8 ( 8%)
COUGH 1 ( 1%) 5 ( 7%) 5 ( 5%)
NASAL CONGESTION 3 ( 3%) 3 ( 4%) 1 ( 1%)
DYSPNOEA 1 ( 1%) 1 ( 1%) 1 ( 1%)
EPISTAXIS 0       2 ( 3%) 1 ( 1%)
PHARYNGOLARYNGEAL PAIN 0       1 ( 1%) 1 ( 1%)
PSYCHIATRIC DISORDERS 7 ( 8%) 3 ( 4%) 9 ( 9%)
CONFUSIONAL STATE 2 ( 2%) 1 ( 1%) 3 ( 3%)
AGITATION 2 ( 2%) 0       3 ( 3%)
INSOMNIA 2 ( 2%) 2 ( 3%) 0      
ANXIETY 0       0       3 ( 3%)
DELUSION 1 ( 1%) 1 ( 1%) 0      
MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS 3 ( 3%) 5 ( 7%) 6 ( 6%)
BACK PAIN 1 ( 1%) 3 ( 4%) 1 ( 1%)
ARTHRALGIA 1 ( 1%) 1 ( 1%) 2 ( 2%)
SHOULDER PAIN 1 ( 1%) 0       2 ( 2%)
MUSCLE SPASMS 0       1 ( 1%) 1 ( 1%)
ARTHRITIS 0       1 ( 1%) 0      
INVESTIGATIONS 5 ( 6%) 3 ( 4%) 4 ( 4%)
ELECTROCARDIOGRAM ST SEGMENT DEPRESSION 4 ( 5%) 0       1 ( 1%)
ELECTROCARDIOGRAM T WAVE INVERSION 2 ( 2%) 1 ( 1%) 1 ( 1%)
BLOOD GLUCOSE INCREASED 0       1 ( 1%) 1 ( 1%)
ELECTROCARDIOGRAM T WAVE AMPLITUDE DECREASED 1 ( 1%) 0       1 ( 1%)
BIOPSY 0       1 ( 1%) 0      

Subjects are counted once per SOC and once per PT.

Percentages based on N per treatment group.

# ---- Example 3: Hierarchical ARD (SOC / PT) ---- # # `cdisc_saf_aesocpt_ard` carries an `ard_stack_hierarchical` shape with # two grouping variables (AEBODSYS / AEDECOD). `pivot_across()` # recognises the hierarchical structure and emits dedicated `soc`, # `label`, and `row_type` columns so the SOC -> PT nesting survives # the pivot. The result is ready for `tabular()` plus `sort_rows()`. head(cdisc_saf_aesocpt_ard, 3) #> {cards} data frame: 3 x 10 #> group1 group1_level group2 group2_level variable variable_level #> 1 <NA> <NA> TRT01A Placebo #> 2 <NA> <NA> TRT01A Placebo #> 3 <NA> <NA> TRT01A Placebo #> stat_name stat_label stat #> 1 n n 86 #> 2 N N 254 #> 3 p % 0.339 #> 1 more variable: context wide <- cdisc_saf_aesocpt_ard |> pivot_across(statistic = "{n} ({p}%)") head(wide, 3) #> soc #> 1 Overall #> 2 SKIN AND SUBCUTANEOUS TISSUE DISORDERS #> 3 SKIN AND SUBCUTANEOUS TISSUE DISORDERS #> label row_type Placebo #> 1 Overall overall 52 (60%) #> 2 SKIN AND SUBCUTANEOUS TISSUE DISORDERS soc 19 (22%) #> 3 PRURITUS pt 8 (9%) #> Xanomeline High Dose Xanomeline Low Dose #> 1 66 (92%) 81 (84%) #> 2 35 (49%) 36 (38%) #> 3 25 (35%) 21 (22%) # ---- Example 4: Multi-row continuous spec + label re-labelling ---- # # `statistic = c(<label> = <template>, ...)` produces one display # row per named entry — the canonical "N / Mean (SD) / Median / # Min, Max" block for continuous variables. `label = c(...)` # renames the variable headings emitted into the wide output. cdisc_saf_demo_ard |> pivot_across( statistic = list( continuous = c( N = "{N}", "Mean (SD)" = "{mean} ({sd})", Median = "{median}", "Q1, Q3" = "{p25}, {p75}", "Min, Max" = "{min}, {max}" ), categorical = "{n} ({p}%)" ), label = c( AGE = "Age (years)", WEIGHT = "Weight (kg)", HEIGHT = "Height (cm)", BMI = "BMI (kg/m^2)" ) ) #> variable stat_label Placebo #> 1 Age (years) N 86 #> 2 Age (years) Mean (SD) 75.2 (8.59) #> 3 Age (years) Median 76.0 #> 4 Age (years) Q1, Q3 69.0, 82.0 #> 5 Age (years) Min, Max 52.0, 89.0 #> 6 Weight (kg) N 86 #> 7 Weight (kg) Mean (SD) 62.8 (12.77) #> 8 Weight (kg) Median 60.6 #> 9 Weight (kg) Q1, Q3 53.5, 74.4 #> 10 Weight (kg) Min, Max 34.0, 86.2 #> 11 Height (cm) N 86 #> 12 Height (cm) Mean (SD) 162.6 (11.52) #> 13 Height (cm) Median 162.6 #> 14 Height (cm) Q1, Q3 153.7, 171.4 #> 15 Height (cm) Min, Max 137.2, 185.4 #> 16 BMI (kg/m^2) N 86 #> 17 BMI (kg/m^2) Mean (SD) 23.6 (3.67) #> 18 BMI (kg/m^2) Median 23.4 #> 19 BMI (kg/m^2) Q1, Q3 21.2, 25.7 #> 20 BMI (kg/m^2) Min, Max 15.1, 33.3 #> 21 AGEGR1 18-64 14 (16%) #> 22 AGEGR1 >64 72 (84%) #> 23 SEX F 53 (62%) #> 24 SEX M 33 (38%) #> 25 RACE WHITE 78 (91%) #> 26 RACE BLACK OR AFRICAN AMERICAN 8 (9%) #> 27 RACE ASIAN 0 #> 28 RACE AMERICAN INDIAN OR ALASKA NATIVE 0 #> 29 ETHNIC HISPANIC OR LATINO 3 (3%) #> 30 ETHNIC NOT HISPANIC OR LATINO 83 (97%) #> 31 ETHNIC NOT REPORTED 0 #> 32 BMI_CAT Underweight (<18.5) 3 (3%) #> 33 BMI_CAT Normal (18.5-24.9) 57 (66%) #> 34 BMI_CAT Overweight (25-29.9) 20 (23%) #> 35 BMI_CAT Obese (>=30) 6 (7%) #> Xanomeline High Dose Xanomeline Low Dose Total #> 1 72 96 254 #> 2 73.8 (7.94) 76.0 (8.11) 75.1 (8.25) #> 3 75.5 78.0 77.0 #> 4 70.0, 79.0 71.0, 82.0 70.0, 81.0 #> 5 56.0, 88.0 51.0, 88.0 51.0, 89.0 #> 6 72 95 253 #> 7 69.5 (14.35) 68.0 (14.50) 66.6 (14.13) #> 8 69.0 66.7 66.7 #> 9 56.7, 80.3 55.8, 78.5 55.3, 77.1 #> 10 44.5, 108.0 41.7, 106.1 34.0, 108.0 #> 11 72 96 254 #> 12 165.9 (10.28) 163.7 (10.30) 163.9 (10.76) #> 13 165.1 162.6 162.8 #> 14 157.5, 172.9 157.5, 170.2 156.2, 171.4 #> 15 146.1, 190.5 135.9, 195.6 135.9, 195.6 #> 16 72 95 253 #> 17 25.2 (3.97) 25.2 (4.40) 24.7 (4.09) #> 18 24.8 24.8 24.2 #> 19 22.7, 27.6 22.2, 28.3 21.9, 27.3 #> 20 13.7, 34.6 15.3, 40.2 13.7, 40.2 #> 21 11 (15%) 8 (8%) 33 (13%) #> 22 61 (85%) 88 (92%) 221 (87%) #> 23 35 (49%) 55 (57%) 143 (56%) #> 24 37 (51%) 41 (43%) 111 (44%) #> 25 62 (86%) 90 (94%) 230 (91%) #> 26 9 (12%) 6 (6%) 23 (9%) #> 27 0 0 0 #> 28 1 (1%) 0 1 (0%) #> 29 3 (4%) 6 (6%) 12 (5%) #> 30 69 (96%) 90 (94%) 242 (95%) #> 31 0 0 0 #> 32 1 (1%) 4 (4%) 8 (3%) #> 33 39 (54%) 46 (48%) 142 (56%) #> 34 23 (32%) 32 (34%) 75 (30%) #> 35 9 (12%) 13 (14%) 28 (11%) # ---- Example 5: ARD keyed by summary / tabulate contexts ---- # # The `statistic` list names must match the ARD's `context` column # verbatim. `cards::ard_summary()` / `ard_tabulate()` emit `"summary"` / # `"tabulate"` (not the `"continuous"` / `"categorical"` of # `ard_continuous()` / `ard_categorical()`), so a list keyed # `continuous`/`categorical` would silently match nothing. Always check # `unique(ard$context)` first. Here the bundled `cdisc_saf_demo_ard` is # relabelled to mimic `ard_summary()` + `ard_tabulate()` output; the # by-variable's own row drops automatically and both the summary and # the tabulate variables survive. card_st <- cdisc_saf_demo_ard card_st$context[card_st$context == "continuous"] <- "summary" card_st$context[card_st$context == "categorical"] <- "tabulate" pivot_across( card_st, statistic = list( summary = "{mean} ({sd})", tabulate = "{n} ({p}%)" ) ) #> variable stat_label Placebo #> 1 AGE AGE 75.2 (8.59) #> 2 WEIGHT WEIGHT 62.8 (12.77) #> 3 HEIGHT HEIGHT 162.6 (11.52) #> 4 BMI BMI 23.6 (3.67) #> 5 AGEGR1 18-64 14 (16%) #> 6 AGEGR1 >64 72 (84%) #> 7 SEX F 53 (62%) #> 8 SEX M 33 (38%) #> 9 RACE WHITE 78 (91%) #> 10 RACE BLACK OR AFRICAN AMERICAN 8 (9%) #> 11 RACE ASIAN 0 #> 12 RACE AMERICAN INDIAN OR ALASKA NATIVE 0 #> 13 ETHNIC HISPANIC OR LATINO 3 (3%) #> 14 ETHNIC NOT HISPANIC OR LATINO 83 (97%) #> 15 ETHNIC NOT REPORTED 0 #> 16 BMI_CAT Underweight (<18.5) 3 (3%) #> 17 BMI_CAT Normal (18.5-24.9) 57 (66%) #> 18 BMI_CAT Overweight (25-29.9) 20 (23%) #> 19 BMI_CAT Obese (>=30) 6 (7%) #> Xanomeline High Dose Xanomeline Low Dose Total #> 1 73.8 (7.94) 76.0 (8.11) 75.1 (8.25) #> 2 69.5 (14.35) 68.0 (14.50) 66.6 (14.13) #> 3 165.9 (10.28) 163.7 (10.30) 163.9 (10.76) #> 4 25.2 (3.97) 25.2 (4.40) 24.7 (4.09) #> 5 11 (15%) 8 (8%) 33 (13%) #> 6 61 (85%) 88 (92%) 221 (87%) #> 7 35 (49%) 55 (57%) 143 (56%) #> 8 37 (51%) 41 (43%) 111 (44%) #> 9 62 (86%) 90 (94%) 230 (91%) #> 10 9 (12%) 6 (6%) 23 (9%) #> 11 0 0 0 #> 12 1 (1%) 0 1 (0%) #> 13 3 (4%) 6 (6%) 12 (5%) #> 14 69 (96%) 90 (94%) 242 (95%) #> 15 0 0 0 #> 16 1 (1%) 4 (4%) 8 (3%) #> 17 39 (54%) 46 (48%) 142 (56%) #> 18 23 (32%) 32 (34%) 75 (30%) #> 19 9 (12%) 13 (14%) 28 (11%)