Skip to contents

Demography Table

For this demography table we are going to use data_demog, an example analysis results dataset found in the package, which is based on the CDISC pilot data. This dataset has two different row label columns, rowlbl1 and rowlbl2 because we are building a table with group and row labels. There are also two order columns which will be used to set the row order of the output. There is a single column to define our table’s columns (multiple column columns are used when there is column spanning). Finally there is a param column, a value column and an additional grouping column, grp, which we can use for more complex formatting.

#> # A tibble: 6 × 8
#> # Groups:   rowlbl1 [1]
#>   rowlbl1 rowlbl2 param grp    ord1  ord2 column                 value
#>   <chr>   <chr>   <chr> <chr> <dbl> <dbl> <chr>                  <dbl>
#> 1 Age (y) n       n     cont      1     1 Placebo               86    
#> 2 Age (y) n       n     cont      1     1 Xanomeline Low Dose   84    
#> 3 Age (y) n       n     cont      1     1 Xanomeline High Dose  84    
#> 4 Age (y) n       n     cont      1     1 Total                254    
#> 5 Age (y) n       p     cont      1     1 p-value                0.593
#> 6 Age (y) Mean    Mean  cont      1     2 Placebo               75.2

The mock we are going to match looks like this:

Placebo Xanomeline Low Dose Xanomeline High Dose Total p-value
Age (y) n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
<65 yrs xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) x.xxx
65-80 yrs xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
>80 yrs xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
Sex n xxx xxx xxx xxx x.xxx
Male xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
Female xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
Race (Origin) n xxx xxx xxx xxx x.xxx
Caucasian xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
African Descent xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
Hispanic xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
Other xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
MMSE n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
Duration of disease n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
<12 months xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) x.xxx
>=12 months xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
Years of education n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
Baseline weight(kg) n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
Baseline height(cm) n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
Baseline BMI n xxx xxx xxx xxx x.xxx
Mean xxx.x xxx.x xxx.x xxx.x
SD xxx.xx xxx.xx xxx.xx xxx.xx
Median xxx.x xxx.x xxx.x xxx.x
Min xxx.x xxx.x xxx.x xxx.x
Max xxx.x xxx.x xxx.x xxx.x
<25 xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) x.xxx
25-<30 xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)
>=30 xxx (xx.x %) xxx (xx.x %) xxx (xx.x %) xxx (xx.x %)

For this table, we have three columns for each of the treatment groups, a total column for all groups combined, and a p-value column. The table also contains a mix of categorical and continuous analysis.

The first thing we are going to do when building out the tfrmt is specify all our columns

tfrmt(
  # specify columns in the data
  group = c(rowlbl1,grp),
  label = rowlbl2,
  column = column, 
  param = param,
  value = value,
  sorting_cols = c(ord1, ord2)) %>% 
  print_to_gt(data_demog) %>% 
  tab_options(
    container.width = 900
  )
