How do you get percentages across rows rather than columns in tbl_summary?

Hi,
I can’t figure out how to get the percentages across the rows instead of down the columns in the table I’m producing with tbl_summary. This is for my own knowledge and is using fake data (i.e. not urgent).

I’ve tried googling and looking at various pages that come up. Looking at the information pages for tbl_summary and add_p, looking through the Epi R handbook - and I just can’t seem to figure it out. Below should reproduce the table I’m getting where percentages are calculated down the columns. I want the percentages calculated across the rows.

Thank you for your time and any assistance!

# load packages

pacman::p_load (
  rio,         #for importing data
  here,        #for file paths
  janitor,     #for data cleaning
  epikit,      #for creating age categories
  flextable,   #for formatting tables
  scales,      #for formatting values
  gtsummary,   #for pretty tables
  ggExtra,     #add additional plotting fxns to ggplot2 incl in tidyverse
  gghighlight, #highlights portions of the plot
  viridis,     #for more color palettes, incl color-blind friendly palette
  scales,      #for formatting
  lubridate,   #for dates
  apyramid,    #for creating age pyramids
  tsibble,     #for epiweeks & other time series analyses
  reprex,
  datapasta,
  tidyverse    #for data management
)

# import data
linelist <- data.frame(
  stringsAsFactors = FALSE,
  race = c("Black","Black","White",
           "White","Black","White","Black","White","White","Black",
           "White",NA,"White","White","White",NA,NA,"Black",
           "Black","Black","White","White","Black","White",
           "White","Black","Asian","Black",NA,"White","White",
           "Black",NA,"Black","White","White","Black","Black",
           "Black","Black","Black","Black",NA,"Black","White",
           "Black","Black","White","White","White","White",
           "Black","White","White","Black","Other","Asian",
           "White","White","White","White","Black","Black","Black",
           "Asian","White","Black","White","White",NA,
           "White","Black","Black","White","White","White","Asian",
           "Black","White","Black","Black","White","White",
           "Black","White","Black",NA,"Black","Black","Black",
           "Black","Black","White","Other",NA,"White","Black",
           "White",NA,"White"),
  hospitalized = c("No","No","No","No","No",
                   "Yes","No","No",NA,NA,NA,NA,NA,NA,NA,NA,NA,
                   "No","No","Yes","No","Yes",NA,"Yes",NA,NA,"No",
                   "No",NA,NA,NA,NA,NA,"Yes","No","No",NA,"No","No",
                   NA,"No","No",NA,"No",NA,NA,NA,"Yes",NA,NA,
                   "No",NA,"No","No","No",NA,"No",NA,NA,"No",NA,NA,
                   NA,"No","No",NA,NA,"Yes","No",NA,NA,NA,"No",NA,
                   "No","No",NA,"Yes","No",NA,"No","No",NA,NA,NA,
                   "No",NA,"No",NA,"No","Yes",NA,NA,"No","No",
                   "No",NA,"No",NA,NA)
)


# table with gtsummary
linelist %>% select(
  race,                        # values I want separated out by
  hospitalized
) %>%
  tbl_summary(by = hospitalized
  ) %>%
  add_p() %>%                                  # applies pearson's chi-square test
  as_flex_table() 
#> 50 observations missing `hospitalized` have been removed. To include these observations, use `forcats::fct_na_value_to_level()` on `hospitalized` column before passing to `tbl_summary()`.

Created on 2024-04-12 with reprex v2.1.0

