# Exporting in-text tables

#### Laure Cougnaud

#### June 12, 2024

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) or accessible with the commands below.

    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.

# 1 Load packages and data

    library(inTextSummaryTable)
    library(clinUtils)
    library(pander)
    library(tools) # toTitleCase

    # load example data
    data(dataADaMCDISCP01)

    dataAll <- dataADaMCDISCP01
    labelVars <- attr(dataAll, "labelVars")

This section creates example data sets used below.

    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

# 2 Output formats

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.

## 2.1 Static table for Word/Powerpoint

### 2.1.1 General format

To get the table in a format suitable for Word/Powerpoint documents, the outputType parameter should be set to ‘flextable’ (the default).

    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)

    ## [1] "flextable"

    summaryTableFt

Table: Adverse Events by System Organ Class and Preferred Term
TEAE by SOC and Preferred Term n (%) | Xanomeline Low Dose (N=2) | Xanomeline High Dose (N=3)
--- | --- | ---
Dictionary-Derived Term
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |  |
APPLICATION SITE DERMATITIS | 0 | 1 (33.3)
APPLICATION SITE ERYTHEMA | 2 (100) | 1 (33.3)
APPLICATION SITE IRRITATION | 1 (50.0) | 1 (33.3)
APPLICATION SITE PRURITUS | 2 (100) | 2 (66.7)
FATIGUE | 0 | 1 (33.3)
SECRETION DISCHARGE | 1 (50.0) | 0
SUDDEN DEATH | 1 (50.0) | 0
INFECTIONS AND INFESTATIONS |  |
LOWER RESPIRATORY TRACT INFECTION | 0 | 1 (33.3)
PNEUMONIA | 1 (50.0) | 0
N = number of subjects in the data; n = number of subjects with observation
Denominator for percentage calculations is the total number of subjects per treatment group in the safety population

The object `summaryTableFt` is a flextable object.

This is available through the `flextable` R package (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 version >= 2.0 (see `rmarkdown` documentation for Word)
- for `pptx` document: pandoc version >= 2.4 (see `rmarkdown` documentation for Powerpoint)

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.

### 2.1.2 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.

    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 (%)")
    )

Table: Adverse Events by System Organ Class and Preferred Term
TEAE by SOC and Preferred Term n (%) | Xanomeline Low Dose (N=2) | Xanomeline High Dose (N=3)
--- | --- | ---
Dictionary-Derived Term
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |  |
APPLICATION SITE DERMATITIS | 0 | 1 (33.3)
APPLICATION SITE ERYTHEMA | 2 (100) | 1 (33.3)
APPLICATION SITE IRRITATION | 1 (50.0) | 1 (33.3)
APPLICATION SITE PRURITUS | 2 (100) | 2 (66.7)
FATIGUE | 0 | 1 (33.3)
SECRETION DISCHARGE | 1 (50.0) | 0
SUDDEN DEATH | 1 (50.0) | 0
INFECTIONS AND INFESTATIONS |  |
LOWER RESPIRATORY TRACT INFECTION | 0 | 1 (33.3)
PNEUMONIA | 1 (50.0) | 0
N = number of subjects with data; n = number of subjects with this observation
Denominator for percentage calculations is the total number of subjects per treatment group in the safety population

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”.

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

Table: Adverse Events by System Organ Class and Preferred Term
TEAE by SOC and Preferred Term n (%) | Xanomeline Low Dose (N=2) | Xanomeline High Dose (N=3)
--- | --- | ---
Dictionary-Derived Term
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |  |
APPLICATION SITE DERMATITIS | 0 | 1 (33.3)
APPLICATION SITE ERYTHEMA | 2 (100) | 1 (33.3)
APPLICATION SITE IRRITATION | 1 (50.0) | 1 (33.3)
APPLICATION SITE PRURITUS | 2 (100) | 2 (66.7)
FATIGUE | 0 | 1 (33.3)
SECRETION DISCHARGE | 1 (50.0) | 0
SUDDEN DEATH | 1 (50.0) | 0
INFECTIONS AND INFESTATIONS |  |
LOWER RESPIRATORY TRACT INFECTION | 0 | 1 (33.3)
PNEUMONIA | 1 (50.0) | 0
N = number of subjects with data; n = number of subjects with this observation
Denominator for percentage calculations is the total number of subjects per treatment group in the safety population