#> The following rows of the given dataset have no format applied to them 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346
#> Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
#> dplyr 1.1.0.
#>  Please use `reframe()` instead.
#>  When switching from `summarise()` to `reframe()`, remember that `reframe()`
#>   always returns an ungrouped data frame and adjust accordingly.
#>  The deprecated feature was likely used in the tfrmt package.
#>   Please report the issue at
#>   <https://github.com/GSK-Biostatistics/tfrmt/issues>.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> Multiple param listed for the same group/label values.
#> The following frmt_structures may be missing from the body_plan
#> or the order may need to be changed:
#> - `frmt_structure(group_val = list(rowlbl1 = "Age (y)", grp = "cat"), label_val = c("65-80 yrs","<65 yrs",">80 yrs"), frmt_combine("{n}, {pct}",n = frmt("xx"), pct = frmt("xx")))`
#> - `frmt_structure(group_val = list(rowlbl1 = "Baseline BMI", grp = "cat"), label_val = c("25-<30","<25",">=30"), frmt_combine("{n}, {pct}",n = frmt("xx"), pct = frmt("xx")))`
#> - `frmt_structure(group_val = list(rowlbl1 = "Duration of disease ", grp = "cat"), label_val = c("<12 months",">=12 months"), frmt_combine("{n}, {pct}",n = frmt("xx"), pct = frmt("xx")))`
#> - `frmt_structure(group_val = list(rowlbl1 = "Race (Origin)", grp = "cat"), label_val = c("African Descent","Caucasian","Hispanic","Other"), frmt_combine("{n}, {pct}",n = frmt("xx"), pct = frmt("xx")))`
#> - `frmt_structure(group_val = list(rowlbl1 = "Sex", grp = "cat"), label_val = c("Female","Male"), frmt_combine("{n}, {pct}",n = frmt("xx"), pct = frmt("xx")))`
ord1 ord2 Placebo Xanomeline Low Dose Xanomeline High Dose Total p-value
Age (y)
cont
n 1 1 86 84 84 254 0.593435775283097
Mean 1 2 75.2093023255814 75.6666666666667 74.3809523809524 75.0866141732283 NA
SD 1 3 8.59016712714193 8.28605059954093 7.88609384869824 8.24623389621606 NA
Median 1 4 76 77.5 76 77 NA
Min 1 5 52 51 56 51 NA
Max 1 6 89 88 88 89 NA
cat
<65 yrs 1 7 14, 16.2790697674419 8, 9.52380952380952 11, 13.0952380952381 33, 12.992125984252 0.143917025502502
65-80 yrs 1 8 42, 48.8372093023256 47, 55.9523809523809 55, 65.4761904761905 144, 56.6929133858268 NA
>80 yrs 1 9 30, 34.8837209302326 29, 34.5238095238095 18, 21.4285714285714 77, 30.3149606299213 NA
Sex
cat
n 2 1 86 84 84 254 0.140859828596478
Male 2 2 33, 38.3720930232558 34, 40.4761904761905 44, 52.3809523809524 111, 43.7007874015748 NA
Female 2 3 53, 61.6279069767442 50, 59.5238095238095 40, 47.6190476190476 143, 56.2992125984252 NA
Race (Origin)
cat
n 3 1 86 84 84 254 0.647674941661787
Caucasian 3 2 75, 87.2093023255814 72, 85.7142857142857 71, 84.5238095238095 218, 85.8267716535433 NA
African Descent 3 3 8, 9.30232558139535 6, 7.14285714285714 9, 10.7142857142857 23, 9.05511811023622 NA
Hispanic 3 4 3, 3.48837209302326 6, 7.14285714285714 3, 3.57142857142857 12, 4.7244094488189 NA
Other 3 5 NA, NA NA, NA 1, 1.19047619047619 1, 0.393700787401575 NA
MMSE
cont
n 4 1 86 84 84 254 0.59465975941027
Mean 4 2 18.046511627907 17.8690476190476 18.5119047619048 18.1417322834646 NA
SD 4 3 4.2727783404855 4.22208696726523 4.15800591577738 4.21032874412179 NA
Median 4 4 19.5 18 20 19 NA
Min 4 5 10 10 10 10 NA
Max 4 6 23 24 24 24 NA
Duration of disease
cont
n 5 1 86 84 84 254 0.152960564175341
Mean 5 2 42.65 48.6916666666667 40.5071428571429 43.9393700787402 NA
SD 5 3 30.241571504451 29.5841711516636 24.6935472070355 28.3973156262964 NA
Median 5 4 35.3 40.25 35.95 36.25 NA
Min 5 5 7.2 7.8 2.2 2.2 NA
Max 5 6 183.1 130.8 135 183.1 NA
cat
<12 months 5 7 5, 5.81395348837209 3, 3.57142857142857 4, 4.76190476190476 12, 4.7244094488189 0.788536928535063
>=12 months 5 8 81, 94.1860465116279 81, 96.4285714285714 80, 95.2380952380952 242, 95.2755905511811 NA
Years of education
cont
n 6 1 86 84 84 254 0.38750874992316
Mean 6 2 12.5813953488372 13.1666666666667 12.5119047619048 12.751968503937 NA
SD 6 3 2.94843973206596 4.14738510544312 2.91854910157261 3.3829227112292 NA
Median 6 4 12 12 12 12 NA
Min 6 5 6 3 6 3 NA
Max 6 6 21 24 20 24 NA
Baseline weight(kg)
cont
n 7 1 86 83 84 253 0.00304006274608553
Mean 7 2 62.7593023255814 67.2795180722892 70.0047619047619 66.6478260869565 NA
SD 7 3 12.7715435329253 14.1235986486909 14.6534333717795 14.1314255372792 NA
Median 7 4 60.55 64.9 69.2 66.7 NA
Min 7 5 34 45.4 41.7 34 NA
Max 7 6 86.2 106.1 108 108 NA
Baseline height(cm)
cont
n 8 1 86 84 84 254 0.126217916960126
Mean 8 2 162.573255813953 163.433333333333 165.820238095238 163.931496062992 NA
SD 8 3 11.5223611185188 10.4192400034262 10.1313515524819 10.7604472686284 NA
Median 8 4 162.6 162.6 165.1 162.85 NA
Min 8 5 137.2 135.9 146.1 135.9 NA
Max 8 6 185.4 195.6 190.5 195.6 NA
Baseline BMI
cont
n 9 1 86 83 84 253 0.0133190726378392
Mean 9 2 23.6360465116279 25.0626506024096 25.347619047619 24.6723320158103 NA
SD 9 3 3.67192569419556 4.27050893303881 4.15826876019846 4.09218492698334 NA
Median 9 4 23.4 24.3 24.8 24.2 NA
Min 9 5 15.1 17.7 13.7 13.7 NA
Max 9 6 33.3 40.1 34.5 40.1 NA
cat
<25 9 7 59, 68.6046511627907 47, 55.9523809523809 44, 52.3809523809524 150, 59.0551181102362 0.232621461976889
25-<30 9 8 21, 24.4186046511628 27, 32.1428571428571 28, 33.3333333333333 76, 29.9212598425197 NA
>=30 9 9 6, 6.97674418604651 10, 11.9047619047619 12, 14.2857142857143 28, 11.0236220472441 NA

