What happened?
Description
When using col_plan with span_structure across three column levels, mid-level labels that repeat
across top-level groups (e.g. "Female" under both "Active" and "Placebo") get merged into a
single spanner. The columns are reordered so that all "Female" leaf columns are grouped together
under one span, and all "Male" leaf columns under another — breaking the intended treatment-first
hierarchy.
Reproducible example
library(tfrmt)
library(tibble)
ard <- tribble(
~row_label, ~trt, ~sex_trt, ~stat, ~param, ~value,
"Subjects", "Active", "Active_Female", "n", "val", 40,
"Subjects", "Active", "Active_Female", "pct", "val", 47.1,
"Subjects", "Active", "Active_Male", "n", "val", 45,
"Subjects", "Active", "Active_Male", "pct", "val", 52.9,
"Subjects", "Placebo", "Placebo_Female", "n", "val", 38,
"Subjects", "Placebo", "Placebo_Female", "pct", "val", 50.7,
"Subjects", "Placebo", "Placebo_Male", "n", "val", 37,
"Subjects", "Placebo", "Placebo_Male", "pct", "val", 49.3,
)
freq_tfrmt <- tfrmt(
label = "row_label",
column = c(trt, sex_trt, stat),
param = "param",
value = "value",
body_plan = body_plan(
frmt_structure(group_val = ".default", label_val = ".default", val = frmt("xx.x"))
),
col_plan = col_plan(
span_structure(
trt = "Active",
sex_trt = c("Female" = "Active_Female", "Male" = "Active_Male"),
stat = c(n, pct)
),
span_structure(
trt = "Placebo",
sex_trt = c("Female" = "Placebo_Female", "Male" = "Placebo_Male"),
stat = c(n, pct)
)
)
)
print_to_gt(freq_tfrmt, .data = ard)
Expected
| Active | Placebo | Active | Placebo |
| Female | Male | Female | Male |
| n | pct | n | pct | n | pct | n | pct |
Actual
| Active | Placebo | Active | Placebo |
| Female | Male |
| n | pct | n | pct | n | pct | n | pct |
Root cause
In format_gt_column_labels (R/print_to_gt.R), the code uses group_by(.data$value) which groups by
label alone, ignoring parent context. All "Female" rows — regardless of whether they belong to
"Active" or "Placebo" — are merged into one spanner, reordering columns in the process.
Proposed fix
Group by the full ancestral path rather than label alone, and pass an explicit level to
tab_spanner:
What happened?
Description
When using col_plan with span_structure across three column levels, mid-level labels that repeat
across top-level groups (e.g. "Female" under both "Active" and "Placebo") get merged into a
single spanner. The columns are reordered so that all "Female" leaf columns are grouped together
under one span, and all "Male" leaf columns under another — breaking the intended treatment-first
hierarchy.
Reproducible example
library(tfrmt)
library(tibble)
ard <- tribble(
~row_label, ~trt, ~sex_trt, ~stat, ~param, ~value,
"Subjects", "Active", "Active_Female", "n", "val", 40,
"Subjects", "Active", "Active_Female", "pct", "val", 47.1,
"Subjects", "Active", "Active_Male", "n", "val", 45,
"Subjects", "Active", "Active_Male", "pct", "val", 52.9,
"Subjects", "Placebo", "Placebo_Female", "n", "val", 38,
"Subjects", "Placebo", "Placebo_Female", "pct", "val", 50.7,
"Subjects", "Placebo", "Placebo_Male", "n", "val", 37,
"Subjects", "Placebo", "Placebo_Male", "pct", "val", 49.3,
)
freq_tfrmt <- tfrmt(
label = "row_label",
column = c(trt, sex_trt, stat),
param = "param",
value = "value",
body_plan = body_plan(
frmt_structure(group_val = ".default", label_val = ".default", val = frmt("xx.x"))
),
col_plan = col_plan(
span_structure(
trt = "Active",
sex_trt = c("Female" = "Active_Female", "Male" = "Active_Male"),
stat = c(n, pct)
),
span_structure(
trt = "Placebo",
sex_trt = c("Female" = "Placebo_Female", "Male" = "Placebo_Male"),
stat = c(n, pct)
)
)
)
print_to_gt(freq_tfrmt, .data = ard)
Expected
| Active | Placebo | Active | Placebo |
| Female | Male | Female | Male |
| n | pct | n | pct | n | pct | n | pct |
Actual
| Active | Placebo | Active | Placebo |
| Female | Male |
| n | pct | n | pct | n | pct | n | pct |
Root cause
In format_gt_column_labels (R/print_to_gt.R), the code uses group_by(.data$value) which groups by
label alone, ignoring parent context. All "Female" rows — regardless of whether they belong to
"Active" or "Placebo" — are merged into one spanner, reordering columns in the process.
Proposed fix
Group by the full ancestral path rather than label alone, and pass an explicit level to
tab_spanner: