user1697590
user1697590

Reputation: 183

With gtsummary, is it possible to have N on a separate row to the column name?

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.

enter image description here

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

Answers (3)

Ian Davis
Ian Davis

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

kittykatstat
kittykatstat

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}**")
  • First, I am making a new variable that is just total observations (called total)
  • Then, I am customizing the way I want that variable statistic to be displayed
  • Then I am using gtsummary::modify_header() to remove N from the header

Additionally, 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

Daniel D. Sjoberg
Daniel D. Sjoberg

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}")

enter image description here

Upvotes: 4

Related Questions