While this makes a table, it isn’t a very nice table and definitely doesn’t match the mock. So let’s start with formatting all the numbers. To do this we are going to build a body_plan to add to our tfrmt. This will be a fairly quick explanation of body_plans but if you would like more information see vignettes("Body Plan")

Body plans are made up of a series of frmt_stuctures where each frmt_stucture represents the formatting of a cell within the table. The order of the frmt_structures matter; they are always applied latest to oldest. This means the first frmt_stucture in the body_plan should be the most generic. You can use the groups, labels and parameters to specify which formatting applies to which values.

To start, we are going to use all the rows that are “n (%)” as the default. This way we don’t need to list out every row that is an “n (%)” row. These rows are made up of two different values, so we will need to use frmt_combine. Next, we can format the continuous variables, which is just a straightforward one value per row so we can just use the label to filter and frmt to define the look. Finally, we want to format the p-values. This is a bit more complicated, since the p-value sits in the same row as other parameters; therefore the group and label value are not specific enough and we need something more granular. As such, we will need to specify the parameter in the frmt_structure like so: frmt_structure(group_val = ".default", label_val = ".default", p = frmt("x.xx"). Further, we also need to make sure it never displays a rounded p-value of 0 or 1. So we can use frmt_when to specify the formatting based on the value.

tfrmt(
  # specify columns in the data
  group = c(rowlbl1,grp),
  label = rowlbl2,
  column = column, 
  param = param,
  value = value,
  sorting_cols = c(ord1, ord2),
  # specify value formatting 
  body_plan = body_plan(
    frmt_structure(group_val = ".default", label_val = ".default", frmt_combine("{n} ({pct} %)", 
                                                                                n = frmt("xxx"),
                                                                                pct = frmt("xx.x"))),
    frmt_structure(group_val = ".default", label_val = "n", frmt("xxx")),
    frmt_structure(group_val = ".default", label_val = c("Mean", "Median", "Min","Max"), frmt("xxx.x")),
    frmt_structure(group_val = ".default", label_val = "SD", frmt("xxx.xx")),
    frmt_structure(group_val = ".default", label_val = ".default", p = frmt_when(">0.99" ~ ">0.99",
                                                                                 "<0.001" ~ "<0.001",
                                                                                 TRUE ~ frmt("x.xxx", missing = "")))
  )) %>% 
  print_to_gt(data_demog) %>% 
  tab_options(
    container.width = 900
  )
ord1 ord2 Placebo Xanomeline Low Dose Xanomeline High Dose Total p-value
Age (y)
cont
n 1 1 86 84 84 254 0.593
Mean 1 2 75.2 75.7 74.4 75.1
SD 1 3 8.59 8.29 7.89 8.25
Median 1 4 76.0 77.5 76.0 77.0
Min 1 5 52.0 51.0 56.0 51.0
Max 1 6 89.0 88.0 88.0 89.0
cat
<65 yrs 1 7 14 (16.3 %) 8 ( 9.5 %) 11 (13.1 %) 33 (13.0 %) 0.144
65-80 yrs 1 8 42 (48.8 %) 47 (56.0 %) 55 (65.5 %) 144 (56.7 %)
>80 yrs 1 9 30 (34.9 %) 29 (34.5 %) 18 (21.4 %) 77 (30.3 %)
Sex
cat
n 2 1 86 84 84 254 0.141
Male 2 2 33 (38.4 %) 34 (40.5 %) 44 (52.4 %) 111 (43.7 %)
Female 2 3 53 (61.6 %) 50 (59.5 %) 40 (47.6 %) 143 (56.3 %)
Race (Origin)
cat
n 3 1 86 84 84 254 0.648
Caucasian 3 2 75 (87.2 %) 72 (85.7 %) 71 (84.5 %) 218 (85.8 %)
African Descent 3 3 8 ( 9.3 %) 6 ( 7.1 %) 9 (10.7 %) 23 ( 9.1 %)
Hispanic 3 4 3 ( 3.5 %) 6 ( 7.1 %) 3 ( 3.6 %) 12 ( 4.7 %)
Other 3 5 1 ( 1.2 %) 1 ( 0.4 %)
MMSE
cont
n 4 1 86 84 84 254 0.595
Mean 4 2 18.0 17.9 18.5 18.1
SD 4 3 4.27 4.22 4.16 4.21
Median 4 4 19.5 18.0 20.0 19.0
Min 4 5 10.0 10.0 10.0 10.0
Max 4 6 23.0 24.0 24.0 24.0
Duration of disease
cont
n 5 1 86 84 84 254 0.153
Mean 5 2 42.6 48.7 40.5 43.9
SD 5 3 30.24 29.58 24.69 28.40
Median 5 4 35.3 40.2 36.0 36.2
Min 5 5 7.2 7.8 2.2 2.2
Max 5 6 183.1 130.8 135.0 183.1
cat
<12 months 5 7 5 ( 5.8 %) 3 ( 3.6 %) 4 ( 4.8 %) 12 ( 4.7 %) 0.789
>=12 months 5 8 81 (94.2 %) 81 (96.4 %) 80 (95.2 %) 242 (95.3 %)
Years of education
cont
n 6 1 86 84 84 254 0.388
Mean 6 2 12.6 13.2 12.5 12.8
SD 6 3 2.95 4.15 2.92 3.38
Median 6 4 12.0 12.0 12.0 12.0
Min 6 5 6.0 3.0 6.0 3.0
Max 6 6 21.0 24.0 20.0 24.0
Baseline weight(kg)
cont
n 7 1 86 83 84 253 0.003
Mean 7 2 62.8 67.3 70.0 66.6
SD 7 3 12.77 14.12 14.65 14.13
Median 7 4 60.5 64.9 69.2 66.7
Min 7 5 34.0 45.4 41.7 34.0
Max 7 6 86.2 106.1 108.0 108.0
Baseline height(cm)
cont
n 8 1 86 84 84 254 0.126
Mean 8 2 162.6 163.4 165.8 163.9
SD 8 3 11.52 10.42 10.13 10.76
Median 8 4 162.6 162.6 165.1 162.8
Min 8 5 137.2 135.9 146.1 135.9
Max 8 6 185.4 195.6 190.5 195.6
Baseline BMI
cont
n 9 1 86 83 84 253 0.013
Mean 9 2 23.6 25.1 25.3 24.7
SD 9 3 3.67 4.27 4.16 4.09
Median 9 4 23.4 24.3 24.8 24.2
Min 9 5 15.1 17.7 13.7 13.7
Max 9 6 33.3 40.1 34.5 40.1
cat
<25 9 7 59 (68.6 %) 47 (56.0 %) 44 (52.4 %) 150 (59.1 %) 0.233
25-<30 9 8 21 (24.4 %) 27 (32.1 %) 28 (33.3 %) 76 (29.9 %)
>=30 9 9 6 ( 7.0 %) 10 (11.9 %) 12 (14.3 %) 28 (11.0 %)

Now that all the numbers look correct, we can drop the order columns and the grp column (note that while we do not want to display the grp column, it plays a role behind the scenes, which will be addressed in the next step). To do this we use a col_plan which uses tidy-select nomenclature to drop/move columns.

tfrmt(
  # specify columns in the data
  group = c(rowlbl1,grp),
  label = rowlbl2,
  column = column, 
  param = param,
  value = value,
  sorting_cols = c(ord1, ord2),
  # specify value formatting 
  body_plan = body_plan(
    frmt_structure(group_val = ".default", label_val = ".default", frmt_combine("{n} {pct}", 
                                                                                n = frmt("xxx"),
                                                                                pct = frmt_when("==100" ~ "",
                                                                                                "==0" ~ "",
                                                                                                TRUE ~ frmt("(xx.x %)")))),
    frmt_structure(group_val = ".default", label_val = "n", frmt("xxx")),
    frmt_structure(group_val = ".default", label_val = c("Mean", "Median", "Min","Max"), frmt("xxx.x")),
    frmt_structure(group_val = ".default", label_val = "SD", frmt("xxx.xx")),
    frmt_structure(group_val = ".default", label_val = ".default", p = frmt("")),
    frmt_structure(group_val = ".default", label_val = c("n","<65 yrs","<12 months","<25"), p = frmt_when(">0.99" ~ ">0.99",
                                                                                 "<0.001" ~ "<0.001",
                                                                                 TRUE ~ frmt("x.xxx", missing = "")))
  ),
  # remove extra cols
  col_plan = col_plan(-grp, 
                      -starts_with("ord") )) %>% 
  print_to_gt(data_demog) %>% 
  tab_options(
    container.width = 900
  )
Placebo Xanomeline Low Dose Xanomeline High Dose Total p-value
Age (y)
n 86 84 84 254 0.593
Mean 75.2 75.7 74.4 75.1
SD 8.59 8.29 7.89 8.25
Median 76.0 77.5 76.0 77.0
Min 52.0 51.0 56.0 51.0
Max 89.0 88.0 88.0 89.0
<65 yrs 14 (16.3 %) 8 ( 9.5 %) 11 (13.1 %) 33 (13.0 %) 0.144
65-80 yrs 42 (48.8 %) 47 (56.0 %) 55 (65.5 %) 144 (56.7 %)
>80 yrs 30 (34.9 %) 29 (34.5 %) 18 (21.4 %) 77 (30.3 %)
Sex
n 86 84 84 254 0.141
Male 33 (38.4 %) 34 (40.5 %) 44 (52.4 %) 111 (43.7 %)
Female 53 (61.6 %) 50 (59.5 %) 40 (47.6 %) 143 (56.3 %)
Race (Origin)
n 86 84 84 254 0.648
Caucasian 75 (87.2 %) 72 (85.7 %) 71 (84.5 %) 218 (85.8 %)
African Descent 8 ( 9.3 %) 6 ( 7.1 %) 9 (10.7 %) 23 ( 9.1 %)
Hispanic 3 ( 3.5 %) 6 ( 7.1 %) 3 ( 3.6 %) 12 ( 4.7 %)
Other 1 ( 1.2 %) 1 ( 0.4 %)
MMSE
n 86 84 84 254 0.595
Mean 18.0 17.9 18.5 18.1
SD 4.27 4.22 4.16 4.21
Median 19.5 18.0 20.0 19.0
Min 10.0 10.0 10.0 10.0
Max 23.0 24.0 24.0 24.0
Duration of disease
n 86 84 84 254 0.153
Mean 42.6 48.7 40.5 43.9
SD 30.24 29.58 24.69 28.40
Median 35.3 40.2 36.0 36.2
Min 7.2 7.8 2.2 2.2
Max 183.1 130.8 135.0 183.1
<12 months 5 ( 5.8 %) 3 ( 3.6 %) 4 ( 4.8 %) 12 ( 4.7 %) 0.789
>=12 months 81 (94.2 %) 81 (96.4 %) 80 (95.2 %) 242 (95.3 %)
Years of education
n 86 84 84 254 0.388
Mean 12.6 13.2 12.5 12.8
SD 2.95 4.15 2.92 3.38
Median 12.0 12.0 12.0 12.0
Min 6.0 3.0 6.0 3.0
Max 21.0 24.0 20.0 24.0
Baseline weight(kg)
n 86 83 84 253 0.003
Mean 62.8 67.3 70.0 66.6
SD 12.77 14.12 14.65 14.13
Median 60.5 64.9 69.2 66.7
Min 34.0 45.4 41.7 34.0
Max 86.2 106.1 108.0 108.0
Baseline height(cm)
n 86 84 84 254 0.126
Mean 162.6 163.4 165.8 163.9
SD 11.52 10.42 10.13 10.76
Median 162.6 162.6 165.1 162.8
Min 137.2 135.9 146.1 135.9
Max 185.4 195.6 190.5 195.6
Baseline BMI
n 86 83 84 253 0.013
Mean 23.6 25.1 25.3 24.7
SD 3.67 4.27 4.16 4.09
Median 23.4 24.3 24.8 24.2
Min 15.1 17.7 13.7 13.7
Max 33.3 40.1 34.5 40.1
<25 59 (68.6 %) 47 (56.0 %) 44 (52.4 %) 150 (59.1 %) 0.233
25-<30 21 (24.4 %) 27 (32.1 %) 28 (33.3 %) 76 (29.9 %)
>=30 6 ( 7.0 %) 10 (11.9 %) 12 (14.3 %) 28 (11.0 %)

Now this table looks just about right. There are two problems, (1) alignment and (2) spacing between the continuous and categorical values. To take care of the alignment we are going to add a col_style_plan which accepts a series of col_style_structures. This allows columns to be aligned differently if needed. For this table, we want all the columns to align on either “.”, “,” or ” ” so our col_style_structure looks like col_style_structure(align = c(".",","," "), col = vars(everything())). After the alignment is sorted we can move on to the spacing. In order to match the spacing of the mock we need to use the extra grp column from our data. If we look at our data, we can see we want a space any time either of the groups change.

data_demog %>% 
  distinct(rowlbl1,grp)
#> # A tibble: 12 × 2
#> # Groups:   rowlbl1 [9]
#>    rowlbl1                grp  
#>    <chr>                  <chr>
#>  1 "Age (y)"              cont 
#>  2 "Age (y)"              cat  
#>  3 "Sex"                  cat  
#>  4 "Race (Origin)"        cat  
#>  5 "MMSE"                 cont 
#>  6 "Duration of disease " cont 
#>  7 "Duration of disease " cat  
#>  8 "Years of education"   cont 
#>  9 "Baseline weight(kg)"  cont 
#> 10 "Baseline height(cm)"  cont 
#> 11 "Baseline BMI"         cont 
#> 12 "Baseline BMI"         cat

This means that we can use a row_grp_plan with just a ".default" as the group value and it should handle all of the spacing. In addition to the spacing, row_grp_plan will let us move the spanning group labels to a separate column by changing the label_loc to “column”.

tfrmt(
  # specify columns in the data
  group = c(rowlbl1,grp),
  label = rowlbl2,
  column = column, 
  param = param,
  value = value,
  sorting_cols = c(ord1, ord2),
  # specify value formatting 
  body_plan = body_plan(
    frmt_structure(group_val = ".default", label_val = ".default", frmt_combine("{n} {pct}", 
                                                                                n = frmt("xxx"),
                                                                                pct = frmt_when("==100" ~ "",
                                                                                                "==0" ~ "",
                                                                                                TRUE ~ frmt("(xx.x %)")))),
    frmt_structure(group_val = ".default", label_val = "n", frmt("xxx")),
    frmt_structure(group_val = ".default", label_val = c("Mean", "Median", "Min","Max"), frmt("xxx.x")),
    frmt_structure(group_val = ".default", label_val = "SD", frmt("xxx.xx")),
    frmt_structure(group_val = ".default", label_val = ".default", p = frmt("")),
    frmt_structure(group_val = ".default", label_val = c("n","<65 yrs","<12 months","<25"), p = frmt_when(">0.99" ~ ">0.99",
                                                                                 "<0.001" ~ "<0.001",
                                                                                 TRUE ~ frmt("x.xxx", missing = "")))
  ),
  # remove extra cols
  col_plan = col_plan(-grp, 
                      -starts_with("ord") ),
  # Specify column styling plan
  col_style_plan = col_style_plan(
    col_style_structure(align = c(".",","," "), col = vars(everything()))
  ),
  
    # Specify row group plan
  row_grp_plan = row_grp_plan(
    row_grp_structure(group_val = ".default", element_block(post_space = " ")),
    label_loc = element_row_grp_loc(location = "column")
  )
  
  ) %>% 
  print_to_gt(data_demog) %>% 
  tab_options(
    container.width = 900
  )
Placebo Xanomeline Low Dose Xanomeline High Dose Total p-value
Age (y) n 86 84 84 254 0.593
Mean 75.2 75.7 74.4 75.1
SD 8.59 8.29 7.89 8.25
Median 76.0 77.5 76.0 77.0
Min 52.0 51.0 56.0 51.0
Max 89.0