Reputation: 183
gtsummary by default puts the number of observations in a by
group beside the label for that group. This increases the width of the table... with many groups or large N, the table would quickly become very wide.
Is it possible to get gtsummary to report N on a separate row beneath the label? E.g.
> data(mtcars)
> mtcars %>%
+ select(mpg, cyl, vs, am) %>%
+ tbl_summary(by = am) %>%
+ as_tibble()
# A tibble: 6 x 3
`**Characteristic**` `**0**, N = 19` `**1**, N = 13`
<chr> <chr> <chr>
1 mpg 17.3 (14.9, 19.2) 22.8 (21.0, 30.4)
2 cyl NA NA
3 4 3 (16%) 8 (62%)
4 6 4 (21%) 3 (23%)
5 8 12 (63%) 2 (15%)
6 vs 7 (37%) 7 (54%)
would become
# A tibble: 6 x 3
`**Characteristic**` `**0**` `**1**`
<chr> <chr> <chr>
1 N = 19 N = 13
2 mpg 17.3 (14.9, 19.2) 22.8 (21.0, 30.4)
3 cyl NA NA
4 4 3 (16%) 8 (62%)
5 6 4 (21%) 3 (23%)
6 8 12 (63%) 2 (15%)
7 vs 7 (37%) 7 (54%)
(I only used as_tibble so that it was easy to show what I mean by editing it manually...)
Any idea?
Thanks!
Upvotes: 6
Views: 5116
Reputation: 11
Lots of great help from folks above.
Thought I'd add a suggestion for a helper function:
# Add {n} in a row in the table body
add_n_row <- \(x){
# x is a gtsummary object
x$table_body <- x$table_body |>
bind_rows(
# Retrieve {n} from the header
x$table_styling$header |>
select(column, modify_stat_n) |>
filter(!is.na(modify_stat_n)) |>
# Format as character (incl. commas for thousands)
mutate(modify_stat_n = scales::comma(modify_stat_n),
across(everything(), ~as.character(.x))) |>
# Reshape to be one row
pivot_wider(names_from = column, values_from = modify_stat_n) |>
# Add identifiers for table styling & structure
mutate(variable = "N", var_type = "continuous", var_label = "N",
row_type = "label", label = "N")
)
# Return the whole (edited) gtsummary object
return(x)
}
## Example ####
(cyl_tbl <- tbl_summary(
data = mtcars,
by = cyl, # split table by cylinder
statistic = list(all_categorical() ~ "{n} ({p}%)")
) |>
add_n_row() |>
modify_header(all_stat_cols() ~ "**{level}**") |>
bold_labels()
)
Upvotes: 0
Reputation: 578
Here is one way you could do this:
library(tidyverse)
library(gtsummary)
mtcars %>%
select(mpg, cyl, vs, am) %>%
# create a new variable to display N in table
mutate(total = 1) %>%
# this is just to reorder variables for table
select(total, everything()) %>%
tbl_summary(
by = am,
# this is to specify you only want N (and no percentage) for new total variable
statistic = total ~ "N = {N}") %>%
# this is a gtsummary function that allows you to edit the header
modify_header(all_stat_cols() ~ "**{level}**")
total
)gtsummary::modify_header()
to remove N from the headerAdditionally, if you use the flextable
print engine, you can add a line break in the header itself:
mtcars %>%
select(mpg, cyl, vs, am) %>%
# create a new variable to display N in table
tbl_summary(
by = am
# this is to specify you only want N (and no percentage) for new total variable
) %>%
# this is a gtsummary function that allows you to edit the header
modify_header(all_stat_cols() ~ "**{level}**\nN = {n}") %>%
as_flex_table()
Good luck!
Upvotes: 14
Reputation: 11689
@kittykatstat already posted two fantastic solutions! I'll just add a slight variation :)
If you want to use the {gt} package to print the table and you're outputting to HTML, you can use the HTML tag <br>
to add a line break in the header row (very similar to the \n
solution already posted).
library(gtsummary)
mtcars %>%
select(mpg, cyl, vs, am) %>%
dplyr::mutate(am = factor(am, labels = c("Manual", "Automatic"))) %>%
# create a new variable to display N in table
tbl_summary(by = am) %>%
# this is a gtsummary function that allows you to edit the header
modify_header(stat_by = "**{level}**<br>N = {N}")
Upvotes: 4