Skip to content

Commit

Permalink
Merge branch 'main' into 767_sort_filter_hierarchical
Browse files Browse the repository at this point in the history
  • Loading branch information
ddsjoberg authored Jan 7, 2025
2 parents ae03a80 + 14362f7 commit 5f9f63c
Show file tree
Hide file tree
Showing 32 changed files with 1,022 additions and 277 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: gtsummary
Title: Presentation-Ready Data Summary and Analytic Result Tables
Version: 2.0.4.9006
Version: 2.0.4.9007
Authors@R: c(
person("Daniel D.", "Sjoberg", , "[email protected]", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-0862-2018")),
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export(modify_fmt_fun)
export(modify_footnote)
export(modify_footnote_body)
export(modify_footnote_header)
export(modify_footnote_spanning_header)
export(modify_header)
export(modify_source_note)
export(modify_spanning_header)
Expand All @@ -188,8 +189,10 @@ export(ratio_summary)
export(remove_abbreviation)
export(remove_footnote_body)
export(remove_footnote_header)
export(remove_footnote_spanning_header)
export(remove_row_type)
export(remove_source_note)
export(remove_spanning_header)
export(reset_gtsummary_theme)
export(scope_header)
export(scope_table_body)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

* Previously, source notes were an undocumented feature and only a single source note could be included in a table. We now export `modify_source_note()` and `remove_source_note()` to add and remove any number of source notes. Also, when merging and stacking tables, previously due to the one source note limit, only the first source note was retained. Now all source notes will be included below the resulting table. _This is different behavior compared to previous versions of the package and in rare cases may result in a different source note._ Moreover, `kableExtra` output now supports source notes, where previously they were omitted.

* The `modify_spanning_header(level)` argument has been added to allow for multiple levels of spanning headers in the resulting tables. The `remove_spanning_header()` function has also been added to ease the removal of spanning headers. (#2099)

* The `modify_footnote_spanning_header()` function has been added to ease adding footnotes to spanning headers. A companion function, `remove_footnote_spanning_header()`, has been added to remove spanning headers.

* Language translations have been updated with a handful of missing translations. (#2100)

* The `modify_caption(caption)` argument now accepts a vector of captions, instead of just a string. Note, however, that not all print engines support a vector of captions. (#2107)
Expand Down
104 changes: 60 additions & 44 deletions R/as_flex_table.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,51 +118,60 @@ table_styling_to_flextable_calls <- function(x, ...) {

# add_header_row -------------------------------------------------------------
# this is the spanning rows
any_spanning_header <- any(!is.na(x$table_styling$header$spanning_header))
any_spanning_header <- nrow(x$table_styling$spanning_header) > 0L
if (any_spanning_header == FALSE) {
flextable_calls[["add_header_row"]] <- list()
} else {
df_header0 <-
x$table_styling$header |>
dplyr::filter(.data$hide == FALSE) |>
dplyr::select("spanning_header") |>
flextable_calls[["add_header_row"]] <-
tidyr::expand_grid(
level = unique(x$table_styling$spanning_header$level),
column = x$table_styling$header$column[!x$table_styling$header$hide]
) |>
dplyr::left_join(
x$table_styling$spanning_header[c("level", "column", "spanning_header")],
by = c("level", "column")
) |>
dplyr::mutate(
spanning_header = ifelse(is.na(.data$spanning_header),
" ", .data$spanning_header
),
.by = "level",
spanning_header =
ifelse(is.na(.data$spanning_header), " ", .data$spanning_header),
spanning_header_id = dplyr::row_number()
)
# assigning an ID for each spanning header group
for (i in seq(2, nrow(df_header0))) {
if (df_header0$spanning_header[i] == df_header0$spanning_header[i - 1]) {
df_header0$spanning_header_id[i] <- df_header0$spanning_header_id[i - 1]
}
}

df_header <-
df_header0 |>
dplyr::group_by(.data$spanning_header_id) |>
dplyr::mutate(width = dplyr::n()) |>
dplyr::distinct() |>
dplyr::ungroup() |>
dplyr::mutate(
column_id = map2(.data$spanning_header_id, .data$width, ~ seq(.x, .x + .y - 1L, by = 1L))
)
) |>
dplyr::group_by(.data$level) |>
dplyr::group_map(
\(df_values, df_group) {
# assigning an ID for each spanning header group
for (i in seq(2, nrow(df_values))) {
if (df_values$spanning_header[i] == df_values$spanning_header[i - 1]) {
df_values$spanning_header_id[i] <- df_values$spanning_header_id[i - 1]
}
}

flextable_calls[["add_header_row"]] <- list(
expr(
# add the header row with the spanning headers
flextable::add_header_row(
values = !!df_header$spanning_header,
colwidths = !!df_header$width
)
)
)
df_header <-
dplyr::bind_cols(df_group, df_values) |>
dplyr::select(-"column") |>
dplyr::group_by(.data$spanning_header_id) |>
dplyr::mutate(width = dplyr::n()) |>
dplyr::distinct() |>
dplyr::ungroup() |>
dplyr::mutate(
column_id = map2(.data$spanning_header_id, .data$width, ~ seq(.x, .x + .y - 1L, by = 1L))
)

flextable_calls[["compose_header_row"]] <-
.chr_with_md_to_ft_compose(
x = df_header$spanning_header,
j = df_header$column_id
c(
list(expr(
# add the header row with the spanning headers
flextable::add_header_row(
values = !!df_header$spanning_header,
colwidths = !!df_header$width
)
)),
.chr_with_md_to_ft_compose(
x = df_header$spanning_header,
j = df_header$column_id
)
)
}
)
}

Expand Down Expand Up @@ -207,20 +216,28 @@ table_styling_to_flextable_calls <- function(x, ...) {
flextable_calls[["autofit"]] <- expr(flextable::autofit())

# footnote_header ------------------------------------------------------------
spanning_header_lvls <- x$table_styling$spanning_header$level |> append(0L) |> max()
df_footnote_header <-
.number_footnotes(x, "footnote_header") |>
tidyr::nest(df_location = c("column", "column_id")) |>
dplyr::bind_rows(
x$table_styling$footnote_header |> dplyr::mutate(level = 0L),
x$table_styling$footnote_spanning_header
) |>
dplyr::mutate(
row_numbers = .env$spanning_header_lvls - .data$level + 1L
) %>%
.number_footnotes(x, type = .) |>
tidyr::nest(df_location = c("column", "column_id", "row_numbers")) |>
dplyr::mutate(
row_numbers = map(.data$df_location, ~ getElement(.x, "row_numbers")),
column_id = map(.data$df_location, ~ getElement(.x, "column_id"))
)
header_i_index <- ifelse(any_spanning_header == TRUE, 2L, 1L)

flextable_calls[["footnote_header"]] <-
map(
seq_len(nrow(df_footnote_header)),
~ expr(
flextable::footnote(
i = !!header_i_index,
i = !!df_footnote_header$row_numbers[[.x]],
j = !!df_footnote_header$column_id[[.x]],
value = flextable::as_paragraph(!!df_footnote_header$footnote[[.x]]),
part = "header",
Expand All @@ -231,7 +248,7 @@ table_styling_to_flextable_calls <- function(x, ...) {

# footnote_body --------------------------------------------------------------
df_footnote_body <-
.number_footnotes(x, "footnote_body", start_with = nrow(df_footnote_header)) |>
.number_footnotes(x, type = x$table_styling$footnote_body, start_with = nrow(df_footnote_header)) |>
tidyr::nest(df_location = c("column", "column_id", "row_numbers")) |>
dplyr::mutate(
row_numbers = map(.data$df_location, ~ getElement(.x, "row_numbers")),
Expand All @@ -252,7 +269,6 @@ table_styling_to_flextable_calls <- function(x, ...) {
)
)


# abbreviation ---------------------------------------------------------------
flextable_calls[["abbreviations"]] <-
case_switch(
Expand Down
64 changes: 39 additions & 25 deletions R/as_gt.R
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,25 @@ table_styling_to_gt_calls <- function(x, ...) {
expr(gt::cols_label_with(fn = function(x) gsub(x = x, pattern = "\\n(?!\\\\)", replacement = "", fixed = FALSE, perl = TRUE)))
)

# spanning_header ------------------------------------------------------------
gt_calls[["tab_spanner"]] <-
case_switch(
nrow(x$table_styling$spanning_header) > 0L ~
x$table_styling$spanning_header |>
dplyr::group_by(.data$level, .data$spanning_header, .data$text_interpret) |>
dplyr::group_map(
\(.x, .y) {
expr(gt::tab_spanner(
columns = !!.x$column,
label = !!call2(parse_expr(.y$text_interpret), .y$spanning_header),
level = !!.y$level,
id = !!paste0("level ", .y$level, "; ", .x$column[1]),
gather = FALSE
))
}
),
.default = list()
)

# tab_footnote ---------------------------------------------------------------
gt_calls[["tab_footnote"]] <-
Expand Down Expand Up @@ -270,32 +289,27 @@ table_styling_to_gt_calls <- function(x, ...) {
)
)
}
)
)

# spanning_header ------------------------------------------------------------
df_spanning_header <-
x$table_styling$header |>
dplyr::select("column", "interpret_spanning_header", "spanning_header") |>
dplyr::filter(!is.na(.data$spanning_header)) |>
tidyr::nest(cols = "column") |>
dplyr::mutate(
spanning_header = map2(
.data$interpret_spanning_header, .data$spanning_header,
~ call2(parse_expr(.x), .y)
),
cols = map(.data$cols, ~ dplyr::pull(.x))
) |>
dplyr::select("spanning_header", "cols")

gt_calls[["tab_spanner"]] <-
map(
seq_len(nrow(df_spanning_header)),
~ expr(gt::tab_spanner(
columns = !!df_spanning_header$cols[[.x]],
label = gt::md(!!df_spanning_header$spanning_header[[.x]]),
gather = FALSE
))
# spanning header footnotes
map(
seq_len(nrow(x$table_styling$footnote_spanning_header)),
function(i) {
expr(
gt::tab_footnote(
footnote =
!!call2(
parse_expr(x$table_styling$footnote_spanning_header$text_interpret[i]),
x$table_styling$footnote_spanning_header$footnote[i]
),
locations =
gt::cells_column_spanners(
spanners = !!paste0("level ", x$table_styling$footnote_spanning_header$level[i], "; ", x$table_styling$footnote_spanning_header$column[i]),
levels = !!x$table_styling$footnote_spanning_header$level[i]
)
)
)
}
)
)

# horizontal_line ------------------------------------------------------------
Expand Down
56 changes: 31 additions & 25 deletions R/as_hux_table.R
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,9 @@ table_styling_to_huxtable_calls <- function(x, ...) {
# footnote -------------------------------------------------------------------
vct_footnote <-
dplyr::bind_rows(
.number_footnotes(x, "footnote_header"),
.number_footnotes(x, "footnote_body")
.number_footnotes(x, x$table_styling$footnote_spanning_header),
.number_footnotes(x, x$table_styling$footnote_header),
.number_footnotes(x, x$table_styling$footnote_body)
) |>
dplyr::pull("footnote") %>%
unique()
Expand Down Expand Up @@ -331,30 +332,35 @@ table_styling_to_huxtable_calls <- function(x, ...) {
expr(huxtable::insert_row(after = 0, !!!col_labels))
)

any_spanning_header <- sum(!is.na(x$table_styling$header$spanning_header)) > 0
if (any_spanning_header) {
header_content <- x$table_styling$header$spanning_header[x$table_styling$header$hide == FALSE]
huxtable_calls[["insert_row"]] <- append(
huxtable_calls[["insert_row"]],
expr(huxtable::insert_row(after = 0, !!!header_content))
)

header_colspans <- rle(header_content)$lengths
header_colspan_cols <- cumsum(c(
1,
header_colspans[-length(header_colspans)]
))
huxtable_calls[["insert_row"]] <- append(
huxtable_calls[["insert_row"]],
expr(
huxtable::set_colspan(
row = 1, col = !!header_colspan_cols,
value = !!header_colspans
)
if (nrow(x$table_styling$spanning_header) > 0L) {
huxtable_calls[["insert_row"]] <-
huxtable_calls[["insert_row"]] |>
append(
tidyr::expand_grid(
level = unique(x$table_styling$spanning_header$level),
column = x$table_styling$header$column[!x$table_styling$header$hide]
) |>
dplyr::left_join(
x$table_styling$spanning_header[c("level", "column", "spanning_header")],
by = c("level", "column")
) |>
dplyr::group_by(.data$level) |>
dplyr::group_map(
\(df_values, df_group) {
header_content <- df_values$spanning_header
header_colspans <- rle(header_content)$lengths
header_colspan_cols <- cumsum(c(1, header_colspans[-length(header_colspans)]))

list(
expr(huxtable::insert_row(after = 0, !!!header_content)),
expr(huxtable::set_colspan(row = 1, col = !!header_colspan_cols, value = !!header_colspans))
)
}
)
)
)
}
header_bottom_row <- if (any_spanning_header) 2 else 1

header_bottom_row <- length(unique(x$table_styling$spanning_header$level)) + 1L
huxtable_calls[["insert_row"]] <- append(
huxtable_calls[["insert_row"]],
expr(
Expand All @@ -366,7 +372,7 @@ table_styling_to_huxtable_calls <- function(x, ...) {
)

# set_markdown ---------------------------------------------------------------
header_rows <- switch(any_spanning_header, 1:2) %||% 1L # styler: off
header_rows <- seq_len(length(unique(x$table_styling$spanning_header$level)) + 1L) # styler: off
huxtable_calls[["set_markdown"]] <-
list(
set_markdown =
Expand Down
Loading

0 comments on commit 5f9f63c

Please sign in to comment.