--- title: "Exporting in-text tables" author: "Laure Cougnaud" date: "`r format(Sys.Date(), '%B %d, %Y')`" output: rmarkdown::html_document: toc: true toc_float: true toc_depth: 5 number_sections: true vignette: > %\VignetteIndexEntry{Exporting in-text tables} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r options, echo = FALSE} library(knitr) opts_chunk$set( echo = TRUE, results = 'markup', warning = FALSE, # stop document execution if error (not the default) error = FALSE, message = FALSE, cache = FALSE, fig.width = 8, fig.height = 7, fig.path = "./figures_vignette/", fig.align = 'center') options(width = 170) # instead of warn = 0 by default # include warnings when they occur in the document options(warn = 1) ``` In this vignette we focus on exporting tables created with the `inTextSummaryTable` package. In particular, we will cover * how to **export tables for specific formats as Word/Powerpoint/Html/R object** * how to **make your custom aesthetics** * how to **save tables into separated documents** * how to set specific **text formatting** (e.g. superscripts/subscripts) * how to **create multiple tables** at once based on a variable of interest * how to **create a listing** * how to **filter and split** exported tables We assume you are already familiar on how to create tables, otherwise we advise to first check out the dedicated vignette on how to make tables, available [here](../doc/inTextSummaryTable-createTables.html)) or accessible with the commands below. ```{r getVignette, eval = FALSE} vignette("inTextSummaryTable-createTables", "inTextSummaryTable") ``` We will first create example data sets to show how the exporting functionalities work. The data sets used are available in the `clinUtils` package. # Load packages and data ```{r loadPackages} library(inTextSummaryTable) library(clinUtils) library(pander) library(tools) # toTitleCase ``` ```{r loadData} # load example data data(dataADaMCDISCP01) dataAll <- dataADaMCDISCP01 labelVars <- attr(dataAll, "labelVars") ``` This section creates example data sets used below. ```{r createExampleData} dataAE <- subset(dataAll$ADAE, SAFFL == "Y" & TRTEMFL == "Y") dataAEInterest <- subset(dataAE, AESOC %in% c( "INFECTIONS AND INFESTATIONS", "GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS" ) ) # ensure that order of elements is the one specified in # the corresponding numeric variable dataAEInterest$TRTA <- reorder(dataAEInterest$TRTA, dataAEInterest$TRTAN) dataAEInterest$AESEV <- factor(dataAEInterest$AESEV, levels = c("MILD", "MODERATE")) dataTotalAE <- subset(dataAll$ADSL, TRT01A != "Placebo") # should contain columns specified in 'colVar' dataTotalAE$TRTA <- dataTotalAE$TRT01A ``` # Output formats {#outputFormat} The default exported format is **`flextable`**, which is **suitable for Word/PowerPoint** documents. Alternatively, a table can be exported as **datatable interactive table for html reports**, or as a **data frame in R**. The `outputType` parameter specifies the output format of the summary statistics table. ## Static table for Word/Powerpoint ### General format To get the table in a format **suitable for _Word_/_Powerpoint_** documents, the **outputType** parameter should be set to **'flextable'** (the default). ```{r outputType-flextable} summaryTableFt <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term", footer = c( "N = number of subjects in the data; n = number of subjects with observation", paste("Denominator for percentage calculations is the total number of subjects", "per treatment group in the safety population" ) ), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), outputType = "flextable" ) class(summaryTableFt) summaryTableFt ``` The object `summaryTableFt` is a **flextable object**. This is available through the [`flextable` R package](https://cran.r-project.org/package=flextable) (see `?flextable`). When printed into the R console, a flextable object is displayed in the internet browser. However, the `summaryTableFt` object can be inserted within a `rmarkdown` document by printing it in the specified document chunk, providing that the following system requirement are satisfied: * `flextable` version >= 0.4.7 * for a `docx` document (`output_type`): [pandoc](https://pandoc.org/installing.html) version >= 2.0 (see [`rmarkdown` documentation for Word](https://bookdown.org/yihui/rmarkdown/word-document.html)) * for `pptx` document: [pandoc](https://pandoc.org/installing.html) version >= 2.4 (see [`rmarkdown` documentation for Powerpoint](https://bookdown.org/yihui/rmarkdown/powerpoint-presentation.html)) A `rmarkdown` chunk can contains only one `flextable` object. To include a list of `flextable` in a `rmarkdown` document, the function `knitPrintListObjects` of the `clinUtils` package can be used. ### Style: report or presentation The table can be styled via the **`style`** parameter. This parameter affects the fontsize, font family, color of the text and background, dimensions of the table. Other specific parameters can be used to export the table as landscape (`landscape`), specify custom margins (`margin`), row indent (`rowVarPadBase`), font size and family (`fontsize` and `font`). By default, the table is styled for a **CSR report**. ```{r style-report} getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term", footer = c( "N = number of subjects with data; n = number of subjects with this observation", paste("Denominator for percentage calculations is the total number of subjects", "per treatment group in the safety population" ) ), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)") ) ``` The table can also be styled for a **presentation**. Note that the default dimension of a table printed in a Powerpoint document follows the standard 4:3 size (7.5 x 10 inches). If you wish to set the dimension for a Widescreen document, it is possible to set a global option for this. Please, check out the section "Custom aesthetics". ```{r style-presentation} getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term", footer = c( "N = number of subjects with data; n = number of subjects with this observation", paste("Denominator for percentage calculations is the total number of subjects", "per treatment group in the safety population" ) ), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), style = "presentation" ) ``` ## Interactive table for html The summary statistics table is exported as an interactive table by setting the parameter: **`outputType`** to **'DT'**. A `datatable` object, created with the [DT](https://CRAN.R-project.org/package=DT) R package is created. Interactivity includes the functionalities to: * filter the table * order columns * export the table (or a subset of this) * include custom interactivity e.g. the possibility to have expandable rows/columns, see patient profiles inclusion in the medical monitoring report Such table can be included in a _html_ `rmarkdown` report. The interactive table can be further customized by setting options specific to this output (see `clinUtils::getClinDT`). Please note that the interactive table is only created if Pandoc is available. ```{r outputType-DT, eval = rmarkdown::pandoc_available()} getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n"), dataTotal = dataTotalAE, labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term", footer = c( "N = number of subjects in the data; n = number of subjects with observation", paste("Denominator for percentage calculations is the total number of subjects", "per treatment group in the safety population" ) ), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), outputType = "DT", ## DT-specific options buttons = c() # remove all export buttons ) ``` ## Data frames for R The summary statistics table can be stored as a R object. Note that the exported `data.frame`s contains _attributes_ that indicate which statistics are used, which row variables/column variables are to be shown and how etc. ### Final table The summary statistics data displayed in the final table object can be exported as an R `data.frame`. In this case the **`outputType`** should be set to **`data.frame`**. ```{r outputType-dataframe} summaryTable <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term", footer = c( "N = number of subjects in the data; n = number of subjects with observation", paste("Denominator for percentage calculations is the total number of subjects", "per treatment group in the safety population" ) ), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), style = "presentation", outputType = "data.frame" ) pander(summaryTable, split.table = Inf) ``` ### Full table in long format The table with all the computed summary statistics, the specified set of statistics of interest and all row/column variables stored in a long format is available by using the **`outputType`** parameter to **'data.frame-base'**. This format is typically of interest to run quality checks of the computed summary statistics in comparison with e.g. Table/Listing/Figures, or to compare the statistics computed between batches of the data. ```{r outputType-dataframe-base} summaryTableAll <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term", footer = c( "N = number of subjects in the data; n = number of subjects with observation", paste("Denominator for percentage calculations is the total number of subjects", "per treatment group in the safety population" ) ), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), style = "presentation", outputType = "data.frame-base" ) pander(summaryTableAll, split.table = Inf) ``` ## Differences between the format Most of the functionalities and default parameters are the same between the `flextable` and `DT` format, at the exception of: * for the `DT` format, by default the variables and statistics are included in different columns: `rowVarInSepCol` is `rowVar` and `statsLayout` is set to 'col' * for the `flextable` format, by default the row variables and statistics are nested: `rowVarInSepCol` is set to: 'NULL' and the `statsLayout` is set to 'row' # Custom aesthetics The `inTextSummaryTable` contains functionalities to set the aesthetics of the tables. Those **aesthetics are set through global options**. This has the main advantage that if you wish to change the default color scheme, it is possible to set your preferences only once at the beginning of the R script or Rmd document. When loading the package, the global options for palettes get automatically defined into the R session. If you wish to change the default colors, you can apply your preferences by changing the global options. The `inTextSummaryTable` package has the following options for table aesthetics: * **`inTextSummaryTable.colors.table.presentation`** * **`inTextSummaryTable.pageDim.presentation`** The first option sets custom colors for a table in a presentation mode, the last one defines the page dimension of a Powerpoint when exporting a table. Note that **the options have to be defined after loading the package**. This because when loading the package, the default global options for palettes will overwrite the custom palettes. ## Colors As you may have already noticed if you have read the vignette till here, by default, the reporting format makes tables with a black text on a white background. On the contrary, the presentation format creates tables with a blue header and white text, whereas the body is grey with black text. If you wish to define a personal color scheme, a named vector can be created and passed to the `options`, as shown below. Colors can be provided in hexadecimals or in `rgb` specification. In the example we use hexadecimals just for convenience. Note that the `bodyBackground1` and `bodyBackground2` allow to have alternating row colors. ```{r aesthetics-changeReportingOptions} # create named vector customColorTable <- c( # black text in the header 'header' = "#000000", # green background in the header 'headerBackground' = "#74D055FF", # black text in the body 'body' = "#000000", # yellow background for all rows 'bodyBackground1' = "#FDE725FF", 'bodyBackground2' = "#FDE725FF", # black footer 'footer' = "#000000", # white footer background 'footerBackground' = "#FFFFFF", # black line for footer 'line' = "#000000" ) # set options options(inTextSummaryTable.colors.table.presentation = customColorTable) # create the table on a dummy data set getSummaryStatisticsTable( data = data.frame(USUBJID = c(1, 2)), style = "presentation" ) ``` ## Dimension of the page By default, the `inTextSummaryTable` ships with a default PowerPoint in the standard 4:3 size (7.5 x 10 inches). However, it is often common to create a PowerPoint template with **Widescreen size** of 16:9 which consistts of 7.50 x 13.32 inches. Therefore, it possible to accomodate such widescreen size by providing ```{r aesthetics-changeDimPageOptions} # set custom dimension of page for presentation # in this example, the dimension is the widescreen size pageDimCustom <- c(7.5, 13.32) options(inTextSummaryTable.pageDim.presentation = pageDimCustom) getOption("inTextSummaryTable.pageDim.presentation") ``` ## Further info on asthetics For further information about the aesthetics settings of the package, a dedicated vignette is available at ```{r getVignetteAesthetics, eval = FALSE} vignette("inTextSummaryTable-aesthetics", "inTextSummaryTable") ``` ## Set back default palettes There is always to possibility to switch back to the default palettes of the package: ```{r aesthetics-backDefaultPalettes} options(inTextSummaryTable.colors.table.presentation = tableColorsPresentation) ``` # Export table to a separated file The table can be exported to a different file by specifying a file name in the **`file`** parameter. Possible formats are: * **txt**: the raw base table is exported to a text file. This format can be used e.g. to run quality checks with reference Table/Listings/Figures. * **docx**: the formatted table is exported to a Word document. Such format is typically of interest to share the tables to medical writers, to be imported directly into a Statistical Analysis Plan (if `style = 'report'`). * **html**: an interactive table is exported to the specified html file ```{r file, eval = FALSE} # export table to a Word document summaryTableFt <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, file = file.path("tables_CSR", "summaryTable-AEs.docx") ) # export interactive table to a html document summaryTableFt <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, file = file.path("tables_CSR", "summaryTable-AEs.html") ) # export table in raw format to a text file summaryTableFt <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, file = file.path("tables_CSR", "summaryTable-AEs.txt") ) # export to multiple formats at once summaryTableFt <- getSummaryStatisticsTable( data = dataAEInterest, rowVar = c("AESOC", "AEDECOD"), colVar = "TRTA", stats = getStats("n (%)"), dataTotal = dataTotalAE, labelVars = labelVars, file = file.path("tables_CSR", c("summaryTable.txt", "summaryTable.docx", "summaryTable.html") ) ) ``` # Export table of pre-computed summary statistics To create a table in a similar format as the in-text tables created by the package, from pre-computed summary statistics (e.g. from CRO or from a different software as _SAS_) the function **`exportSummaryStatisticsTable`** can be used. This function takes as input a **`data.frame` in long format** that should contain the summary statistics. The long format should be such that the * statistic value (`statsVar`) * statistic type (if multiple), grouping row and columns (`rowVar` and `colVar`) are both stored as different columns in the `data.frame`. If the column headers should contain total number of subjects, the corresponding counts should be stored in records with the column `isTotal` set to TRUE. ```{r export} dataDIABP <- subset( dataAll$ADVS, SAFFL == "Y" & ANL01FL == "Y" & PARAMCD == "DIABP" & AVISIT %in% c("Baseline", "Week 6") & ATPT == "AFTER LYING DOWN FOR 5 MINUTES" ) # create example of data.frame containing statistics of interest statsEff <- sapply(c("AVAL", "CHG"), function(var) { getStats( type = c("n", "mean (se)", "median (range)"), x = dataDIABP[[var]] ) }, simplify = FALSE) summaryTable <- computeSummaryStatisticsTable( data = dataDIABP, colVar = c("TRTP", "AVISIT"), var = c("AVAL", "CHG"), varGeneralLab = "", stats = statsEff, labelVars = labelVars ) # format df with statistics to in-text table format export( summaryTable = summaryTable, statsVar = c("n", "Mean (SE)", "Median (range)"), rowVar = "variable", rowVarLab = "Statistic", colVar = c("TRTP", "AVISIT"), colHeaderTotalInclude = TRUE, labelVars = simpleCap(tolower(labelVars[c("AVAL", "CHG")])), title = toTitleCase( "Table: Diastolic Blood Pressure (mmHg) statistics" ) ) ``` # Text formatting Specific formatted text can be specified for the `flextable` output type only. ## Superscript/subscript Superscript and subscript are specified via the special text pattern as `a^{b}` and `a_{b}` respectively. To use a superscript or a subscript in the table, the text should be formatted as: `text^{superscript}` or `text_{subscript}`. This is for example useful to reference additional informations in a specific footnote or specify custom variable labels. ```{r summaryTable-PP-rowVarWithLabel-rowVarLab-superscript} dataSL <- subset(dataAll$ADSL, SAFFL == "Y") varsSL <- c("AGE", "WEIGHTBL", "BMIBL") labelVars[varsSL] <- c( "Age", "Weight_{t}", "BMI (kg/m^{2})" ) getSummaryStatisticsTable( data = dataSL, var = varsSL, stats = getStats("n (%"), labelVars = labelVars, fontsize = 16, title = toTitleCase("Demographic data (Safety Analysis Set)") ) ``` ## Bold and greek letters Specific cells of the table can be highlight in bold by using the syntax: `bold{}` in the `stats` or `statsExtra` function. This highlighting may depend: * on a additional variable via the `statsExtra` parameter: for example: `statsExtra <- function(data) with(data, ifelse(ANRIND != "N", paste0("bold{", toString(AVAL), "}"), toString(AVAL))` * on the values of computed statistics, via the `stats` parameter Greek letters are included as they are in the table. ```{r summaryTable-PP-rowVarWithLabel-rowVarLab-bold} getSummaryStatisticsTable( data = dataSL, var = varsSL, stats = list( expression(paste0( ifelse(statMean > statMedian, paste0("bold{", roundHalfUpTextFormat(statMean, 1), "}"), roundHalfUpTextFormat(statMean, 1) ), "\n(", roundHalfUpTextFormat(statSE, 2),")") ) ), labelVars = labelVars, fontsize = 12, title = toTitleCase("Demographic data (Safety Analysis Set)") ) ``` # Create multiple tables by a variable Multiple tables can be created for each element of a variable in the dataset by specifying the name of the variable in the argument **`byVar`**. A list of final summary table can be included into a `rmarkdown` document with the `knitPrintListObjects`. Please note that the following chunk option should be used: **`results = 'asis'`**. For example, AE tables are created for each treatment: ```{r byVar, results = "asis"} summaryTableList <- getSummaryStatisticsTable( data = subset(dataAE, TRTEMFL == "Y"), rowVar = c("AESOC", "AEDECOD"), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), colVar = "TRTA", byVar = "TRTA", stats = getStats("n (%)"), labelVars = labelVars, title = "Table: Treatment-Emergent adverse events" ) # print the list of tables in the rmarkdown document clinUtils::knitPrintListObjects(summaryTableList) ``` # Listing Listing in a similar format as the in-text summary table is created via the `getListing` function. ```{r getListing} varsListing <- c( "USUBJID", "AEBODSYS", "AEDECOD", "TRTA", "AESEV", "AESER", "ASTDY", "AENDY" ) dataListing <- subset(dataAE, TRTEMFL == "Y" & AESEV == "SEVERE") dataListing <- dataListing[, varsListing] colnames(dataListing) <- getLabelVar(var = varsListing, labelVars = labelVars) getListing( data = dataListing, title = "Listing of treatment-emergent severe adverse events", includeRownames = FALSE ) ``` # Filter records in a table If only a subset of the records should be displayed in the final table, a custom filtering functions is specified via the **`filterFct`** parameter. ```{r countTable-AE-filterFct} library(plyr) # SOC with AE terms with at least 2 subjects getSummaryStatisticsTable( data = subset(dataAE, TRTEMFL == "Y"), rowVar = c("AESOC", "AEDECOD"), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), colVar = "TRTA", stats = getStats("n (%)"), filterFct = function(x) ddply(x, "AESOC", function(y) if(any(y$statN >= 2)) y ), labelVars = labelVars, title = paste( "Table: Adverse Events by System Organ Class and", "Preferred Term with at least 2 patients in System Organ Class" ) ) # AE term with at least one term with more than 70% in the treatment getSummaryStatisticsTable( data = subset(dataAE, TRTEMFL == "Y"), rowVar = c("AESOC", "AEDECOD"), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), colVar = "TRTA", stats = getStats("n (%)"), filterFct = function(x) ddply(x, "AEDECOD", function(xTerm) { if(any(xTerm$statPercN >= 70)) xTerm }), labelVars = labelVars, title = "Table: Adverse Events by System Organ Class and Preferred Term with at least 70% patients" ) ``` Note: to help the creation of this filtering function, the displaying data could be extracted first via the `computeSummaryStatisticsTable` function (instead of creating directly the in-text table via the `getSummaryStatisticsTable`), and then used to define the `filterFct` parameter. ```{r countTable-AE-filterFct- details} x <- computeSummaryStatisticsTable( data = subset(dataAE, TRTEMFL == "Y"), rowVar = c("AESOC", "AEDECOD"), rowVarLab = c('AESOC' = "TEAE by SOC and Preferred Term\nn (%)"), colVar = "TRTA", stats = getStats("n (%)"), labelVars = labelVars ) head(x) # for specific AEDECOD xTerm <- subset(x, AEDECOD == "MYOCARDIAL INFARCTION") # identify the record for treated patient: subset(xTerm, grepl("Placebo", TRTA)) # keep all records (placebo + treatment) if percentage if higher than 20%: # if no 'else' condition, nothing (NULL) is returned: if(subset(xTerm, grepl("Placebo", TRTA))$statPercN >= 20) xTerm # across all AE terms: ddply(x, "AEDECOD", function(xTerm) if(subset(xTerm, grepl("Placebo", TRTA))$statPercN >= 20) xTerm ) # format it as a function and pass it to the 'filterFct' parameter ``` # Appendix ## Session information ```{r includeSessionInfo, echo = FALSE} pander(sessionInfo()) ```