## 2.2 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 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.

    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
    )

## 2.3 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.

### 2.3.1 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`.

    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)

AESOC | AEDECOD | Xanomeline Low Dose (N=2) | Xanomeline High Dose (N=3)
--- | --- | --- | ---
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE DERMATITIS | 0 | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE ERYTHEMA | 2 (100) | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE IRRITATION | 1 (50.0) | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE PRURITUS | 2 (100) | 2 (66.7)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | FATIGUE | 0 | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SECRETION DISCHARGE | 1 (50.0) | 0
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SUDDEN DEATH | 1 (50.0) | 0
INFECTIONS AND INFESTATIONS | LOWER RESPIRATORY TRACT INFECTION | 0 | 1 (33.3)
INFECTIONS AND INFESTATIONS | PNEUMONIA | 1 (50.0) | 0

### 2.3.2 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.

    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)

AESOC | AEDECOD | TRTA | isTotal | statN | statm | statPercTotalN | statPercN | n (%)
--- | --- | --- | --- | --- | --- | --- | --- | ---
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE DERMATITIS | Xanomeline Low Dose | FALSE | 0 | 0 | 2 | 0 | 0
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE ERYTHEMA | Xanomeline Low Dose | FALSE | 2 | 2 | 2 | 100 | 2 (100)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE IRRITATION | Xanomeline Low Dose | FALSE | 1 | 2 | 2 | 50 | 1 (50.0)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE PRURITUS | Xanomeline Low Dose | FALSE | 2 | 2 | 2 | 100 | 2 (100)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | FATIGUE | Xanomeline Low Dose | FALSE | 0 | 0 | 2 | 0 | 0
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SECRETION DISCHARGE | Xanomeline Low Dose | FALSE | 1 | 2 | 2 | 50 | 1 (50.0)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SUDDEN DEATH | Xanomeline Low Dose | FALSE | 1 | 1 | 2 | 50 | 1 (50.0)
INFECTIONS AND INFESTATIONS | LOWER RESPIRATORY TRACT INFECTION | Xanomeline Low Dose | FALSE | 0 | 0 | 2 | 0 | 0
INFECTIONS AND INFESTATIONS | PNEUMONIA | Xanomeline Low Dose | FALSE | 1 | 2 | 2 | 50 | 1 (50.0)
NA | NA | Xanomeline Low Dose | TRUE | 2 | 2 | 2 | 100 | 2 (100)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE DERMATITIS | Xanomeline High Dose | FALSE | 1 | 2 | 3 | 33.33 | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE ERYTHEMA | Xanomeline High Dose | FALSE | 1 | 1 | 3 | 33.33 | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE IRRITATION | Xanomeline High Dose | FALSE | 1 | 2 | 3 | 33.33 | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE PRURITUS | Xanomeline High Dose | FALSE | 2 | 3 | 3 | 66.67 | 2 (66.7)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | FATIGUE | Xanomeline High Dose | FALSE | 1 | 1 | 3 | 33.33 | 1 (33.3)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SECRETION DISCHARGE | Xanomeline High Dose | FALSE | 0 | 0 | 3 | 0 | 0
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SUDDEN DEATH | Xanomeline High Dose | FALSE | 0 | 0 | 3 | 0 | 0
INFECTIONS AND INFESTATIONS | LOWER RESPIRATORY TRACT INFECTION | Xanomeline High Dose | FALSE | 1 | 2 | 3 | 33.33 | 1 (33.3)
INFECTIONS AND INFESTATIONS | PNEUMONIA | Xanomeline High Dose | FALSE | 0 | 0 | 3 | 0 | 0
NA | NA | Xanomeline High Dose | TRUE | 3 | 3 | 3 | 100 | 3 (100)