Session info
sessionInfo()
#> R version 4.3.0 (2023-04-21 ucrt)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19045)
#> 
#> Matrix products: default
#> 
#> 
#> locale:
#> [1] LC_COLLATE=English_United States.utf8 
#> [2] LC_CTYPE=English_United States.utf8   
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.utf8    
#> 
#> time zone: America/New_York
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] forcats_1.0.0     stringr_1.5.1     dplyr_1.1.4       purrr_1.0.2      
#>  [5] readr_2.1.5       tidyr_1.3.0       tibble_3.2.1      tidyverse_2.0.0  
#>  [9] datapasta_3.1.0   reprex_2.1.0      tsibble_1.1.3     apyramid_0.1.3   
#> [13] lubridate_1.9.3   viridis_0.6.4     viridisLite_0.4.2 gghighlight_0.4.1
#> [17] ggplot2_3.4.4     ggExtra_0.10.1    gtsummary_1.7.2   scales_1.3.0     
#> [21] flextable_0.9.4   epikit_0.1.5      janitor_2.2.0     here_1.0.1       
#> [25] rio_1.0.1        
#> 
#> loaded via a namespace (and not attached):
#>  [1] DBI_1.2.1               gridExtra_2.3           rlang_1.1.3            
#>  [4] magrittr_2.0.3          snakecase_0.11.1        e1071_1.7-14           
#>  [7] compiler_4.3.0          png_0.1-8               systemfonts_1.0.5      
#> [10] vctrs_0.6.5             httpcode_0.3.0          pkgconfig_2.0.3        
#> [13] crayon_1.5.2            fastmap_1.1.1           backports_1.4.1        
#> [16] ellipsis_0.3.2          utf8_1.2.4              promises_1.2.1         
#> [19] rmarkdown_2.25          tzdb_0.4.0              anytime_0.3.9          
#> [22] ragg_1.2.7              xfun_0.41               jsonlite_1.8.8         
#> [25] highr_0.10              later_1.3.2             uuid_1.2-0             
#> [28] broom_1.0.5             R6_2.5.1                stringi_1.8.3          
#> [31] Rcpp_1.0.12             knitr_1.45              pacman_0.5.1           
#> [34] httpuv_1.6.13           timechange_0.2.0        tidyselect_1.2.0       
#> [37] rstudioapi_0.15.0       yaml_2.3.8              miniUI_0.1.1.1         
#> [40] curl_5.2.0              shiny_1.8.0             withr_3.0.0            
#> [43] askpass_1.2.0           evaluate_0.23           sf_1.0-15              
#> [46] units_0.8-5             proxy_0.4-27            zip_2.3.0              
#> [49] xml2_1.3.6              pillar_1.9.0            KernSmooth_2.23-20     
#> [52] generics_0.1.3          rprojroot_2.0.4         hms_1.1.3              
#> [55] munsell_0.5.0           xtable_1.8-4            class_7.3-21           
#> [58] glue_1.7.0              gdtools_0.3.5           tools_4.3.0            
#> [61] gfonts_0.2.0            data.table_1.14.10      fs_1.6.3               
#> [64] grid_4.3.0              colorspace_2.1-0        cli_3.6.2              
#> [67] textshaping_0.3.7       officer_0.6.3           fontBitstreamVera_0.1.1
#> [70] fansi_1.0.6             broom.helpers_1.14.0    gt_0.10.1              
#> [73] gtable_0.3.4            digest_0.6.34           fontquiver_0.2.1       
#> [76] classInt_0.4-10         crul_1.4.0              htmltools_0.5.7        
#> [79] lifecycle_1.0.4         mime_0.12               fontLiberation_0.1.0   
#> [82] openssl_2.1.1
1 Like

Hello,

The percent argument of the tbl_summary controls how the percentages are calculated, see below:

# loading packages
library(tidyverse)
library(gtsummary)

# creating fake data
linelist <- data.frame(
    stringsAsFactors = FALSE,
    race = c("Black","Black","White",
                     "White","Black","White","Black","White","White","Black",
                     "White",NA,"White","White","White",NA,NA,"Black",
                     "Black","Black","White","White","Black","White",
                     "White","Black","Asian","Black",NA,"White","White",
                     "Black",NA,"Black","White","White","Black","Black",
                     "Black","Black","Black","Black",NA,"Black","White",
                     "Black","Black","White","White","White","White",
                     "Black","White","White","Black","Other","Asian",
                     "White","White","White","White","Black","Black","Black",
                     "Asian","White","Black","White","White",NA,
                     "White","Black","Black","White","White","White","Asian",
                     "Black","White","Black","Black","White","White",
                     "Black","White","Black",NA,"Black","Black","Black",
                     "Black","Black","White","Other",NA,"White","Black",
                     "White",NA,"White"),
    hospitalized = c("No","No","No","No","No",
                                     "Yes","No","No",NA,NA,NA,NA,NA,NA,NA,NA,NA,
                                     "No","No","Yes","No","Yes",NA,"Yes",NA,NA,"No",
                                     "No",NA,NA,NA,NA,NA,"Yes","No","No",NA,"No","No",
                                     NA,"No","No",NA,"No",NA,NA,NA,"Yes",NA,NA,
                                     "No",NA,"No","No","No",NA,"No",NA,NA,"No",NA,NA,
                                     NA,"No","No",NA,NA,"Yes","No",NA,NA,NA,"No",NA,
                                     "No","No",NA,"Yes","No",NA,"No","No",NA,NA,NA,
                                     "No",NA,"No",NA,"No","Yes",NA,NA,"No","No",
                                     "No",NA,"No",NA,NA)
)

# creating table
# table with gtsummary
linelist |>
    tbl_summary(by = hospitalized, percent = "row") |>
    add_p()
