Skip to contents

The tfrmt package is designed to create highly formatted Tables by layering formatting logic onto data. While the primary output of print_to_gt() is a gt object ready for reporting, there are times when you may need to access the “final” processed data frame—for example, to perform secondary validations, to export the data, or use in another reporting tool.

The extract_data() function provides a clean way to pull this underlying data back out of a gt_tbl or gt_group object. extract_data() handles the cleanup of the data, removing internal columns and ensuring that column names reflect the labels seen in the final table. The extracted data frame contains the formatted character strings as seen in the table.

Basic Usage

In this example, we take a demographic dataset, apply a tfrmt, and then extract the resulting data frame.

# Subset data for a simple example
data_demog_test <- data_demog |>
  filter(
    rowlbl1 %in% c("Age (y)", "Sex"),
    column != "p-value"
  )

# Define and print the tfrmt
tfrmt_obj <- tfrmt(
  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"))
  ),
  # remove extra cols
  col_plan = col_plan(
    -grp,
    -ord1,
    -ord2
  )
)

gt_table <- print_to_gt(tfrmt_obj, data_demog_test)

gt_table
Placebo Xanomeline Low Dose Xanomeline High Dose Total
Age (y)



  n  86  84  84 254
  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 %)
  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
  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 %)

# Extract the data
extracted_df <- extract_data(gt_table)

extracted_df
#> # A tibble: 14 × 5
#>    rowlbl2       Placebo      `Xanomeline Low Dose` `Xanomeline High Dose` Total
#>    <chr>         <chr>        <chr>                 <chr>                  <chr>
#>  1 "Age (y)"      NA           NA                    NA                     NA  
#>  2 "  n"         " 86"        " 84"                 " 84"                  "254"
#>  3 "  Mean"      " 75.2"      " 75.7"               " 74.4"                " 75…
#>  4 "  SD"        "  8.59"     "  8.29"              "  7.89"               "  8…
#>  5 "  Median"    " 76.0"      " 77.5"               " 76.0"                " 77…
#>  6 "  Min"       " 52.0"      " 51.0"               " 56.0"                " 51…
#>  7 "  Max"       " 89.0"      " 88.0"               " 88.0"                " 89…
#>  8 "  <65 yrs"   " 14 (16.3 … "  8 ( 9.5 %)"        " 11 (13.1 %)"         " 33…
#>  9 "  65-80 yrs" " 42 (48.8 … " 47 (56.0 %)"        " 55 (65.5 %)"         "144…
#> 10 "  >80 yrs"   " 30 (34.9 … " 29 (34.5 %)"        " 18 (21.4 %)"         " 77…
#> 11 "Sex"          NA           NA                    NA                     NA  
#> 12 "  n"         " 86"        " 84"                 " 84"                  "254"
#> 13 "  Male"      " 33 (38.4 … " 34 (40.5 %)"        " 44 (52.4 %)"         "111…
#> 14 "  Female"    " 53 (61.6 … " 50 (59.5 %)"        " 40 (47.6 %)"         "143…

Extracting from Paged Tables (gt_group)

When using a page_plan, print_to_gt() returns a gt_group object containing multiple tables. extract_data() automatically detects this and returns a list of data frames, one for each page.

In this example we add pagination to our table using page_plan() and extract the data from the gt_group object using extract_data().

tfrmt_paged <- tfrmt_obj |>
  tfrmt(
    page_plan = page_plan(
      page_structure(
        group_val = list(
          rowlbl1 = ".default"
        )
      ),
      note_loc = "source_note"
    )
  )

gt_paged <- print_to_gt(tfrmt_paged, data_demog_test)

gt_paged
Placebo Xanomeline Low Dose Xanomeline High Dose Total
Age (y)



  n  86  84  84 254
  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 %)
  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 %)
rowlbl1: Age (y)
Placebo Xanomeline Low Dose Xanomeline High Dose Total
Sex



  n  86  84  84 254
  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 %)
rowlbl1: Sex

# This returns a list of data frames
data_list <- extract_data(gt_paged)