## 2.4 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’

# 3 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.

## 3.1 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.

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

Statistic | StatisticValue (N=2)
--- | ---
statN | 2
statm | 2
statPercTotalN | 2
statPercN | 100

## 3.2 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

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

    ## [1]  7.50 13.32

## 3.3 Further info on asthetics

For further information about the aesthetics settings of the package, a dedicated vignette is available at

    vignette("inTextSummaryTable-aesthetics", "inTextSummaryTable")

## 3.4 Set back default palettes

There is always to possibility to switch back to the default palettes of the package:

    options(inTextSummaryTable.colors.table.presentation = tableColorsPresentation)

# 4 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

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

# 5 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.

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

Table: Diastolic Blood Pressure (mmHg) Statistics
| Placebo | Xanomeline High Dose | Xanomeline Low Dose
--- | --- | --- | ---
Baseline (N=2) | Week 6 (N=1) | Baseline (N=3) | Week 6 (N=3) | Baseline (N=2) | Week 6 (N=2)
Analysis Value |  |  |  |  |  |
n | 2 | 1 | 3 | 3 | 2 | 2
Mean (SE) | 71.0 (1.00) | 70.0 ( NA) | 68.3 (6.69) | 72.0 (10.02) | 74.0 (10.00) | 65.0 (5.00)
Median (range) | 71.0 (70,72) | 70.0 (70,70) | 70.0 (56,79) | 76.0 (53,87) | 74.0 (64,84) | 65.0 (60,70)
Change from Baseline |  |  |  |  |  |
n | 2 | 1 | 3 | 3 | 2 | 2
Mean (SE) | 0.0 (0.00) | -2.0 ( NA) | 0.0 (0.00) | 3.7 (3.38) | 0.0 (0.00) | -9.0 (5.00)
Median (range) | 0.0 (0,0) | -2.0 (-2,-2) | 0.0 (0,0) | 6.0 (-3,8) | 0.0 (0,0) | -9.0 (-14,-4)

# 6 Text formatting

Specific formatted text can be specified for the `flextable` output type only.

## 6.1 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.

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

Demographic Data (Safety Analysis Set)
Variable | n (%) (N=7)
--- | ---
Age | 7 (100)
Weight t | 7 (100)
BMI (kg/m 2) | 7 (100)

## 6.2 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.

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

Demographic Data (Safety Analysis Set)
Variable | StatisticValue (N=7)
--- | ---
Age | 74.3 (3.71)
Weight t | 62.0 (5.68)
BMI (kg/m 2) | 23.4 (1.71)

# 7 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:

    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)

Table: Treatment-Emergent adverse events
Actual Treatment: Placebo
TEAE by SOC and Preferred Term n (%) | Placebo (N=2)
--- | ---
Dictionary-Derived Term
CARDIAC DISORDERS |
MYOCARDIAL INFARCTION | 1 (50.0)
PSYCHIATRIC DISORDERS |
COMPLETED SUICIDE | 1 (50.0)

