rkablekableextragt

How to print headers on every page when printing a long table using gt


Trying to print a long table that span multiple pages using gt, however, the tab_options(latex.use_longtable = TRUE, page.header.use_tbl_headings = TRUE) function in gt is being ignored and it does not print headers on every page but only on the first page. Is there a workaround? I know that using kableExtra, some tables like xtable can be converted to kable with xtable2kable() and table1 tables can be converted to kable as well using t1kable(). Is there any function that converts gt tables to kable()?

See code below:

---
title: "GT Long Table Example"
output: pdf_document
---

```{r, echo=FALSE}
library(gt)

data <- data.frame(
  ID = 1:200,
  Name = paste("Name", 1:200),
  Value = rnorm(200)
)

data %>%
  gt() %>%
  tab_header(
    title = "Example Long Table",
    subtitle = "This table spans multiple pages") %>%
  tab_options(latex.use_longtable = TRUE, page.header.use_tbl_headings = TRUE)
```

UPDATE: larger dataset

library(tidyverse)

dat <- structure(list(StationCode = c("Gate 6", "Gate 7", "Gate 6", 
"Gate 7", "Gate 8", "Gate 8", "Gate 8", "Gate 7", "Gate 8", "Gate 7", 
"Gate 6", "Gate 8", "Gate 6", "Gate 8", "Gate 7", "Gate 7", "Gate 7", 
"Gate 7", "Gate 6", "Gate 8", "Gate 6", "Gate 3", "Gate 6", "Gate 6", 
"Gate 8", "Gate 8", "Gate 6", "Gate 8", "Gate 8", "Gate 7", "Gate 6", 
"Gate 7", "Gate 6", "Gate 8", "Gate 3"), SampleDate = structure(c(15387, 
15638, 15627, 15639, 15647, 15683, 15617, 15384, 15633, 15592, 
15627, 15688, 15370, 15660, 15657, 15688, 15650, 15366, 15662, 
15537, 15609, 15635, 15649, 15656, 15637, 15643, 15626, 15653, 
15393, 15627, 15626, 15684, 15632, 15612, 15593), class = "Date"), 
    WaterTemperature = c(49.2000007629395, 53.5, 55.5, 53.5, 
    54.7000007629395, 50.2000007629395, 57.0999984741211, 49.5999984741211, 
    55.4000015258789, 57.9000015258789, 55.5, 48.2999992370605, 
    48.5999984741211, 53, 52.0999984741211, 48.2999992370605, 
    55.4000015258789, 48.7000007629395, 52.7000007629395, 57, 
    56.7999992370605, 53.7000007629395, 55.5, 51.5, 53, 55.0999984741211, 
    54.5, 51.7999992370605, 50.7999992370605, 55.5, 54.5, 51, 
    55.7000007629395, 57.0999984741211, 57.7000007629395), Turbidity = c(2.0699999332428, 
    1.17999994754791, 1.48000001907349, 1.22000002861023, 1.45000004768372, 
    14.3000001907349, 1.16999995708466, 6.53000020980835, 1.14999997615814, 
    1.01999998092651, 1.48000001907349, 14, 2.70000004768372, 
    1.13999998569489, 1.28999996185303, 13.8999996185303, 1.29999995231628, 
    9.9399995803833, 48.7999992370605, 1.98000001907349, 0.980000019073486, 
    1.51999998092651, 1.1599999666214, 1.07000005245209, 1.36000001430511, 
    1.67999994754791, 0.959999978542328, 1.03999996185303, 2.58999991416931, 
    0.779999971389771, 0.959999978542328, 7.46999979019165, 0.899999976158142, 
    1.37000000476837, 1.64999997615814), ForkLength = c(95, 62, 
    57, 47, 65, 49, 33, 76, 39, 38, 38, 60, 90, 60, 55, 58, 67, 
    121, 44, 32, 39, 57, 43, 49, 52, 52, 55, 45, 83, 36, 33, 
    81, 58, 33, 32), Count = c(2, 3, 1, 2, 1, 1, 4, 1, 7, 1, 
    7, 1, 1, 2, 10, 1, 2, 1, 6, 1, 1, 1, 1, 5, 2, 1, 1, 3, 1, 
    26, 2, 1, 3, 3, 1), year = c("2012", "2012", "2012", "2012", 
    "2012", "2012", "2012", "2012", "2012", "2012", "2012", "2012", 
    "2012", "2012", "2012", "2012", "2012", "2012", "2012", "2012", 
    "2012", "2012", "2012", "2012", "2012", "2012", "2012", "2012", 
    "2012", "2012", "2012", "2012", "2012", "2012", "2012"), 
    race2 = c("Winter", "Winter", "Winter", "Winter", "Winter", 
    "Winter", "Winter", "Winter", "Winter", "Winter", "Winter", 
    "Winter", "Winter", "Winter", "Winter", "Winter", "Winter", 
    "Winter", "Winter", "Winter", "Winter", "Winter", "Winter", 
    "Winter", "Winter", "Winter", "Winter", "Winter", "Winter", 
    "Winter", "Winter", "Winter", "Winter", "Winter", "Winter"
    ), week = c("07", "43", "41", "43", "44", "49", "40", "07", 
    "42", "36", "41", "50", "05", "46", "46", "50", "45", "04", 
    "46", "29", "39", "43", "45", "46", "43", "44", "41", "45", 
    "08", "41", "41", "50", "42", "39", "37"), month = c("February", 
    "October", "October", "October", "November", "December", 
    "October", "February", "October", "September", "October", 
    "December", "January", "November", "November", "December", 
    "November", "January", "November", "July", "September", "October", 
    "November", "November", "October", "October", "October", 
    "November", "February", "October", "October", "December", 
    "October", "September", "September")), row.names = c(NA, 
35L), class = "data.frame")