data_list
#> [[1]]
#> # A tibble: 10 × 5
#>    rowlbl2       Placebo      `Xanomeline Low Dose` `Xanomeline High Dose` Total
#>    <chr>         <chr>        <chr>                 <chr>                  <chr>
#>  1 "Age (y)"      NA           NA                    NA                     NA  
#>  2 "  n"         " 86"        " 84"                 " 84"                  "254"
#>  3 "  Mean"      " 75.2"      " 75.7"               " 74.4"                " 75…
#>  4 "  SD"        "  8.59"     "  8.29"              "  7.89"               "  8…
#>  5 "  Median"    " 76.0"      " 77.5"               " 76.0"                " 77…
#>  6 "  Min"       " 52.0"      " 51.0"               " 56.0"                " 51…
#>  7 "  Max"       " 89.0"      " 88.0"               " 88.0"                " 89…
#>  8 "  <65 yrs"   " 14 (16.3 … "  8 ( 9.5 %)"        " 11 (13.1 %)"         " 33…
#>  9 "  65-80 yrs" " 42 (48.8 … " 47 (56.0 %)"        " 55 (65.5 %)"         "144…
#> 10 "  >80 yrs"   " 30 (34.9 … " 29 (34.5 %)"        " 18 (21.4 %)"         " 77…
#> 
#> [[2]]
#> # A tibble: 4 × 5
#>   rowlbl2    Placebo        `Xanomeline Low Dose` `Xanomeline High Dose` Total  
#>   <chr>      <chr>          <chr>                 <chr>                  <chr>  
#> 1 "Sex"       NA             NA                    NA                    NA     
#> 2 "  n"      " 86"          " 84"                 " 84"                  254    
#> 3 "  Male"   " 33 (38.4 %)" " 34 (40.5 %)"        " 44 (52.4 %)"         111 (4…
#> 4 "  Female" " 53 (61.6 %)" " 50 (59.5 %)"        " 40 (47.6 %)"         143 (5…

Integration with Big N

extract_data() is also compatible with big_n_structure. If your table includes Big N values in the column headers, these formatted strings will be preserved as the column names in the extracted data frame.

Example showing big Ns preserved in the column names after using extract_data().

data <- tibble::tibble(
  Group = c("N", "N", "N", rep(c("Age (y)", "Sex", "Age (y)", "Sex"), c(3, 3, 6, 12))),
  Label = c("N", "N", "N", rep(c("n", "Mean (SD)", "Male", "Female"), c(6, 6, 6, 6))),
  Column = c("Placebo", "Treatment", "Total", rep(c("Placebo", "Treatment", "Total"), times = 8)),
  Param = c("bigN", "bigN", "bigN", rep(c("n", "mean", "sd", "n", "pct", "n", "pct"), c(6, 3, 3, 3, 3, 3, 3))),
  Value = c(
    30, 40, 60, 15, 13, 28, 14, 13, 27, 73.56, 74.231, 71.84, 9.347, 7.234, 8.293,
    8, 7, 15, 8 / 14, 7 / 13, 15 / 27, 6, 6, 12, 6 / 14, 6 / 13, 12 / 27
  )
) |>
  dplyr::mutate(
    Value = dplyr::case_when(
      Param == "pct" ~ Value * 100,
      TRUE ~ Value
    ),
    ord1 = dplyr::if_else(Param == "bigN", 0, 1),
    ord2 = dplyr::if_else(Param == "bigN", 0, 1)
  )


bign <- tfrmt(
  group = Group,
  label = Label,
  column = Column,
  value = Value,
  param = Param,
  sorting_cols = c(ord1, ord2),
  body_plan = body_plan(
    frmt_structure(
      group_val = ".default",
      label_val = ".default",
      frmt_combine("{n} {pct}",
        n = frmt("X"),
        pct = frmt("(xx.x%)", missing = " ")
      )
    ),
    frmt_structure(
      group_val = "Age (y)", label_val = "Mean (SD)",
      frmt_combine("{mean} ({sd})",
        mean = frmt("XX.X"),
        sd = frmt("x.xx")
      )
    ),
    frmt_structure(group_val = ".default", label_val = "n", frmt("xx"))
  ),
  col_plan = col_plan(everything(), -starts_with("ord"), "Total"),
  row_grp_plan = row_grp_plan(
    row_grp_structure(group_val = ".default", element_block(post_space = " "))
  ),
  big_n = big_n_structure(param_val = "bigN", n_frmt = frmt("\nN = xx"))
) |>
  print_to_gt(data)

bign
Placebo N = 30 Treatment N = 40 Total N = 60
Age (y)


  n 15 13 28
Sex


  n 14 13 27
Age (y)


  Mean (SD) 73.6 (9.35) 74.2 (7.23) 71.8 (8.29)
         