Table: Treatment-Emergent adverse events
Actual Treatment: Xanomeline High Dose
TEAE by SOC and Preferred Term n (%) | Xanomeline High Dose (N=3)
--- | ---
Dictionary-Derived Term
GASTROINTESTINAL DISORDERS |
NAUSEA | 2 (66.7)
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |
APPLICATION SITE DERMATITIS | 1 (33.3)
APPLICATION SITE ERYTHEMA | 1 (33.3)
APPLICATION SITE IRRITATION | 1 (33.3)
APPLICATION SITE PRURITUS | 2 (66.7)
FATIGUE | 1 (33.3)
INFECTIONS AND INFESTATIONS |
LOWER RESPIRATORY TRACT INFECTION | 1 (33.3)
METABOLISM AND NUTRITION DISORDERS |
DECREASED APPETITE | 1 (33.3)
MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS |
BACK PAIN | 1 (33.3)
FLANK PAIN | 1 (33.3)
NERVOUS SYSTEM DISORDERS |
AMNESIA | 1 (33.3)
LETHARGY | 1 (33.3)
PARTIAL SEIZURES WITH SECONDARY GENERALISATION | 1 (33.3)
PSYCHIATRIC DISORDERS |
HALLUCINATION, VISUAL | 1 (33.3)
RENAL AND URINARY DISORDERS |
CALCULUS URETHRAL | 1 (33.3)
RESPIRATORY, THORACIC AND MEDIASTINAL DISORDERS |
EPISTAXIS | 1 (33.3)
SKIN AND SUBCUTANEOUS TISSUE DISORDERS |
ACTINIC KERATOSIS | 1 (33.3)

Table: Treatment-Emergent adverse events
Actual Treatment: Xanomeline Low Dose
TEAE by SOC and Preferred Term n (%) | Xanomeline Low Dose (N=2)
--- | ---
Dictionary-Derived Term
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |
APPLICATION SITE ERYTHEMA | 2 (100)
APPLICATION SITE IRRITATION | 1 (50.0)
APPLICATION SITE PRURITUS | 2 (100)
SECRETION DISCHARGE | 1 (50.0)
SUDDEN DEATH | 1 (50.0)
INFECTIONS AND INFESTATIONS |
PNEUMONIA | 1 (50.0)
INJURY, POISONING AND PROCEDURAL COMPLICATIONS |
JOINT DISLOCATION | 1 (50.0)
SKIN LACERATION | 1 (50.0)
INVESTIGATIONS |
NASAL MUCOSA BIOPSY | 1 (50.0)
MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS |
MUSCULAR WEAKNESS | 1 (50.0)
SHOULDER PAIN | 1 (50.0)
PSYCHIATRIC DISORDERS |
CONFUSIONAL STATE | 1 (50.0)
RENAL AND URINARY DISORDERS |
INCONTINENCE | 1 (50.0)
RESPIRATORY, THORACIC AND MEDIASTINAL DISORDERS |
DYSPNOEA | 1 (50.0)
SKIN AND SUBCUTANEOUS TISSUE DISORDERS |
ERYTHEMA | 1 (50.0)

# 8 Listing

Listing in a similar format as the in-text summary table is created via the `getListing` function.

    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
    )

Listing of treatment-emergent severe adverse events
Unique Subject Identifier | Body System or Organ Class | Dictionary-Derived Term | Actual Treatment | Severity/Intensity | Serious Event | Analysis Start Relative Day | Analysis End Relative Day
--- | --- | --- | --- | --- | --- | --- | ---
01-701-1211 | GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | SUDDEN DEATH | Xanomeline Low Dose | SEVERE | N | 61 | 61
01-704-1445 | PSYCHIATRIC DISORDERS | COMPLETED SUICIDE | Placebo | SEVERE | N | 174 | 174
01-710-1083 | CARDIAC DISORDERS | MYOCARDIAL INFARCTION | Placebo | SEVERE | N | 12 | 12
01-718-1371 | NERVOUS SYSTEM DISORDERS | PARTIAL SEIZURES WITH SECONDARY GENERALISATION | Xanomeline High Dose | SEVERE | Y | 38 | 41
01-718-1427 | GASTROINTESTINAL DISORDERS | NAUSEA | Xanomeline High Dose | SEVERE | N | 36 | 39

# 9 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.

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

