class: inverse, left, middle # **gtsummary**: Creating publication-ready analytical tables .large[ **Margie Hannum** Research Biostatistician, Department of Epidemiology & Biostatistics Memorial Sloan Kettering Cancer Center **RLadies NYC** February 26, 2020 ] .medium[.footnote[Twitter: @Margaret-Hannum Github: @margarethannum ]] --- # Motivation: reproducible, presentation-ready tables <img src="images/tang_retrospective_bonemarrow_demographic.png" width=60%> .footnote[<a href="https://www.nature.com/articles/s41409-019-0441-4">Tang et. al, *Bone Marrow Transplantation* (February 2019)</a>] ??? - In our department we make table 1 and summarize regression models all the time. We all had our own siloed way to do this, some people had personal packages and some people just had scripts to do what they wanted. - As a department we wanted a unified way to make these tables, nothing out there did exactly what we wanted, with the level of customizability that we wanted. -Create reproducible summary and analytic tables that are ready to present. Create demographic tables, summarize regression models, and report analysis results. -Other packages out there to create similar tables are opinionated without being very customizable -What if a collaborator wants 2 decimal places? 3? Mean (Range) for some variables and Median (IQR) for others? Bolding significant p-values? A special kind of confidence interval formula instead of the default? - Often end up doing formatting after-the-fact which is not reproducible! Or, people have their own siloed code to make tables a particular way that they like (also not reproducible and shareable, unless they also make a package) --- # Motivation: reproducible, presentation-ready tables <img src="images/tang_retrospective_bonemarrow_mva.png" width=60%> .footnote[<a href="https://www.nature.com/articles/s41409-019-0441-4">Tang et. al, *Bone Marrow Transplantation* (February 2019)</a>] --- # {gtsummary} Overview .large[ .pull-left[ * Package will create your tabular summaries, with sensible defaults that are highly customizable - Summarize data frames/tibbles - Summarize regression models - Customize tables - Report statistics from {gtsummary} tables inline in R Markdown * Leverages {broom}, {labelled}, {tidyselect}, and {gt} packages to get the job done! * CRAN version 1.2.6 released 2020-02-13 ] ] .pull-right[ <img src="images/gtsummary_logo.png" width=100%> ] --- # Background: {gt} .large[ .pull-left[ * New package from RStudio * Package for printing highly customized tables * Goal is to unify code for creating tables in HTML, Word (via RTF), and PDF * Check it out! https://gt.rstudio.com/ ] .pull-right[ <img src="images/gt_logo.png" width=90%> ] ] ??? -Gauge room for who uses kable/gt -When Dan started developing gtsummary last year it was originally designed for our tables to be printed using knitr::kable(). But mid-last-year Rich Iannone from RStudio started developing a table construction package called {gt}, and even though it’s still in development (and not on CRAN), since it’s from the RStudio world we anticipate it will have full support and eventually be widely adopted, so he switched our package to take full advantage of the functionality {gt} offers. Still can use our package and print with kable, more on that later. --- # Background: {gt} <img src="images/gt_parts_of_a_table.png" width=70%> .pull-left[.footnote[Image source: https://gt.rstudio.com/]] ??? the gt documentation is great! all functions grouped by the part of a table they modify --- # Background: {gt} <img src="images/gt_workflow_diagram.png" width=100%> .pull-left[.footnote[Image source: https://gt.rstudio.com/]] ??? * Workflow: input data frame or tibble, create gt object (list with data and formatting elements), output gt table as HTML (previewed in the Viewer). --- # {gtsummary} Print Engines - gt or kable? .large[ .pull-left[ ## gt ♥ Highly Customizable ♥ All output includes informative footnotes, indents levels ♥ HTML Output ⚠ PDF Output still in dev ⚠ MS Word Output via RTF still in dev, requires re-sizing of tables by hand ] .pull-right[ ## kable ⚠ Less Customizable ⚠ Footnotes and spanning headers stripped from all output. No indents. ♥ HTML Output ♥ PDF Output ♥ MS Word Output ] **We built {gtsummary} as a companion to {gt} and highly recommend it!** ] ??? .footnote[All examples shown use {gt} print engine] --- # **trial** data set overview .pull-left[ ```r *head(trial, 3) ``` ``` ## # A tibble: 3 x 8 ## trt age marker stage grade response death ttdeath ## <chr> <dbl> <dbl> <fct> <fct> <int> <int> <dbl> ## 1 Drug A 23 0.16 T1 II 0 0 24 ## 2 Drug B 9 1.11 T2 I 1 0 24 ## 3 Drug A 31 0.277 T1 II 0 0 24 ``` <p align="center"><img src="images/gt_trial_info.png" width=60%> ] .medium[ .pull-right[ * Throughout this presentation examples will use the `trial` data set, included with {gtsummary}. * Data set contains baseline characteristics of 200 patients who received Drug A or Drug B. Includes outcome of tumor response to the treatment. * Variables have label attributes assigned using the `labelled` package. * For simplicity in this presentation, subset data to a few variables of interest: ```r *sm_trial <- trial %>% * select(trt, age, response, grade) ``` ] ] --- class: inverse, center, middle # tbl_summary() --- # {gtsummary} summarize data with tbl_summary() .pull-left[ .large[ **Raw data summary** ⚠ Difficult to work with ⚠ Need to convert character to factor variables ⚠ Dichotomous variables coded 0/1 are treated as numeric for descriptive statistics ⚠ Takes a lot of work to get this into a presentable table! ] ] .pull-right[ ```r head(sm_trial, 3) ``` ``` ## # A tibble: 3 x 4 ## trt age response grade ## <chr> <dbl> <int> <fct> ## 1 Drug A 23 0 II ## 2 Drug B 9 1 I ## 3 Drug A 31 0 II ``` ```r *summary(sm_trial) ``` ``` ## trt age response grade ## Length:200 Min. : 6.00 Min. :0.0000 I :68 ## Class :character 1st Qu.:38.00 1st Qu.:0.0000 II :68 ## Mode :character Median :47.00 Median :0.0000 III:64 ## Mean :47.24 Mean :0.3161 ## 3rd Qu.:57.00 3rd Qu.:1.0000 ## Max. :83.00 Max. :1.0000 ## NA's :11 NA's :7 ``` ] ??? - it's not pretty - most often I want the odds ratios from a logistic regression, not the betas - format from every type of model is different and difficult to work with --- # {gtsummary} summarize data with tbl_summary() .pull-left[ .large[**Example: Summarizing clinical trial data in one line of code with `tbl_summary()`**] ```r *tbl_summary(sm_trial) ``` Notice some nice default behaviors: ♥ Detects variable types of input data and calculates descriptive statistics ♥ Variables coded as 0/1, TRUE/FALSE, and Yes/No are presented dichotomously. ♥ Recognizes `NA` values as "missing" and lists them as unknown ♥ Label attributes automatically printed ♥ Variable levels indented and footnotes added with _({gt})_ ] .pull-right[ <p align="center"><img src="images/tbl_summary_1a.png" width=62%></p> ] ??? - This is an abbreviated version of the example data used in the package help files/documentation. - Returns a nicely formatted table with sensible rounding and formatting --- # {gtsummary} summarize data with tbl_summary() .pull-left[ ### **Start customizing using arguments and pipe operator `%>%` to string additional functions together** ```r tbl_summary_1 <- sm_trial %>% * tbl_summary(by = trt) %>% * add_p() %>% * add_overall() ``` ] .pull-right[ <br> <br> <p align="center"><img src="images/tbl_summary_2.png" width=100%></p> ] ??? .medium[ - `by =` argument to split table by a categorical variable - `add_p()` - default tests are the Wilcoxon rank-sum test for continuous variables, chi-square test of independence/ Fisher's exact test for categorical (Fisher's for low expected counts). - `add_overall()` - to add back in an overall summary of the data (not split by the `by` argument) ] - Go slow here - summarizing a data set is the MOST important analysis - summarize data first! you will often catch mistakes. Data is complicated, and understanding it up front is important. - Communicating a summary of the data ALONG with analytic results in necessary (others may catch mistakes you're not aware of) - {gtsummary} is for presenting results, other great packages are available for summarizing data for your self (e.g. skimr) - just one line of code - all functions beginning with `tbl_*` create a new tables - this is how I used the package 95% percent of the time...so easy - three types of data shown here (explain them) --- # {gtsummary} summarize data with tbl_summary() .pull-left[ .large[ **Customize further using formula syntax and tidy selectors** ] ```r tbl_summary_3 <- sm_trial %>% tbl_summary( by = trt, statistic = list( all_continuous() ~ "{mean} ({sd})", all_categorical() ~ "{n} / {N} ({p}%)"), label = age ~ "Patient Age") %>% add_p(test = all_continuous() ~ "t.test") ``` .medium[ - `statistic` - Report mean and standard deviation for continuous (default is median) - `label` - Specify label for age - `type` - Specify variable types - `digits` - Specify number of decimals to round to - `test` [`add_p()`] - Report t-test p-values all continuous (default is Wilcoxan Rank Sum) ] ] .pull-right[ <p align="center"><img src="images/tbl_summary_3.png" width=100%></p> ] ??? - defaults are great, let's change the default behavior - statistics can be changed to anything...literally any R function (e.g. variance) - discuss the formula notation - it's like `case_when()`, condition/variable on LHS and result on RHS - one formula doesn't need to be in a list, but more than one must be listed - the vignette has more examples --- # {gtsummary} summarize data with tbl_summary() <img src = "images/tbl_summary_demo.gif" alt = "animated" width = "100%"> ??? .large[ **Additional `tbl_summary` Features** ] .medium[ - Use **{tidyselect}** functions to select variables for customization - Use **custom functions** for calculating p-values and reporting any statistic for continuous variables (including user-written functions) - **Formatting** functions to bold and italicize labels and levels - **Missing data** options - **Sort variables** by significance (`sort_p()`); sort categorical variables by frequency - Calculate **cell percents and row percents** (default is column-wide) - Only report p-values for select variables (`add_p(include = ...)`); report q-values (like false discovery rate) - **Rounding options** and ability to **set global options** for rounding p-values ] .large[ Review [`tbl_summary()` vignette](http://www.danieldsjoberg.com/gtsummary/articles/tbl_summary.html) for more details and examples! ] ??? - There is more I'm not covering here. Many options in the vignette. --- class: inverse, center, middle # tbl_regression() --- # {gtsummary} summarize models with tbl_regression() .pull-left[ .large[ **Raw model output** ⚠ Difficult to work with ⚠ Format varies from different types of models ⚠ Need to exponentiate betas to get odds ratios from a logistic regression etc. ] ] .pull-right[ ```r m1 <- glm(response ~ trt + grade + age, data = trial, family = binomial) *m1 ``` ``` ## ## Call: glm(formula = response ~ trt + grade + age, family = binomial, ## data = trial) ## ## Coefficients: ## (Intercept) trtDrug B gradeII gradeIII age ## -1.694687 0.124330 -0.160518 0.007672 0.019000 ## ## Degrees of Freedom: 182 Total (i.e. Null); 178 Residual ## (17 observations deleted due to missingness) ## Null Deviance: 228.6 ## Residual Deviance: 225.3 AIC: 235.3 ``` ] ??? - it's not pretty - most often I want the odds ratios from a logistic regression, not the betas - format from every type of model is different and difficult to work with --- # {gtsummary} summarize models with tbl_regression() .pull-left[ .large[ **`broom::tidy()` output** ♥ Using `broom::tidy()` a step in the right direction! ♥ All models returned with consistent table format (term, estimates, standard errors...) ♥ Option to exponentiate ⚠ Does not include reference groups ⚠ Needs additional modification before it can be presented ] ] .pull-right[ ```r *broom::tidy(m1, conf.int = TRUE, exponentiate = TRUE) ``` ``` ## # A tibble: 5 x 7 ## term estimate std.error statistic p.value conf.low conf.high ## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 (Intercept) 0.184 0.630 -2.69 0.00715 0.0509 0.610 ## 2 trtDrug B 1.13 0.321 0.387 0.699 0.603 2.13 ## 3 gradeII 0.852 0.395 -0.406 0.685 0.389 1.85 ## 4 gradeIII 1.01 0.385 0.0199 0.984 0.472 2.15 ## 5 age 1.02 0.0114 1.67 0.0952 0.997 1.04 ``` ] ??? - MUCH MUCH better! - all models returned with consistent format - but does not include reference groups - still needs additional modification before it can be presented --- # {gtsummary} summarize models with tbl_regression() .pull-left[ ### **{gtsummary} Output** .large[ `tbl_regression()` accepts regression model object as input. Uses {broom} in the background, outputs table with nice defaults: ♥ Reference groups added to the table ♥ Sensible default number rounding and formatting ♥ Label attributes printed ♥ Certain model types detected ♥ Estimate header and footnote included ] ] .pull-right[ ```r *tbl_reg_1 <- tbl_regression(m1, exponentiate = TRUE) ``` <p align="center"><img src="images/tbl_regression_1.png" width=90%></p> ] ??? - This table is ready for publication in a single line of code! - That is something no other package I know of can do - The back end for the function is {broom} and {gt}, meaning that there is broad support for most regression model types, and the resulting tables are gorgeous and customizable. - Common regression models, such as logistic regression and Cox regression, are automatically identified and the tables are created with appropriate headers. - build the regression model on your own....we are not in the business of model estimation or checking --- # {gtsummary} summarize models with tbl_regression() .pull-left[ .large[ **Format variables with `tbl_regression` arguments** - `show_single_row` - If variable is dichotomous (e.g. Yes/No), you can choose to print regression coefficient on a single row - `label` - Specify labels ]] .pull-right[ ```r tbl_reg_1a <- tbl_regression( m1, * show_single_row = trt, * label = trt ~ "Treatment B vs A", exponentiate = TRUE) ``` <p align="center"><img src="images/tbl_regression_1a.png" width=90%></p> ] --- # {gtsummary} summarize models with tbl_regression() .pull-left[ .large[ **Format values with `tbl_regression` arguments** - `exponentiate` - default is `FALSE` - `conf.level` - Specify between 0-1. Default 0.95. - `estimate_fun`, `pvalue_fun` - Specify functions to round and format values - `tidy_fun` - Specify a specific or custom tidier (e.g. use `summary(object, type = "tidy")` from the {mice} package) ] ] .pull-right[ ```r tbl_reg_1b <- tbl_regression( m1, * conf.level = 0.9, * pvalue_fun = function(x) style_pvalue(x, digits = 2), exponentiate = TRUE) ``` <p align="center"><img src="images/tbl_regression_1b.png" width=90%></p> ] --- # {gtsummary} summarize models with tbl_regression() <img src = "images/tbl_regression_demo.gif" alt = "animated" width = "100%"> --- # {gtsummary} summarize models with tbl_regression() .pull-left[ ```r library(survival) tbl_reg_3 <- coxph(Surv(ttdeath, death) ~ trt + grade + age, data = trial) %>% tbl_regression(exponentiate = TRUE) tbl_reg_4 <- * tbl_merge( * tbls = list(tbl_reg_1, tbl_reg_3), * tab_spanner = c("**Tumor Response**", "**Time to Death**") * ) ``` .large[ - Build Cox regression model with same predictors as previous model. - Merge the two regression models with the same predictors and present results side-by-side. ] ] .pull-right[ <p align="center"><img src="images/tbl_regression_4.png" width=100%></p> ] ??? - side-by-side regression results is common in cancer research (e.g. time to recurrence, then time to death) - stacking two or more models is also possible - easy to create custom tables that are formatted beautifully --- # {gtsummary} summarize data with tbl_uvregression() .pull-left[ ```r library(survival) tbl_uvregression_1 <- * tbl_uvregression( * sm_trial, * method = glm, * y = response, * method.args = list(family = binomial), * exponentiate = TRUE * ) ``` .large[ - Table of univariate regression models - Specify the outcome, and the remaining variables in data frame serve as predictors - Customize table similar to `tbl_regression()` ] ] .pull-right[ <p align="center"><img src="images/tbl_uvregression_1.png" width=100%></p> ] ??? - Tables of univariate results can be good for exploratory analysis - Code is similar to {ggplot2} `geom_smooth()` and `stat_smooth()` - also great with time-to-event endpoint when you cannot do a `tbl_summary()` to get bivariate p-values --- class: inverse, center, middle # inline_text() --- # {gtsummary} reporting results with inline_text() .large[ - Tables are important, but we often need to report results in-line in a report. - Any statistic reported in a {gtsummary} table can be extracted and reported in-line in a R Markdown document with the `inline_text()` function. ```r inline_text(tbl_reg_1, variable = trt, level = "Drug B") ``` ``` 1.13 (95% CI 0.60, 2.13; p=0.7) ``` - The pattern of what is reported can be modified with the `pattern = ` argument. - Default is `pattern = "{estimate} ({conf.level*100}% CI {conf.low}, {conf.high}; {p.value})"`. ] ??? - discuss importance of reproducible results - data is constantly updating - this functionality assures you won't miss updating a reported estimate in a document - for me, this is one the most powerful parts of the {gtsummary} package - something I've never seen in another package --- class: inverse, center, middle # Customization --- # {gtsummary} Formulas .large[ **Formulas** - Most arguments to `tbl_summary()` and `tbl_regression()` require formula syntax, and provide many more options to easily select the table variables you want to modify. .center[ <br> **select variables ~ specify what you want to do** ] ```r tbl_summary( trial, by = trt, statistic = age ~ "{mean} ({sd})" ) ``` ] --- # {gtsummary} Formulas .large[ **Formulas** .center[ **select variables ~ specifiy what you want to do** ] .pull-left[**select variables** * use quoted or unquoted variables, minus sign to negate (e.g. `age` or `"age"` to select, `-age` to deselect) * use any {tidyselect} functions, e.g. `contains("stage") ~ ...`, including type selectors (available in next version of {dplyr}) ] .pull-right[**specify what you want to do** (depends on the argument) * change the statistic you report using [{glue}](https://github.com/tidyverse/glue) syntax where whatever in the curly brackets gets evaluated and passed directly into the string. e.g `statistic = ... ~ "{mean} ({sd})"` * pass a string to change labels ] ] --- # {gtsummary} tbl_summary() Formulas .pull-left[ ```r tbl_summary_4 <- sm_trial %>% tbl_summary( by = trt, * type = response ~ "categorical", * statistic = all_continuous() ~ "{mean} ({sd})", * digits = age ~ c(0, 1) ) %>% add_p(test = list(all_continuous() ~ "t.test", response ~ "fisher.test")) ``` ] .pull-right[ <p align="center"><img src="images/tbl_summary_4.png" width=100%></p> ] ??? - Report levels for the response variable. - Report mean instead of median (using glue) - Modify the default rounding for age. - Specify t-test for all continuous variables and Fisher's test for response variable. - further discuss formula notation - just like {gt} can use both select helpers OR characters vector of names - discuss digits and how it's used - discuss `stat_label = `, and mention the footnote was omitted --- # {gtsummary} advanced customization: using {gt} .large[ - It's natural a {gtsummary} package user would want to customize the aesthetics of the table with one or more of the many {gt} functions available. - Every function in {gt} is available to use with a {gtsummary} object. 1. Create a {gtsummary} table. 1. Convert the table to a {gt} object with the `as_gt()` function. 1. Continue formatting as a {gt} table with any {gt} function. https://gt.rstudio.com/ ] ??? Discuss `as_gt()` and how to use **Advanced Customization Using {gt}** - tab_header() - add a table title - tab_spanner() - add headers that span columns - tab_options() - change table padding and font size - tab_footnote() - add additional footnotes to table And many more! https://gt.rstudio.com/ --- # {gtsummary} advanced customization: using {gt} .pull-left[ ```r tbl_summary_5 <- sm_trial %>% tbl_summary(by = trt) %>% # convert from gtsummary object to gt object * as_gt() %>% # modify with gt functions * tab_header("Table 1: Baseline Characteristics") %>% * tab_spanner( * label = "Randomization Group", * columns = starts_with("stat_") * ) %>% * tab_options( * table.font.size = "small", * data_row.padding = gt::px(1)) ``` .footnote[More on this in the `tbl_summary()` <a href="http://www.danieldsjoberg.com/gtsummary/articles/tbl_summary.html#advanced-customization">vignette</a>] ] .pull-right[ <p align="center"><img src="images/tbl_summary_5.png" width=90%></p> ] --- # {gtsummary} advanced customization: global options .large[Have a consistent rounding preference or gt styling you want to apply all the time? Set some **global options** at the top of your RMD or in your .Renviron! ] ## Format {gt} tables For example, to make all your tables have small font size and minimal row padding (for more "compact" tables): `options(gtsummary.as_gt.addl_cmds = "gt::tab_options(table.font.size = 'small', data_row.padding = gt::px(1))")` ## Style Estimates and p-values For example, always round p-values to 2 digits: `options(gtsummary.pvalue_fun = function(x) style_pvalue(x, digits = 2))` Check out the [Global Options](http://www.danieldsjoberg.com/gtsummary/articles/global_options.html) vignette for more examples. --- class: inverse, center, middle # Conclusion --- # {gtsummary} - Package Website Website contains [well-documented functions](http://www.danieldsjoberg.com/gtsummary/reference/index.html), detailed [tutorials](http://www.danieldsjoberg.com/gtsummary/articles/), and [examples](http://www.danieldsjoberg.com/gtsummary/articles/gallery.html)! [<img src="images/gtsummary_website.png" width=80%>](http://www.danieldsjoberg.com/gtsummary/) ??? For live presentation include scrolling iframe <iframe src="http://www.danieldsjoberg.com/gtsummary/" scrolling= "yes" width = "950%" height = 90%"></iframe> --- # {gtsummary} Installation .pull-left[ .medium[ * Install {gtsummary} from **CRAN** with the following code: ```r install.packages("gtsummary") ``` * Also recommended to install the development version of {gt} from **GitHub**. ```r install.packages("remotes") remotes::install_github("rstudio/gt") ``` ] ] .pull-right[ <img src="images/cranlogs.png" width=100%> ] ??? - visit the website and give quick tour r include_url("http://www.danieldsjoberg.com/gtsummary/", height = "400px") * {gtsummary} package website: http://www.danieldsjoberg.com/gtsummary/ * <img src = "images/open-book-white.png" width="2.4%" height="2.4%"> Installation instructions * <img src = "images/open-book-white.png" width="2.4%" height="2.4%"> Thorough documentation on every function * <img src = "images/open-book-white.png" width="2.4%" height="2.4%"> Detailed tutorials --- class: center # Resources .large[ <img src = "images/open-book-white.png" width="2.4%" height="2.4%"> {gtsummary} documentation/website/tutorials <a href="http://www.danieldsjoberg.com/gtsummary/">danieldsjoberg.com/gtsummary/</a> <img src = "images/github_icon.png" width="2.4%" height="2.4%"> {gtsummary} package <a href="https://github.com/ddsjoberg/gtsummary">github.com/ddsjoberg/gtsummary</a> <img src = "images/slide_show_icon.png" width="2.4%" height="2.4%"> slides at <a href="https://github.com/margarethannum/gtsummary-presentation-rladies">github.com/margarethannum/gtsummary-presentation-rladies</a> <img src = "images/github_icon.png" width="2.4%" height="2.4%"> source code for slides at <a href="https://github.com/margarethannum/gtsummary-presentation-rladies">github.com/margarethannum/gtsummary-presentation-rladies</a> <img src = "images/github_icon.png" width="2.4%" height="2.4%"> {gt} package <a href="https://github.com/rstudio/gt">github.com/rstudio/gt</a> ] Slides created using [xaringan](https://github.com/yihui/xaringan) R package [R-Ladies xaringan theme](https://alison.rbind.io/post/2017-12-18-r-ladies-presentation-ninja/) from Alison Hill (@apreshill) ??? Go star {gtsummary} on GitHub...we're already to 50+! --- # Thank you! <img src="images/sjoberg.jpg" width=15%> <img src="images/hannum.jpg" width=15%> <img src="images/whiting.jpg" width=15%> ♥ {gtsummary} Authors: [**Daniel Sjoberg**](http://www.danieldsjoberg.com/) (Maintainer), Margie Hannum, Karissa Whiting <img src="images/curry.jpg" width=15%> <img src="images/drill.jpg" width=15%> <img src="images/flynn.jpg" width=15%> <img src="images/lobaugh.jpg" width=15%> ♥ {gtsummary} Contributors: Emily Zabor (not pictured), Michael Curry, Esther Drill, Jessica Flynn, Stephanie Lobaugh ♥ Huge thank you to [Rich Iannone](https://github.com/rich-iannone), the Author/Maintainer of the {gt} package. ♥ And thank you to everyone in Epidemiology and Biostatistics at Memorial Sloan Kettering for testing the package and providing valuable feedback! --- # Appendix: {gtsummary} Advanced .large[ {gtsummary} output is a list that prints as a {gt} table. ] ```r names(tbl_summary_1) ``` ``` ## [1] "gt_calls" "kable_calls" "table_body" "table_header" "meta_data" ## [6] "inputs" "N" "call_list" "by" "df_by" ## [11] "fmt_fun" ``` ```r pluck(tbl_summary_1, "table_body") %>% head() ``` ``` ## # A tibble: 6 x 7 ## variable row_type label stat_0 stat_1 stat_2 p.value ## <chr> <chr> <chr> <chr> <chr> <chr> <dbl> ## 1 age label Age, yrs 47 (38, 57) 46 (37, 59) 48 (39, 56) 0.717 ## 2 age missing Unknown 11 7 4 NA ## 3 response label Tumor Response 61 (32%) 28 (29%) 33 (34%) 0.637 ## 4 response missing Unknown 7 3 4 NA ## 5 grade label Grade <NA> <NA> <NA> 0.871 ## 6 grade level I 68 (34%) 35 (36%) 33 (32%) NA ``` --- # Appendix: {gtsummary} Advanced ```r pluck(tbl_summary_1, "gt_calls") %>% head(n = 4) ``` ``` ## $gt ## gt::gt(data = x$table_body) ## ## $cols_align ## gt::cols_align(align = 'center') %>% gt::cols_align(align = 'left', columns = gt::vars(label)) ## ## $fmt_missing ## gt::fmt_missing(columns = gt::everything(), missing_text = '') ## ## $tab_style_text_indent ## gt::tab_style(style = gt::cell_text(indent = gt::px(10), align = 'left'),locations = gt::cells_body(columns = gt::vars(label),rows = row_type != 'label')) ``` ??? If there is time, review the structure of a {gtsummary} object Essentially, what is going on is that the {gt} calls on the right are called on the table on the left whenever the object is printed. Understanding this structure will help you modify if you need. If there is a {gt} call that formats in a way you don't like, convert your object with `as_gt()` and use the `omit =` argument to leave out the gt call you don't like. You can replace it with whatever you choose. <!-- --- --> <!-- # Customization --> <!-- ### Additional customization --> <!-- - **Table footnotes** (you can optionally exclude these using `as_gt(exclude = "footnote_stat_label")` ) -->