#> 50 observations missing `hospitalized` have been removed. To include these observations, use `forcats::fct_na_value_to_level()` on `hospitalized` column before passing to `tbl_summary()`.
#jgmohwtrzg table { font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } #jgmohwtrzg thead, #jgmohwtrzg tbody, #jgmohwtrzg tfoot, #jgmohwtrzg tr, #jgmohwtrzg td, #jgmohwtrzg th { border-style: none; } #jgmohwtrzg p { margin: 0; padding: 0; } #jgmohwtrzg .gt_table { display: table; border-collapse: collapse; line-height: normal; margin-left: auto; margin-right: auto; color: #333333; font-size: 16px; font-weight: normal; font-style: normal; background-color: #FFFFFF; width: auto; border-top-style: solid; border-top-width: 2px; border-top-color: #A8A8A8; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #A8A8A8; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; } #jgmohwtrzg .gt_caption { padding-top: 4px; padding-bottom: 4px; } #jgmohwtrzg .gt_title { color: #333333; font-size: 125%; font-weight: initial; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; border-bottom-color: #FFFFFF; border-bottom-width: 0; } #jgmohwtrzg .gt_subtitle { color: #333333; font-size: 85%; font-weight: initial; padding-top: 3px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; border-top-color: #FFFFFF; border-top-width: 0; } #jgmohwtrzg .gt_heading { background-color: #FFFFFF; text-align: center; border-bottom-color: #FFFFFF; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; } #jgmohwtrzg .gt_bottom_border { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #jgmohwtrzg .gt_col_headings { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; } #jgmohwtrzg .gt_col_heading { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; } #jgmohwtrzg .gt_column_spanner_outer { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; padding-top: 0; padding-bottom: 0; padding-left: 4px; padding-right: 4px; } #jgmohwtrzg .gt_column_spanner_outer:first-child { padding-left: 0; } #jgmohwtrzg .gt_column_spanner_outer:last-child { padding-right: 0; } #jgmohwtrzg .gt_column_spanner { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 5px; overflow-x: hidden; display: inline-block; width: 100%; } #jgmohwtrzg .gt_spanner_row { border-bottom-style: hidden; } #jgmohwtrzg .gt_group_heading { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; text-align: left; } #jgmohwtrzg .gt_empty_group_heading { padding: 0.5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: middle; } #jgmohwtrzg .gt_from_md > :first-child { margin-top: 0; } #jgmohwtrzg .gt_from_md > :last-child { margin-bottom: 0; } #jgmohwtrzg .gt_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; } #jgmohwtrzg .gt_stub { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; } #jgmohwtrzg .gt_stub_row_group { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; vertical-align: top; } #jgmohwtrzg .gt_row_group_first td { border-top-width: 2px; } #jgmohwtrzg .gt_row_group_first th { border-top-width: 2px; } #jgmohwtrzg .gt_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #jgmohwtrzg .gt_first_summary_row { border-top-style: solid; border-top-color: #D3D3D3; } #jgmohwtrzg .gt_first_summary_row.thick { border-top-width: 2px; } #jgmohwtrzg .gt_last_summary_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #jgmohwtrzg .gt_grand_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #jgmohwtrzg .gt_first_grand_summary_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; border-top-style: double; border-top-width: 6px; border-top-color: #D3D3D3; } #jgmohwtrzg .gt_last_grand_summary_row_top { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; border-bottom-style: double; border-bottom-width: 6px; border-bottom-color: #D3D3D3; } #jgmohwtrzg .gt_striped { background-color: rgba(128, 128, 128, 0.05); } #jgmohwtrzg .gt_table_body { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #jgmohwtrzg .gt_footnotes { color: #333333; background-color: #FFFFFF; border-bottom-style: none; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; } #jgmohwtrzg .gt_footnote { margin: 0px; font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #jgmohwtrzg .gt_sourcenotes { color: #333333; background-color: #FFFFFF; border-bottom-style: none; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; } #jgmohwtrzg .gt_sourcenote { font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #jgmohwtrzg .gt_left { text-align: left; } #jgmohwtrzg .gt_center { text-align: center; } #jgmohwtrzg .gt_right { text-align: right; font-variant-numeric: tabular-nums; } #jgmohwtrzg .gt_font_normal { font-weight: normal; } #jgmohwtrzg .gt_font_bold { font-weight: bold; } #jgmohwtrzg .gt_font_italic { font-style: italic; } #jgmohwtrzg .gt_super { font-size: 65%; } #jgmohwtrzg .gt_footnote_marks { font-size: 75%; vertical-align: 0.4em; position: initial; } #jgmohwtrzg .gt_asterisk { font-size: 100%; vertical-align: 0; } #jgmohwtrzg .gt_indent_1 { text-indent: 5px; } #jgmohwtrzg .gt_indent_2 { text-indent: 10px; } #jgmohwtrzg .gt_indent_3 { text-indent: 15px; } #jgmohwtrzg .gt_indent_4 { text-indent: 20px; } #jgmohwtrzg .gt_indent_5 { text-indent: 25px; }
Characteristic No, N = 411 Yes, N = 91 p-value2
race

0.9
Asian 3 (100%) 0 (0%)
Black 19 (83%) 4 (17%)
Other 1 (100%) 0 (0%)
White 17 (77%) 5 (23%)
Unknown 1 0
1 n (%)
2 Fisher’s exact test

Created on 2024-04-13 with reprex v2.1.0

All the best,

Tim

Thank you Tim! I’m embarrassed by how long I was looking for the answer and trying things - sometimes we just need others to point out the obvious!

Adelaide

1 Like

Hi Adelaide,

No need to be embarrassed, it’s part of the learning process that we all go through!

All the best,

Tim

1 Like