Table: Adverse Events by System Organ Class and Preferred Term with at least 2 patients in System Organ Class
TEAE by SOC and Preferred Term n (%) | Placebo (N=2) | Xanomeline High Dose (N=3) | Xanomeline Low Dose (N=2)
--- | --- | --- | ---
Dictionary-Derived Term
GASTROINTESTINAL DISORDERS |  |  |
NAUSEA | 0 | 2 (66.7) | 0
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |  |  |
APPLICATION SITE DERMATITIS | 0 | 1 (33.3) | 0
APPLICATION SITE ERYTHEMA | 0 | 1 (33.3) | 2 (100)
APPLICATION SITE IRRITATION | 0 | 1 (33.3) | 1 (50.0)
APPLICATION SITE PRURITUS | 0 | 2 (66.7) | 2 (100)
FATIGUE | 0 | 1 (33.3) | 0
SECRETION DISCHARGE | 0 | 0 | 1 (50.0)
SUDDEN DEATH | 0 | 0 | 1 (50.0)

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

Table: Adverse Events by System Organ Class and Preferred Term with at least 70% patients
TEAE by SOC and Preferred Term n (%) | Placebo (N=2) | Xanomeline High Dose (N=3) | Xanomeline Low Dose (N=2)
--- | --- | --- | ---
Dictionary-Derived Term
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS |  |  |
APPLICATION SITE ERYTHEMA | 0 | 1 (33.3) | 2 (100)
APPLICATION SITE PRURITUS | 0 | 2 (66.7) | 2 (100)

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.

    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)

    ##                                                  AESOC                     AEDECOD    TRTA isTotal statN statm statPercTotalN statPercN    n (%)
    ## 1                                    CARDIAC DISORDERS       MYOCARDIAL INFARCTION Placebo   FALSE     1     1              2        50 1 (50.0)
    ## 2                           GASTROINTESTINAL DISORDERS                      NAUSEA Placebo   FALSE     0     0              2         0        0
    ## 3 GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS APPLICATION SITE DERMATITIS Placebo   FALSE     0     0              2         0        0
    ## 4 GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS   APPLICATION SITE ERYTHEMA Placebo   FALSE     0     0              2         0        0
    ## 5 GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS APPLICATION SITE IRRITATION Placebo   FALSE     0     0              2         0        0
    ## 6 GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS   APPLICATION SITE PRURITUS Placebo   FALSE     0     0              2         0        0

    # for specific AEDECOD
    xTerm <- subset(x, AEDECOD == "MYOCARDIAL INFARCTION")
    # identify the record for treated patient:
    subset(xTerm, grepl("Placebo", TRTA))

    ##               AESOC               AEDECOD    TRTA isTotal statN statm statPercTotalN statPercN    n (%)
    ## 1 CARDIAC DISORDERS MYOCARDIAL INFARCTION Placebo   FALSE     1     1              2        50 1 (50.0)

    # 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

    ##                AESOC               AEDECOD                 TRTA isTotal statN statm statPercTotalN statPercN    n (%)
    ## 1  CARDIAC DISORDERS MYOCARDIAL INFARCTION              Placebo   FALSE     1     1              2        50 1 (50.0)
    ## 33 CARDIAC DISORDERS MYOCARDIAL INFARCTION Xanomeline High Dose   FALSE     0     0              3         0        0
    ## 65 CARDIAC DISORDERS MYOCARDIAL INFARCTION  Xanomeline Low Dose   FALSE     0     0              2         0        0

    # across all AE terms:
    ddply(x, "AEDECOD", function(xTerm)
          if(subset(xTerm, grepl("Placebo", TRTA))$statPercN >= 20) xTerm
    )

    ##                   AESOC               AEDECOD                 TRTA isTotal statN statm statPercTotalN statPercN    n (%)
    ## 1 PSYCHIATRIC DISORDERS     COMPLETED SUICIDE              Placebo   FALSE     1     1              2        50 1 (50.0)
    ## 2 PSYCHIATRIC DISORDERS     COMPLETED SUICIDE Xanomeline High Dose   FALSE     0     0              3         0        0
    ## 3 PSYCHIATRIC DISORDERS     COMPLETED SUICIDE  Xanomeline Low Dose   FALSE     0     0              2         0        0
    ## 4     CARDIAC DISORDERS MYOCARDIAL INFARCTION              Placebo   FALSE     1     1              2        50 1 (50.0)
    ## 5     CARDIAC DISORDERS MYOCARDIAL INFARCTION Xanomeline High Dose   FALSE     0     0              3         0        0
    ## 6     CARDIAC DISORDERS MYOCARDIAL INFARCTION  Xanomeline Low Dose   FALSE     0     0              2         0        0
    ## 7                  <NA>                  <NA>              Placebo    TRUE     2     2              2       100  2 (100)
    ## 8                  <NA>                  <NA> Xanomeline High Dose    TRUE     3    30              3       100  3 (100)
    ## 9                  <NA>                  <NA>  Xanomeline Low Dose    TRUE     2    22              2       100  2 (100)

    # format it as a function and pass it to the 'filterFct' parameter