dat_gt <- dat %>% 
  group_by(race2, year) %>% 
  mutate(obs = row_number()) %>% 
  pivot_wider(names_from = "race2", values_from = "Count", values_fill = 0) %>% 
  arrange(year, obs) %>% 
  select(-obs)

dat_gt |>
  mutate(SampleDate = as.character(SampleDate)) |>
  group_by(year, month, week) |>   
  summarise(across(c(Winter), sum), across(c(Turbidity,WaterTemperature), mean)) |> 
arrange(desc(year),desc(week),desc(month)) |>  
  gt() |>
summary_rows( 
    columns = c(Winter),
    fns =  list(label = "Total", fn = "sum"),
    side = "bottom") |>
  
  summary_rows( 
    columns = c(Turbidity,WaterTemperature),
    fns =  list(label="avg",fn = "mean"),
   fmt = ~ fmt_number(., decimals = 1, use_seps = FALSE)) |> #Format the 'avg' row
  fmt_number(columns = c(Turbidity,WaterTemperature),
             decimals = 1) |>

 # grand_summary_rows(columns = -c(SampleDate,Turbidity,WaterTemperature),
    grand_summary_rows(columns = c(Winter),
                     fns = list(label = "Grand Total", fn = "sum")) |>
  tab_header(title = "Fish Observations",
    subtitle = "Total number of fish by year,month, and week") %>%
  tab_options(latex.use_longtable = TRUE) %>%
  repeat_header_gt()

```

Solution

  • Here's an example that uses parseLatex to edit the header. This uses the CRAN version of parseLatex; things are a little bit simpler in the devel version, but really not very different.

    ---
    title: "GT Long Table Example"
    output: 
      pdf_document:
        keep_md: true
        keep_tex: true
    ---
    
    ```{r, echo=FALSE}
    
    library(gt)
    library(parseLatex)
    
    repeat_header_gt <- function(gt, continuation = "\\textit{(continued...)}") {
      latex <- as_latex(gt)
      parsed <- parseLatex(latex) 
      table <- find_tabular(parsed)
      toprule <- find_macro(parsed[[table]], "\\toprule")[1]
      midrule <- find_macro(parsed[[table]], "\\midrule")[1]
      header <- parsed[[table]][toprule:midrule]
      range <- LaTeX2range(table, midrule)
      parsed <- set_range(parsed, range,
                  paste0("\\midrule\n\\endfirsthead\n\\caption*{", 
                         continuation,
                         "}\\\\\n", deparseLatex(header), "\\endhead\n"))
      newlatex <- deparseLatex(parsed)
      attributes(newlatex) <- attributes(latex)
      newlatex
    }
    
    data <- data.frame(
      ID = 1:200,
      Name = paste("Name", 1:200),
      Value = rnorm(200)
    )
    data %>%
      gt() %>%
      tab_header(
        title = "Example Long Table",
        subtitle = "This table spans multiple pages") %>%
      tab_options(latex.use_longtable = TRUE, page.header.use_tbl_headings = TRUE) %>%
      repeat_header_gt()
    ```
    

    The second page will have a header like this: screenshot

    If you want a different continuation message, you can include it as the continuation argument to repeat_header_gt. It needs to be legal LaTeX code with the backslashes escaped.