Sex


  Male 8 (57.1%) 7 (53.8%) 15 (55.6%)
  Female 6 (42.9%) 6 (46.2%) 12 (44.4%)

extracted <- extract_data(bign)

extracted
#> # A tibble: 10 × 4
#>    Label         `Placebo\nN = 30` `Treatment\nN = 40` `Total\nN = 60`
#>    <chr>         <chr>             <chr>               <chr>          
#>  1 "Age (y)"      NA                NA                  NA            
#>  2 "  n"         "15"              "13"                "28"           
#>  3 "Sex"          NA                NA                  NA            
#>  4 "  n"         "14"              "13"                "27"           
#>  5 "Age (y)"      NA                NA                  NA            
#>  6 "  Mean (SD)" "73.6 (9.35)"     "74.2 (7.23)"       "71.8 (8.29)"  
#>  7 "   "         " "               " "                 " "            
#>  8 "Sex"          NA                NA                  NA            
#>  9 "  Male"      "8 (57.1%)"       "7 (53.8%)"         "15 (55.6%)"   
#> 10 "  Female"    "6 (42.9%)"       "6 (46.2%)"         "12 (44.4%)"

Handling Spanning Headers

If your table has multiple levels of column headers, gt stores these internally using a delimiter.

By default, extract_data() will collapse these levels using an underscore (_), but you can specify a custom delimiter using the col_delim argument.

Example showing how spanning headers are combined using col_delim argument in extract_data().

data <- tibble::tribble(
  ~group, ~label, ~span2, ~span1, ~my_col, ~parm, ~val,
  "g1", "rowlabel1", "column cols", "cols 1,2", "col1", "value", 1,
  "g1", "rowlabel1", "column cols", "cols 1,2", "col2", "value", 1,
  "g1", "rowlabel1", NA, NA, "mycol3", "value", 1,
  "g1", "rowlabel1", "column cols", "col 4", "col4", "value", 1,
  "g1", "rowlabel1", NA, NA, "mycol5", "value", 1,
  "g1", "rowlabel2", "column cols", "cols 1,2", "col1", "value", 2,
  "g1", "rowlabel2", "column cols", "cols 1,2", "col2", "value", 2,
  "g1", "rowlabel2", NA, NA, "mycol3", "value", 2,
  "g1", "rowlabel2", "column cols", "col 4", "col4", "value", 2,
  "g1", "rowlabel2", NA, NA, "mycol5", "value", 2,
  "g2", "rowlabel3", "column cols", "cols 1,2", "col1", "value", 3,
  "g2", "rowlabel3", "column cols", "cols 1,2", "col2", "value", 3,
  "g2", "rowlabel3", NA, NA, "mycol3", "value", 3,
  "g2", "rowlabel3", "column cols", "col 4", "col4", "value", 3,
  "g2", "rowlabel3", NA, NA, "mycol5", "value", 3,
)

# 2 layers of spanning headers
spanning_tfrmt <- tfrmt(
  group = group,
  label = label,
  param = parm,
  value = val,
  column = c(span2, span1, my_col),
  body_plan = body_plan(
    frmt_structure(group_val = ".default", label_val = ".default", frmt("x"))
  ),
  col_plan = col_plan(
    group,
    label,
    starts_with("col")
  )
) |> print_to_gt(data)

spanning_tfrmt
column cols
cols 1,2
col 4
mycol3 mycol5
col1 col2 col4
g1




  rowlabel1 1 1 1 1 1
  rowlabel2 2 2 2 2 2
g2




  rowlabel3 3 3 3 3 3

res_layer <- extract_data(spanning_tfrmt, col_delim = "/")
res_layer
#> # A tibble: 5 × 6
#>   label     column cols/cols 1,2…¹ column cols/cols 1,2…² column cols/col 4/co…³
#>   <chr>     <chr>                  <chr>                  <chr>                 
#> 1 "g1"      NA                     NA                     NA                    
#> 2 "  rowla… 1                      1                      1                     
#> 3 "  rowla… 2                      2                      2                     
#> 4 "g2"      NA                     NA                     NA                    
#> 5 "  rowla… 3                      3                      3                     
#> # ℹ abbreviated names: ¹​`column cols/cols 1,2/col1`,
#> #   ²​`column cols/cols 1,2/col2`, ³​`column cols/col 4/col4`
#> # ℹ 2 more variables: mycol3 <chr>, mycol5 <chr>