# 10 Appendix

## 10.1 Session information

R version 4.4.0 (2024-04-24)

Platform: x86_64-pc-linux-gnu

locale: C

attached base packages: tools, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: plyr(v.1.8.9), pander(v.0.6.5), clinUtils(v.0.2.0), inTextSummaryTable(v.3.3.3) and knitr(v.1.47)

loaded via a namespace (and not attached): gtable(v.0.3.5), xfun(v.0.44), bslib(v.0.7.0), ggplot2(v.3.5.1), htmlwidgets(v.1.6.4), ggrepel(v.0.9.5), vctrs(v.0.6.5), crosstalk(v.1.2.1), generics(v.0.1.3), curl(v.5.2.1), tibble(v.3.2.1), fansi(v.1.0.6), highr(v.0.11), pkgconfig(v.2.0.3), data.table(v.1.15.4), uuid(v.1.2-0), lifecycle(v.1.0.4), flextable(v.0.9.6), farver(v.2.1.2), stringr(v.1.5.1), compiler(v.4.4.0), textshaping(v.0.4.0), munsell(v.0.5.1), httpuv(v.1.6.15), fontquiver(v.0.2.1), fontLiberation(v.0.1.0), htmltools(v.0.5.8.1), sass(v.0.4.9), yaml(v.2.3.8), later(v.1.3.2), pillar(v.1.9.0), crayon(v.1.5.2), jquerylib(v.0.1.4), gfonts(v.0.2.0), openssl(v.2.2.0), DT(v.0.33), cachem(v.1.1.0), mime(v.0.12), fontBitstreamVera(v.0.1.1), tidyselect(v.1.2.1), zip(v.2.3.1), digest(v.0.6.35), stringi(v.1.8.4), reshape2(v.1.4.4), dplyr(v.1.1.4), labeling(v.0.4.3), forcats(v.1.0.0), cowplot(v.1.1.3), fastmap(v.1.2.0), grid(v.4.4.0), colorspace(v.2.1-0), cli(v.3.6.2), magrittr(v.2.0.3), crul(v.1.4.2), utf8(v.1.2.4), withr(v.3.0.0), gdtools(v.0.3.7), scales(v.1.3.0), promises(v.1.3.0), rmarkdown(v.2.27), officer(v.0.6.6), askpass(v.1.2.0), ragg(v.1.3.2), hms(v.1.1.3), shiny(v.1.8.1.1), evaluate(v.0.24.0), haven(v.2.5.4), viridisLite(v.0.4.2), rlang(v.1.1.4), Rcpp(v.1.0.12), xtable(v.1.8-4), glue(v.1.7.0), httpcode(v.0.3.0), xml2(v.1.3.6), jsonlite(v.1.8.8), R6(v.2.5.1) and systemfonts(v.1.1.0)